Извините, может быть, слишком абстрактный вопрос, но для меня это вполне практично + может быть, некоторые эксперты имеют схожий опыт и могут объяснить это.
У меня есть большой код размером около 10000 строк.
Я замечаю, что если в определенном месте я положил
if ( expression ) continue;
где выражение всегда false (дважды проверено с логикой кода и cout), но зависит от неизвестных параметров (поэтому компилятор не может просто избавиться от этой строки во время компиляции) скорость программы увеличено на 25% (результат расчета одинаковый). Если я измеряю скорость самого цикла, коэффициент ускорения больше 3.
Почему это может случиться и какие возможные способы использовать эту возможность ускорения без таких трюков?
P.S. Я использую оптимизацию gcc 4.7.3, -O3.
Дополнительная информация:
-
Я пробовал два разных выражения, оба работают.
-
Если я изменил строку на:
if ( expression ) { cout << " HELLO " << endl; continue; };
скорость ушла.
-
Если я изменил строку на:
expression;
скорость ушла.
-
Код, который окружает строку, выглядит следующим образом:
for ( int i = a; ; ) { do { i += d; if ( d*i > d*ilast ) break; // small amount of calculations, and conditional calls of continue; } while ( expression0 ); if ( d*i > dir*ilast ) break; if ( expression ) continue; // very big amount calculations, and conditional calls of continue; }
цикл for выглядит странно. Это потому, что я изменил петли, чтобы поймать эту горло бутылки. Изначально выражение было равно выражению 0, а вместо do-loop у меня было только это.
-
Я попытался использовать __builtin_expect, чтобы понять предсказание ветвей. С
// the expression (= false) is supposed to be true by branch prediction. if ( __builtin_expect( !!(expression), 1) ) continue;
скорость увеличивается на 25%.
// the expression (= false) is supposed to be false by branch prediction. if ( __builtin_expect( !!(expression), 0) ) continue;
скорость ушла.
-
Если я использую -O2 вместо -O3, эффект исчезнет. Код немного (~ 3%) медленнее, чем быстрая версия O3 с ложным условием.
-
То же самое для "-O2 -finline-functions -funswitch-loops -fpredictive-commoning -fgcse-after-reload -ftree-vectorize". С еще одним вариантом: "-O2 -finline-functions -funswitch-loops -fpredictive-commoning -fgcse-after-reload -ftree-vectorize -fipa-cp-clone" эффект усиливается. С "линией" скорость такая же, без "линии" код на 75% медленнее.
-
Причина заключается только в следующем условном операторе. Таким образом, код выглядит следующим образом:
for ( int i = a; ; ) { // small amount of calculations, and conditional calls of continue; if ( expression ) continue; // calculations1 if ( expression2 ) { // calculations2 } // very big amount calculations, and conditional calls of continue; }
Значение выражения2 почти всегда ложно. Поэтому я изменил его следующим образом:
for ( int i = a; ; ) { // small amount of calculations, and conditional calls of continue; // if ( expression ) continue; // don't need this anymore // calculations1 if ( __builtin_expect( !!(expression2), 0 ) ) { // suppose expression2 == false // calculations2 } // very big amount calculations, and conditional calls of continue; }
И получили желаемые 25% ускорения. Еще немного. И поведение больше не зависит от критической линии.
Если кто-то знает материалы, которые могут объяснить это поведение без догадок, я буду очень рад прочитать и принять их ответ.