Обратные вызовы в C++ | страница 21



2.4.6. Производительность

С точки зрения машинных команд, вызов функции – не слишком быстрая операция. Необходимо несколько команд для сохранения стека10; команда перехода к коду функции; команда возврата управления; несколько команд для восстановления стека. А если код тела функции небольшой, к примеру, всего лишь сравнение двух величин, то время, затраченное на вызов функции, может значительно превысить время выполнения кода функции.

Поясним сказанное на примере. Напишем маленькую простую программу, которая считывает из консоли два числа, складывает их и результат выводит на экран (Листинг 19).

Листинг 19. Маленькая простая программа

>#include 


>int Calculate(int a, int b)

>{

>  return a + b;

>}


>int main()

>{

>  int a, b;

>  std::cin >> a >> b;

>  int result = Calculate(a, b);

>  std::cout << result;

>}


Откомпилируем код с выключенной оптимизацией и запустим на выполнение. Посмотрим дизассемблерный участок кода 11, в котором производится вызов функции (Листинг 20):

Листинг 20. Дизассемблерный код с выключенной оптимизацией:

>int Calculate(int a, int b)

>{

>00007FF6DA741005  and         al,8               // 1

>return a + b;

>00007FF6DA741008  mov         eax,dword ptr [b]  // 2

>00007FF6DA74100C  mov         ecx,dword ptr [a]  // 3

>00007FF6DA741010  add          ecx,eax           // 4

>00007FF6DA741012  mov         eax,ecx            // 5

>}

>00007FF6DA741014  ret                            // 6


>int main()

>{

>…….

>int result = Calculate(a, b);

>00007FF6DA741053  mov         edx,dword ptr [b]              // 7

>00007FF6DA741057  mov         ecx,dword ptr [a]              // 8

>00007FF6DA74105B  call        Calculate (07FF6DA741000h)     // 9

>00007FF6DA741060  mov         dword ptr [result],eax         // 10

>…….


В строках 7 и 8 введенные значения a и b сохраняются в регистрах. В строке 9 выполняется вызов функции. В строке 1 выполняется обнуление результата, в строках 2 и 3 переданные значения копируются в регистры, в строке 4 выполняется сложение, в строке 5 результат копируется обратно в регистр, в строке 6 выполняется выход из функции, в строке 10 результат вычисления функции копируется в переменную результата.

Теперь включим оптимизацию, откомпилируем и посмотрим на код (Листинг 21):

Листинг 21. Дизассемблерный код с включенной оптимизацией

>int main()

>{

>…….

>int result = Calculate(a, b);

>00007FF7D5B11033  mov         edx,dword ptr [b]

>00007FF7D5B11037  add          edx,dword ptr [a]  


Как видим, для вычислений у нас всего две операции: запись в регистр значения b и добавление к нему значения a. Код встроен в поток выполнения, вызов функции не производится. Ощутимая разница, не правда ли?