Я написал фрагмент кода C, чтобы показать точку в обсуждении оптимизаций и прогнозирования ветвлений. Затем я заметил еще более разнообразный результат, чем ожидал. Моя цель состояла в том, чтобы написать его на языке, который является общим подмножеством между С++ и C, стандартным для обоих языков, и это довольно портативно. Он был протестирован на разных ПК с ОС Windows:
#include <stdio.h>
#include <time.h>
/// @return - time difference between start and stop in milliseconds
int ms_elapsed( clock_t start, clock_t stop )
{
return (int)( 1000.0 * ( stop - start ) / CLOCKS_PER_SEC );
}
int const Billion = 1000000000;
/// & with numbers up to Billion gives 0, 0, 2, 2 repeating pattern
int const Pattern_0_0_2_2 = 0x40000002;
/// @return - half of Billion
int unpredictableIfs()
{
int sum = 0;
for ( int i = 0; i < Billion; ++i )
{
// true, true, false, false ...
if ( ( i & Pattern_0_0_2_2 ) == 0 )
{
++sum;
}
}
return sum;
}
/// @return - half of Billion
int noIfs()
{
int sum = 0;
for ( int i = 0; i < Billion; ++i )
{
// 1, 1, 0, 0 ...
sum += ( i & Pattern_0_0_2_2 ) == 0;
}
return sum;
}
int main()
{
clock_t volatile start;
clock_t volatile stop;
int volatile sum;
printf( "Puzzling measurements:\n" );
start = clock();
sum = unpredictableIfs();
stop = clock();
printf( "Unpredictable ifs took %d msec; answer was %d\n"
, ms_elapsed(start, stop), sum );
start = clock();
sum = unpredictableIfs();
stop = clock();
printf( "Unpredictable ifs took %d msec; answer was %d\n"
, ms_elapsed(start, stop), sum );
start = clock();
sum = noIfs();
stop = clock();
printf( "Same without ifs took %d msec; answer was %d\n"
, ms_elapsed(start, stop), sum );
start = clock();
sum = unpredictableIfs();
stop = clock();
printf( "Unpredictable ifs took %d msec; answer was %d\n"
, ms_elapsed(start, stop), sum );
}
Скомпилирован с VS2010;/O2. Результаты Intel Core 2, WinXP:
Puzzling measurements:
Unpredictable ifs took 1344 msec; answer was 500000000
Unpredictable ifs took 1016 msec; answer was 500000000
Same without ifs took 1031 msec; answer was 500000000
Unpredictable ifs took 4797 msec; answer was 500000000
Изменить: Полные ключи компилятора:
/Zi/nologo/W3/WX-/O2/Oi/Oy-/GL/D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm -/EHsc/GS/Gy/fp: exact/Zc: wchar_t/Zc: forScope/Fp "Release\Trying.pch" /Fa "Release\" /Fo "Release\" /Fd "Release\vc100.pdb" /Gd/analysis -/errorReport: queue
Другое лицо опубликовано такое... Скомпилировано с MinGW, g++ 4.71, -O1 оптимизация Результаты Intel Core 2, WinXP:
Puzzling measurements:
Unpredictable ifs took 1656 msec; answer was 500000000
Unpredictable ifs took 0 msec; answer was 500000000
Same without ifs took 1969 msec; answer was 500000000
Unpredictable ifs took 0 msec; answer was 500000000
Также он опубликовал такие результаты для оптимизации -O3:
Puzzling measurements:
Unpredictable ifs took 1890 msec; answer was 500000000
Unpredictable ifs took 2516 msec; answer was 500000000
Same without ifs took 1422 msec; answer was 500000000
Unpredictable ifs took 2516 msec; answer was 500000000
Теперь у меня вопрос. Что здесь происходит?
Подробнее... Как фиксированная функция может занимать столько времени? Что-то не так в моем коде? Что-то сложно с процессором Intel? Компиляторы делают что-то странное? Может ли это быть из-за 32-разрядного кода, запущенного на 64-битном процессоре?
Спасибо за внимание!
Edit: Я принимаю, что g++ -O1 просто повторно использует возвращаемые значения в двух других вызовах. Я также согласен с тем, что g++ -O2 и g++ -O3 имеют дефект, который оставляет оптимизацию. Значительное разнообразие измеренных скоростей (450%!!!) кажется загадочным.
Я посмотрел на разборку кода, созданного VS2010. Он сделал inline unpredictableIfs
3 раза. Встроенный код был довольно схожим; петля была такой же. Он не встроил noIfs
. Он немного отбросил noIfs
. Для одной итерации требуется 4 шага. noIfs
вычислить, как было написано, а unpredictableIfs
использовать jne
для перехода через приращение.