Я заметил, что следующий код генерирует выделение кучи, которое в какой-то момент запускает сборщик мусора, и я хотел бы знать, почему это так и как этого избежать:
private Dictionary<Type, Action> actionTable = new Dictionary<Type, Action>();
private void Update(int num)
{
Action action;
// if (!actionTable.TryGetValue(typeof(int), out action))
if (false)
{
action = () => Debug.Log(num);
actionTable.Add(typeof(int), action);
}
action?.Invoke();
}
Я понимаю, что использование лямбды, такой как () => Debug.Log(num)
, создаст небольшой вспомогательный класс (например, ‹>c__DisplayClass7_0) для хранения локальной переменной. Вот почему я хотел проверить, могу ли я кэшировать это распределение в словаре. Однако я заметил, что вызов Update приводит к выделению памяти, даже если лямбда-код никогда не достигается из-за оператора if. Когда я комментирую лямбду, выделение исчезает из профилировщика. Я использую инструмент Unity Profiler (инструмент для создания отчетов о производительности в игровом движке Unity), который показывает такие распределения в байтах на кадр в режиме разработки/отладки.
Я предполагаю, что компилятор или JIT-компилятор генерирует вспомогательный класс для лямбды для области действия метода, хотя я не понимаю, почему это желательно.
Наконец, есть ли способ кэшировать делегаты таким образом, не выделяя и не заставляя вызывающий код кэшировать действие заранее? (Я знаю, что я мог бы также выделить действие один раз в клиентском коде, но в этом примере я бы строго хотел реализовать какое-то автоматическое кэширование, потому что у меня нет полного контроля над клиентом).
Отказ от ответственности: это в основном теоретический вопрос из интереса. Я понимаю, что большинство приложений не выиграют от такой микрооптимизации.
if(false)
, который будет полностью удален оптимизатором. Если вы запускаете это с выключенным оптимизатором, вы не должны ожидать оптимизированного кода, когда отключите оптимизатор! - person Eric Lippert   schedule 12.12.2018