Страницы

Поиск по вопросам

понедельник, 8 октября 2018 г.

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

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

Комментариев нет:

Отправить комментарий