Страницы

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

четверг, 2 января 2020 г.

Странные (лишние) инструкции после компиляции

#c_sharp #ассемблер #компиляция #компилятор


Игрался с замером скорости доступа к 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. Потом
проверяем, а не ноль ли там случаем. И потом эти джампы, которые, насколько я понимаю,
никогда не сработают... Это баг или фича?

    


Ответы

Ответ 1



Похоже что оптимизатор не осилил убрать смесь каста uint в ulong и последующего сравнения. Каст был из (edi) в (eax, edi), и выглядит как заполнение eax нулём (через xor). 00BA088C inc edi //i++ // каст uint i в ulong. Результат в паре eax, edi 00BA088D xor eax,eax // поразрядное сравнение двух ulong // старший разряд 00BA088F test eax,eax // вместо cmp eax, 0 00BA0891 ja 00BA089D 00BA0893 jb 00BA088A // младший разряд 00BA0895 cmp edi,400h 00BA089B jb 00BA088A Т.е. вроде как оптимизатор мог догадаться, что верхний разряд можно не сравнивать, но не догадался. Оптимизатор x86 старый, не ждите от него слишком многого :)

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

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