Встраивание и Структуры в C#
Сегодня рассмотрим встраивание структур в C#. И как оно может оптимизировать производительность несколькими интересными способами.
Встраивание
Встраивание — это оптимизация компилятора, которая заменяет вызов метода его телом. Например:
public int Add(int a, int b) => a + b;
public int CalculateSum(int x, int y)
=> Add(x, y);
Компилятор может оптимизировать его до:
public int CalculateSum(int x, int y)
=> x + y;
Очевидное преимущество здесь в том, что мы избегаем накладных расходов на вызов метода, но это также может увеличить размер кода и негативно сказаться на производительности (поскольку мы копируем тело метода во все места его вызова). Существует атрибут
[MethodImpl(MethodImplOptions.AggressiveInlining)], который можно использовать, чтобы подсказать компилятору, что метод следует встраивать, даже если по умолчанию он этого не делает. Это всего лишь подсказка, и JIT всё равно может её проигнорировать. Мы также можем использовать [MethodImpl(MethodImplOptions.NoInlining)], чтобы предотвратить встраивание метода (подсказать JIT, что встраивать его не следует).Структуры
Неотъемлемой частью структур является то, что они, как правило, передаются по значению. Это означает, что при передаче структуры методу создаётся её копия. Т.е.:
public struct Point
{
public int X;
public int Y;
}
public void MovePoint(Point p)
{
p.X += 10;
p.Y += 10;
}
Point myPoint = new Point { X = 0, Y = 0 };
MovePoint(myPoint);
// myPoint всё ещё { X = 0, Y = 0 }
В идеале структуры лучше сохранять неизменяемыми именно по этой причине. А также делать небольшими, чтобы избежать накладных расходов на копирование. И вот в чём «прелесть»: встраивая функцию, мы «стираем» необходимость копирования структуры в новый стековый фрейм. Т.е. встраивание может фактически удешевить передачу структур.
Посмотрите вот этот пример на sharplab.io. Вам не нужно понимать JIT-код ASM. Но сама разница в объёме кода между
InlineVsNonInlineBenchmark.NonInline() и InlineVsNonInlineBenchmark.Inline() показывает, что Inline скорее всего будет работать быстрее. По сути, NonInline приходится много копировать (инструкции vmovdqu и vmovq), в то время как Inline просто считывает свойство, добавляет что-то и возвращает результат.PS: на самом деле JIT совершает множество гораздо более хитрых оптимизаций вашего кода. На последнем DotNext об этом был хороший доклад Дмитрия Егорова "JIT не волшебство: как он работает и как не мешать". У кого есть доступ, обязательно посмотрите. Остальные - подождите выхода на Youtube.
Источник: https://steven-giesel.com/blogPost/e89d7156-f3fd-4152-b78a-cb908bc43226/inlining-and-structs-in-c