Насколько умен мой компилятор? - PullRequest
9 голосов
/ 12 марта 2012

Я пытаюсь измерить приблизительную продолжительность цикла, которая требуется моему компьютеру для выполнения различных операций, поэтому я выполняю одну и ту же операцию 100 раз и вычисляю среднее значение.Я использую разматывание цикла , чтобы быть немного более точным: я выполняю 10 основных операций в каждой итерации и увеличиваю свой индекс на 10, что приводит к меньшему количеству операций цикла.

Ничего из этого на самом делевопрос для моего вопроса: может ли компилятор понять, что я выполняю одну и ту же операцию несколько раз и выполняю ее только один раз?Вот мой цикл:

for (i=0; i<iterations; i+=LOOP_FACTOR)
{
    result = -1;
    result = -1;
    result = -1;
    result = -1;
    result = -1;
    result = -1;
    result = -1;
    result = -1;
    result = -1;
    result = -1;
}

Кроме того, я не знаю, имеет ли это значение - я использую Eclipse .Я подумал, что это может иметь значение, поскольку существуют разные компиляторы.

Ответы [ 4 ]

16 голосов
/ 12 марта 2012

В GCC без оптимизации он компилируется как:

(gdb) disas main
Dump of assembler code for function main:
   0x00000000004004e4 <+0>: push   rbp
   0x00000000004004e5 <+1>: mov    rbp,rsp
   0x00000000004004e8 <+4>: mov    DWORD PTR [rip+0x200482],0x0        # 0x600974 <i>
   0x00000000004004f2 <+14>:    jmp    0x400567 <main+131>
   0x00000000004004f4 <+16>:    mov    DWORD PTR [rip+0x200472],0xffffffff        # 0x600970 <result>
   0x00000000004004fe <+26>:    mov    DWORD PTR [rip+0x200468],0xffffffff        # 0x600970 <result>
   0x0000000000400508 <+36>:    mov    DWORD PTR [rip+0x20045e],0xffffffff        # 0x600970 <result>
   0x0000000000400512 <+46>:    mov    DWORD PTR [rip+0x200454],0xffffffff        # 0x600970 <result>
   0x000000000040051c <+56>:    mov    DWORD PTR [rip+0x20044a],0xffffffff        # 0x600970 <result>
   0x0000000000400526 <+66>:    mov    DWORD PTR [rip+0x200440],0xffffffff        # 0x600970 <result>
   0x0000000000400530 <+76>:    mov    DWORD PTR [rip+0x200436],0xffffffff        # 0x600970 <result>
   0x000000000040053a <+86>:    mov    DWORD PTR [rip+0x20042c],0xffffffff        # 0x600970 <result>
   0x0000000000400544 <+96>:    mov    DWORD PTR [rip+0x200422],0xffffffff        # 0x600970 <result>
   0x000000000040054e <+106>:   mov    DWORD PTR [rip+0x200418],0xffffffff        # 0x600970 <result>
   0x0000000000400558 <+116>:   mov    eax,DWORD PTR [rip+0x200416]        # 0x600974 <i>
   0x000000000040055e <+122>:   add    eax,0x1
   0x0000000000400561 <+125>:   mov    DWORD PTR [rip+0x20040d],eax        # 0x600974 <i>
   0x0000000000400567 <+131>:   mov    eax,DWORD PTR [rip+0x200407]        # 0x600974 <i>
   0x000000000040056d <+137>:   cmp    eax,0x3e7
   0x0000000000400572 <+142>:   jle    0x4004f4 <main+16>
   0x0000000000400574 <+144>:   mov    eax,DWORD PTR [rip+0x2003f6]        # 0x600970 <result>
   0x000000000040057a <+150>:   mov    esi,eax
   0x000000000040057c <+152>:   mov    edi,0x40067c
   0x0000000000400581 <+157>:   mov    eax,0x0
   0x0000000000400586 <+162>:   call   0x4003e0 <printf@plt>
   0x000000000040058b <+167>:   pop    rbp
   0x000000000040058c <+168>:   ret

Но если вы работаете с базовой оптимизацией (gcc -O), то она сокращается до одной записи:

Dump of assembler code for function main:
   0x00000000004004e4 <+0>: sub    rsp,0x8
   0x00000000004004e8 <+4>: mov    eax,0x3e8
   0x00000000004004ed <+9>: sub    eax,0x1
   0x00000000004004f0 <+12>:    jne    0x4004ed <main+9>
   0x00000000004004f2 <+14>:    mov    DWORD PTR [rip+0x2003fc],0xffffffff        # 0x6008f8 <result>
   0x00000000004004fc <+24>:    mov    DWORD PTR [rip+0x2003f6],0x3e8        # 0x6008fc <i>
   0x0000000000400506 <+34>:    mov    esi,0xffffffff
   0x000000000040050b <+39>:    mov    edi,0x40060c
   0x0000000000400510 <+44>:    mov    eax,0x0
   0x0000000000400515 <+49>:    call   0x4003e0 <printf@plt>
   0x000000000040051a <+54>:    add    rsp,0x8
   0x000000000040051e <+58>:    ret  

Мой код тестирования:

#define TIMES 1000

int result, i;

int main() {
    for (i=0; i<TIMES; i++)
    {
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
    }
    printf("%d", result);
}
6 голосов
/ 12 марта 2012

Это, вероятно, оптимизирует этот код. Если вы хотите профилировать -1, вы должны запустить с -O0. Возможно, вам также следует сгенерировать некоторый код без цикла для профилирования отдельной инструкции.

5 голосов
/ 12 марта 2012

Нет смысла в профилировании кода без оптимизации.

Вместо этого я бы предложил объявить result как volatile.

Как и сейчас, ваш код, вероятно, будет оптимизирован для:

result = -1;

РЕЗУЛЬТАТЫ

Оба кода скомпилированы с полной оптимизацией:

volatile int result = 10000;

00401000  mov         ecx,3E8h 
00401005  or          eax,0FFFFFFFFh 
00401008  jmp         wmain+10h (401010h) 
0040100A  lea         ebx,[ebx] 
00401010  sub         ecx,1 
    {
        result = -1;
00401013  mov         dword ptr [result (40301Ch)],eax 
        result = -1;
00401018  mov         dword ptr [result (40301Ch)],eax 
        result = -1;
0040101D  mov         dword ptr [result (40301Ch)],eax 
        result = -1;
00401022  mov         dword ptr [result (40301Ch)],eax 
        result = -1;
00401027  mov         dword ptr [result (40301Ch)],eax 
        result = -1;
0040102C  mov         dword ptr [result (40301Ch)],eax 
        result = -1;
00401031  mov         dword ptr [result (40301Ch)],eax 
        result = -1;
00401036  mov         dword ptr [result (40301Ch)],eax 
        result = -1;
0040103B  mov         dword ptr [result (40301Ch)],eax 
        result = -1;
00401040  mov         dword ptr [result (40301Ch)],eax 
00401045  jne         wmain+10h (401010h) 
    }
    cout << result;
00401047  mov         eax,dword ptr [result (40301Ch)] 
0040104C  mov         ecx,dword ptr [__imp_std::cout (402038h)] 
00401052  push        eax  
00401053  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40203Ch)] 

int result = 10000;

    for (int i=0; i< 1000 ; i += 1)
    {
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
        result = -1;
    }
    cout << result;
00401000  mov         ecx,dword ptr [__imp_std::cout (402038h)] 
00401006  push        0FFFFFFFFh 
00401008  mov         dword ptr [result (40301Ch)],0FFFFFFFFh 
00401012  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40203Ch)] 
2 голосов
/ 12 марта 2012

Это зависит от уровня оптимизации вашего компилятора.как таковой, он может быть оптимизирован несколькими способами:

  • повторения будут объединены в 1 выражение (посредством исключения мертвых / избыточных назначений)
  • сам цикл будет сложен какничего не меняется между выполнением 1 итерации или 1000 итераций, поэтому его можно сложить в один экземпляр внутренних выражений.

Это становится немного сложнее, если result сделан volatile, что «предотвращает» компилятор, предполагая, что его значение не изменится вне цикла или последовательности выражений, оно может дажеДостаточно, чтобы предотвратить свертывание 10 внутренних операторов в 1.

Лучший способ проверить это - проверить вывод компилятора, используя что-то вроде objdump или отладчик.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...