Игрался с замером скорости доступа к L1,L2,L3 кэша процессора средствами C# и случайно наткнулся на странное поведение компилятора (vs2017, х86, со включенной оптимизацией).
Приведу адаптированный кусок кода:
fixed (uint** array = new uint*[256])
{
var p = array;
uint iters = 1024;
for (uint i = 0; i < iters; i++)
p = (uint**)*p;
}
На выходе, цикл компилится абсолютно корректно:
011E0884 xor edx,edx //uint i = 0
011E0886 mov eax,dword ptr [eax] //p = (uint**)*p;
011E0888 inc edx //i++
011E0889 cmp edx,400h //i < 1024
011E088F jb 011E0886
Но если изменить тип переменной iters, начинается магия:
fixed (uint** array = new uint*[256])
{
var p = array;
ulong iters = 1024; // <--- отличие в этой строке
for (uint i = 0; i < iters; i++)
p = (uint**)*p;
}
В этом случае цикл компилится вот в такое:
00BA0888 xor edi,edi //uint i = 0
00BA088A mov esi,dword ptr [esi] //p = (uint**)*p;
00BA088C inc edi //i++
00BA088D xor eax,eax // Д
00BA088F test eax,eax // И
00BA0891 ja 00BA089D // Ч
00BA0893 jb 00BA088A // Ь
00BA0895 cmp edi,400h //i < 1024
00BA089B jb 00BA088A
Вопросы:
Почему после изменения типа переменной
iters, которая оптимизатором заменяется на константу, и которой по сути вообще нет, появляются лишние бестолковые инструкции?В чем сакральный смысл помеченных четырех инструкций? Сперва обнуляем
eax. Потом проверяем, а не ноль ли там случаем. И потом эти джампы, которые, насколько я понимаю, никогда не сработают... Это баг или фича?
(uint**)*p;с указателями ничего не напутали? Вообще удивительно, что даже со якобы включенной оптимизацией этот цикл не был выкинут целиком. – user7860670 Oct 04 '18 at 20:34