Почему встроенный ассемблер ИНОГДА быстрее, а ИНОГДА в этом коде медленнее?Время выполнения меняется немного с каждым прогоном - PullRequest
0 голосов
/ 28 февраля 2019

Я написал некоторый код C ++ для тестирования на время C ++ и встроенный ассемблерный код.Сначала мне было просто весело с этим, но потом я заметил, что каждый раз, когда я запускал свою программу, я получал разные результаты.Иногда C ++ был быстрее, иногда встроенный ассемблерный код был быстрее, а иногда они были одинаковыми.

Что здесь происходит?

Вот код с выводом программы:

#define TRIALS 1000000
#include <iostream>
using namespace std;
typedef std::chrono::high_resolution_clock Clock;
int main()
{
  auto t1 = Clock::now();
  auto t2 = Clock::now();
  int X3=17;
  int X2=17;
  int X4=17;
  int X=17;



  int sum=0;
  int avg=0;
  cout << "=================================" << endl;
  cout << "| var*=10;                      |" << endl;
  cout << "=================================" << endl;

  for( int i=0; i<TRIALS; i++ )
    {
      X3=17;
      t1 = Clock::now();  
      X3*=10;
      t2 = Clock::now();
      sum+=chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
    }
  avg=sum/TRIALS;
  cout << "| Product:  " << X3<< "  "<< avg << " nanoseconds |" << endl;
  cout << "=================================" << endl;
  cout << endl << endl;

  avg=sum=0;
  cout << "=================================" << endl;
  cout << "| use inline assembler with shl |" << endl;
  cout << "=================================" << endl;

  for( int i=0; i<TRIALS; i++ )
    {
      X=17;
      t1 = Clock::now();
      asm /*volatile*/ (
            "movl %0, %%eax;" // X->ax
            "shll %%eax;"// ax*=2
            "movl %%eax, %%ebx;" // ax->bx
            "shll %%eax;" // ax*=2
            "shll %%eax;" // ax*=2
            "add %%ebx, %%eax;" // bx+ax->ax
            : "=a" (X)
            : "a" (X)
            : "%ebx"
            );
      t2 = Clock::now();
      sum+=chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
    }
  avg=sum/TRIALS;
  cout << "| Product:  " << X << "  "<< avg << " nanoseconds |" << endl;
  cout << "=================================" << endl;
  cout << endl << endl;
  avg=sum=0;

  cout << "=================================" << endl;
  cout << "| var=var*10                    |" << endl;
  cout << "=================================" << endl;

  for( int i=0; i<TRIALS; i++ )
    {
      X2=17;
      t1 = Clock::now();
      X2=X2*10;
      t2 = Clock::now();
      sum+=chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
    }
  avg=sum/TRIALS;
  cout << "| Product:  " << X3<< "  "<< avg << " nanoseconds |" << endl;
  cout << "=================================" << endl;
  cout << endl << endl;

  avg=sum=0;


  cout << "=================================" << endl;
  cout << "| use inline assembler with mul |" << endl;
  cout << "=================================" << endl;
  for( int i=0; i<TRIALS; i++ )
    {
      X4=17;
      t1 = Clock::now();
      asm  (
    "movl %0, %%eax;" // X->ax
    "movl $0x0A, %%ebx;" // 10->bx
    "mul %%ebx;" // 10*ax->ax
    : "=a" (X4)
    : "a" (X4)
    : "%ebx"
    );
      t2 = Clock::now();
      sum+=chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
    }
  avg=sum/TRIALS;
  cout << "| Product:  " << X4<< "  "<< avg << " nanoseconds |" << endl;
  cout << "=================================" << endl;
  cout << endl;

  return(0);
}

ВЫХОД ПРОГРАММЫ № 1:

=================================
| var*=10;                      |
=================================
| Product:  170  50 nanoseconds |
=================================


=================================
| use inline assembler with shl |
=================================
| Product:  170  50 nanoseconds |
=================================


=================================
| var=var*10                    |
=================================
| Product:  170  50 nanoseconds |
=================================


=================================
| use inline assembler with mul |
=================================
| Product:  170  50 nanoseconds |
=================================

ВЫХОД № 2:

=================================
| var*=10;                      |
=================================
| Product:  170  62 nanoseconds |
=================================


=================================
| use inline assembler with shl |
=================================
| Product:  170  57 nanoseconds |
=================================


=================================
| var=var*10                    |
=================================
| Product:  170  59 nanoseconds |
=================================


=================================
| use inline assembler with mul |
=================================
| Product:  170  58 nanoseconds |
=================================

1 Ответ

0 голосов
/ 28 февраля 2019

Это больше похоже на подсказки, а не просто на решение:

1) разверните ТРЕЙЛЫ на порядки величин, чтобы реально что-то измерить в диапазоне секунд

2) повторите измерение несколькораз (n = 100 или более) и возьмите среднее значение (и ошибку среднего = среднеквадратическое значение / квадрат (n), если вам нужна статистика)

3) на самом деле измерьте то, что вы хотите измерить: по крайней мере, поместите толькокод, который вас интересует, в цикл TRAILS, например:

t1 = Clock::now();  
for( int i=0; i<TRIALS; i++ )
    {
     ... only code relevant for your calculation here ...
    }
t2 = Clock::now();
sum = chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();

4) и, наконец, рассмотрим службу проводника компилятора godbolt https://godbolt.org/, где вы можете проверить выходные данные ассемблера вашего кода на различныеНастройки оптимизатора.Для кода, столь же простого, как ваш (я пытался), он просто делает (используя -O3): mov eax,170 так что вы видите: компилятор умен, вы не можете легко победить его встроенным ассемблером!И это наверняка так для нетривиальных примеров.

...