Принуждение компилятора .NET JIT для генерации наиболее оптимизированного кода при запуске приложения

Я пишу приложение DSP в С# (в основном, многодорожечный редактор). Я проделывал его довольно долгое время на разных машинах, и я заметил некоторые "любопытные" вещи.

На моей домашней машине первый запуск цикла воспроизведения занимает около 50% -60% доступного времени (я предполагаю, что это связано с тем, что JIT выполняет свою работу), а затем для последующих циклов он переходит на устойчивое потребление 5%. Проблема в том, что если я запускаю приложение на более медленном компьютере, первый запуск занимает больше, чем доступное время, что приводит к прерыванию воспроизведения и испорчению выходного аудиосигнала, что неприемлемо. После этого он снижается до 8% -10% потребления.

Даже после первого запуска приложение время от времени вызывает вызовы некоторых трудоемких процедур (каждые 2 секунды или более), что приводит к тому, что устойчивое потребление 5% испытывает очень короткие пики 20-25%. Я заметил, что если я позволю программе работать некоторое время, эти пики также снизятся до 7% -10%. (Я не уверен, что это связано с перекомпиляцией JIT этих частей кода).

Итак, у меня есть серьезная проблема с JIT. Хотя приложение будет вести себя хорошо даже на очень медленных машинах, эти "компиляционные штормы" будут большой проблемой. Я пытаюсь понять, как решить эту проблему, и я придумал идею, которая заключается в том, чтобы пометить все "разумные" подпрограммы атрибутом, который будет сообщать приложению "сжать" их заранее во время запуска, поэтому они будут полностью оптимизированы, когда они действительно понадобятся. Но это только идея (и мне тоже это не слишком нравится), и мне интересно, есть ли лучшее решение всей проблемы.

Я бы хотел услышать, что вы, ребята, думаете.

(NGEN приложение не является вариантом, мне нравится и хочу, чтобы все оптимизаторы JIT я мог получить.)

EDIT:

Утилизация памяти и сбор мусора не проблема, я использую пулы объектов, а максимальный пик памяти во время воспроизведения составляет 304 КБ.

Ответ 1

Вы можете запустить JIT-компилятор для компиляции всего набора сборок во время процедуры инициализации приложения, используя метод PrepareMethod... (без использования NGen).

Это решение более подробно описано здесь: Принудительная JIT-компиляция во время выполнения.

Ответ 2

Начальная скорость действительно звучит как Fusion + JIT, чему могут помочь ILMerge (для Fusion) и NGEN (для JIT); вы всегда можете сыграть безмолвную дорожку через систему при запуске, чтобы она выполняла всю тяжелую работу без того, чтобы пользователь заметил какие-либо искажения?

NGEN - хороший вариант; есть ли причина, по которой вы не можете его использовать?

Проблемы, которые вы упомянули после начальной загрузки, имеют не звук, похожий на JIT. Возможно сбор мусора.

Вы пробовали профилировать? Как процессор, так и память (коллекции)?

Ответ 3

Как отметил Марк, продолжающиеся шипы не звучат как проблемы JIT. Другие вещи, которые нужно искать:

  • Сбор мусора - вы выделяете память во время обработки звука? Если вы создаете много мусора или даже объектов, которые выходят из коллекции Gen 0, это может вызвать заметные всплески. Похоже, вы делаете какое-то предварительное выделение, но следите за скрытыми выделениями в библиотечном коде (даже цикл foreach может выделять!)

  • денормализованные числа. Существует проблема с некоторыми типами процессоров при работе с очень маленькими числами с плавающей запятой, которые могут вызвать всплески процессора. Подробнее см. http://www.musicdsp.org/files/denormal.pdf.

Edit:

Даже если вы не хотите использовать NGen, по крайней мере сравните версию NGen'd, чтобы вы могли видеть, какая разница JITing делает

Ответ 4

Если вы считаете, что на вас воздействует JIT, перекомпилируйте приложение с помощью NGEN и снова запустите тесты. В коде отсутствует JIT-код, который был скомпилирован NGEN. Если вы все еще видите всплески в приложении NGEN'd, то вы знаете, что они не вызваны JIT.