Как ускорить время запуска WPF-приложения (использование утилиты ngen.exe)

— Knock, knock. -Who’s there? (long delay….) -Dot Net. 🙂

Запуск даже несложного, но при этом неоптимизированного WPF-приложения, может
занимать длительное время (в среднем порядка 15-30 секунд)
Утилита ngen.exe — это генератор образов в машинном коде. Её использование позволяет сократить время т.н. «горячего запуска» — т.е. быстрого повторного запуска (в отличие от «холодного» запуска, который определяется как запуск сразу после перезагрузки или по прошествии длительного периода времени после закрытия приложения).

Приложение .NET содержит кросс-платформенный код (IL — intermediate language), который с помощью сервиса Windows «.NET Runtime Optimization Service» в фоновом режиме, в порядке очереди преобразуется в т.н. native binary code Windows (машинный двоичный код), сохраняется в некий кеш и далее уже запускается — так осуществляется JIT-компиляция.
Чтобы ускорить «горячий запуск», нужно осуществлять Pre-Jit компиляцию, т.е. выполнять эти действия не в порядке очереди, а принудительно. Для этого используется утилита Ngen.exe.

Предусловия:
Для проверки того, что описано в статье, необходимо предварительно:
— Создать простейшее WPF-приложение, для которого создаётся установщик
— Скачать примеры по WIX# и с их помощью создать проект установщика.
Собственно, рекомендации по настройки уже созданного и работающего установщика и описаны в статье.

Для использования утилиты ngen.exe в установщике, написанном с помощью WIX (Windows Installer XML) необходимо выполнить следующие шаги:
1) Добавить в проект установщика ссылку на библиотеку, расширяющую возможности WIX: WixNetFxExtension.dll
2) Программирование на WIX заключается в создании правильного xml-файла формата .wxs, основным тегом в этом файле является тег <Wix> </Wix>. Необходимо в этот тег добавить пространство имен, соответствующее библиотеке из первого пункта:
<Wix
...
xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">
...
</Wix>

3) Внутри тэга сборки, для которой необходимо запустить NGen, необходимо добавить элемент
<NetFx:NativeImage> внутри родительского:
<Component Id="myapplication.exe" Guid="PUT-GUID-HERE">
<File Id="myapplication.exe" Source="MySourceFiles\MyApplication.exe" KeyPath="yes" Checksum="yes">
<netfx:NativeImage Id="ngen_MyApplication.exe" Platform="32bit" Priority="0" AppBaseDirectory="APPLICATIONROOTDIRECTORY"/>
</File>
</Component>

Реализация данного алгоритма с помощью WIX# (это фреймворк C#, с помощью которого можно написать полноценный MSI-установщик, используя только синтаксис языка C#) может выглядеть следующим образом
(за основу WIX# берётся Release v1.0.30.0, от Tue Dec 1, 2015 at 8:00 AM. В этой версии не реализован функционал WIX, связанный с использованием утилиты ngen.exe):
1)-2) Ссылка на библиотеку
Обычно к моменту, когда происходит оптимизация скорости запуска приложения, основная часть установщика уже как минимум, написана проверка, установлен ли .NET на компьютере, на котором запускается установщик (про это будет отдельная статья):
var project = new Project(...);
project.SetNetFxPrerequisite(Condition.Net45_Installed, "Отсутствует .NET Framework 4.5.");

В случае, если такая проверка есть, то WIX# автоматически добавит ссылку на пространство имён netfx, и этот шаг можно будет пропустить.
Иначе, нужно будет добавить примерно следующее:
#region // Как добавить пространство имён (ссылку на библиотеку)
// Чтобы посмотреть, какие пространства имён уже добавлены, можно при отладке проекта установщика (при условии, что в решение добавлены проекты с исходным кодом WIx и Wix.UI), открыть файл Wixsharp.Compiler.cs и в его методе GenerateWixProj() при создании объекта XDocument используется переменная "extra namespaces" - здесь можно поставить точку останова и посмотреть список добавленных пространств имён
project.IncludeWixExtension(new WixExtension(@"NetFxExtension.dll", "netfx", "http://schemas.microsoft.com/wix/NetFxExtension"));
#endregion

3) Для добавления элемента «NetFx:NativeImage» можно воспользоваться XML-инъекцией :

/// <summary>
/// Данный метод необходим для ускорения первого запуска Программы
/// (с помощью утилиты ngen.exe, встроенной в Wix во время процесса установки, осуществляется компиляция программы и всех библиотек, от которых она зависит) в native image
/// </summary>
/// <param name="document"></param>
static void XMLInjection_Ngen(XDocument document)
{
string appFullName = "MyApplication.exe";
#region Использование Ngen для pre-jit компиляции, которое должно происходить во время установки, что значительно ускоряет запуск приложения:
XNamespace ns = "http://schemas.microsoft.com/wix/NetFxExtension";
var compArray = document.FindAll("Component");
var targetComponentTag = componentsMass.Single(x => x.HasAttribute("Id", value => value.EndsWith("Component." + appFullName)));
var targetFileTag = targetComponentTag .FindAll("File").Single(x => x.HasAttribute("Id", value => value.EndsWith(appFullName)));
var ngenMyAppTag =
new XElement
(
NS + "NativeImage",
new XAttribute("Id", "ngen_MyApplication.exe"),
new XAttribute("Platform", "32bit"), // если в свойствах проекта WPF-приложения целевой выбрана архитектура AnyCPU или x64 - необходимо указывать 64bit, или задать архитектуру x86
new XAttribute("Priority", "0"),
new XAttribute("AppBaseDirectory", "APPLICATIONROOTDIRECTORY") // папка, в которой расположена сборка
);
targetFileTag.AddElement(ngenMyAppTag);
#endregion
}

Далее необходимо собрать установщик и запустить его на клиентской машине
Как проверить, что после установки:
Для того, чтобы проверить работу ngen, необходимо открыть командную строку от имени Администратора, перейти в директорию, в которой расположена утилита ngen.exe (в зависимости от архитектуры приложения):
cd C:\Windows\Microsoft.NET\Framework\v4.0.30319
cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319

и вызвать команду:
ngen.exe display "C:\Program Files (x86)\MyApplication\MyApplication.exe"
, причем путь указан до установленного приложения.
Также для принудительной установки\удаления машинного образа приложения можно выполнить следующие команды:
ngen.exe install "C:\Program Files (x86)\MyApplication\MyApplication.exe"
ngen.exe uninstall "C:\Program Files (x86)\MyApplication\MyApplication.exe"

Источники:
Методы ускорения запуска
ngen.exe
Разные виды JIT-компиляции
Как использовать ngen.exe при работе с WIX
Описание WixNetFXExtension
Описание элемент NetFx:NativeImage
WIX#
Инструкция от WIX#, как вручную редактировать теги
Пример, как с помощью WIX# добавить ссылку на пространство имён
Также можно запустить ngen.exe из кода

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *


Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.