Нанооптимизация в цикле for

267
02 июля 2017, 12:11

Например, есть массив с элементами.

int [] numbers = new int[] { 1, 2, 3, 4, 5 };
for (int i = 0; i < numbers.Length; ++i)
{
    Console.WriteLine(numbers[i]);
}
  1. Открыв ILSpy, обнаружил что (++i)преинкремент стал постинкриментом(i++), хотя казалось бы, должно быть наоборот.
  2. number.Length высчитывается 1 раз или при каждой итерации??(по идее каждый раз, но мало ли среда как-то это оптимизирует) Есть ли смысл выносить условие в отдельную переменную перед циклом?
Answer 1

Вы смотрите не туда.

Реальной оптимизацией занимается JIT-компилятор, а он довольно умный. Для вашего кода, он произвёл следующий объектный код (разумеется, Release-режим и без отладчика):

00007FF88B8B04ED  xor         edi,edi  ; i = 0
00007FF88B8B04EF  movsxd      rcx,edi  
00007FF88B8B04F2  mov         ecx,dword ptr [rsi+rcx*4+10h]  ; ecx = numbers[i]
00007FF88B8B04F6  call        00007FF8E9C19E40  ; Console.WriteLine
00007FF88B8B04FB  inc         edi               ; i++
00007FF88B8B04FD  cmp         edi,5             ; if (i < 5)
00007FF88B8B0500  jl          00007FF88B8B04EF  ; repeat

Вы видите, что не только исчезла разница между пре- и пост-инкрементом, не только исчезло повторное чтение Length, а ещё оптимизатор смог понять, что длина равна константе 5.

Поверьте, компиляторы справляются с нанооптимизациями куда лучше нас с вами.

На уровне IL-кода разницы между пре- и постинкрементом нет*:

IL_001e: ldloc.1
IL_001f: ldc.i4.1
IL_0020: add
IL_0021: stloc.1

То, что ILSpy декомпилировал эту последовательность команд на C# как постинкремент, это исключительно для удобства читателей (постинкремент привычнее).

Ну и финальный совет: нет никаких письменных правил (кроме личного опыта) по поводу того, что быстро, а что медленно. Хуже того, любые «правила» с выходом новой версии компилятора станут скорее всего неправильными: оптимизаторы улучшаются каждый день. Поэтому:

  • старайтесь писать так, чтобы ваш код был читаем, не увлекайтесь нанооптимизациями,
  • профилируйте ваш код регулярно и устраняйте bottleneck'и.

*Как правильно подсказывает в комментариях @PetSerAl, разница между пре- и постинкрементом в C# (в отличие от C++) состоит лишь в том, какое значение возвращается выражением. Поэтому для случая, когда результатом инкремента никто не пользуется, их смысл всегда одинаков (для любого типа, не только для int).

READ ALSO
Запрет на создание процессов, работы с файловой системой и интернетом

Запрет на создание процессов, работы с файловой системой и интернетом

Доброго времени суток! При написании подсистемы тестирования пользовательских решений задач по программированию столкнулся с такой проблемой:

174
Как в Angular Material(1.0) использовать один компонент?

Как в Angular Material(1.0) использовать один компонент?

Добрый вечерЕсть вопрос, использую Angular Material для первого Angular'a

207