Необходимо предоставить информацию о том, какую операционную систему и тип файловой системы Вы используете. На определенных ароматах UNIX и определенных файловых систем Вы могли бы быть в состоянии использовать команды ff
и ncheck
как альтернативы.
Во-первых, процессоры имеют возможность, называемую предсказанием ветвлений . После нескольких запусков цикла процессор сможет заметить, что ваш оператор if
всегда идет в одну сторону. (Он может даже замечать обычные шаблоны, такие как true false true false
.) Затем он спекулятивно выполнит эту ветвь, и до тех пор, пока он сможет правильно предсказать, дополнительные затраты на Оператор if
практически исключен. Если вы считаете, что пользователь с большей вероятностью выберет true
, а не false
, вы даже можете сообщить об этом компилятору gcc (специфичное для gcc расширение).
Однако в одном из комментариев вы упомянули, что у вас «гораздо более сложная последовательность bools». Я думаю, что вполне возможно, что у процессора нет памяти для сопоставления с образцом всех этих прыжков - к тому времени, когда он вернется к первому оператору if
, знание того, в каком направлении прошел этот прыжок, уже был вытеснен из его памяти. Но здесь мы могли бы помочь ...
Компилятор имеет возможность преобразовывать циклы и операторы if в то, что он считает более оптимальными формами. Например, он может преобразовать ваш код в форму, заданную schnaader. Это известно как отключение цикла . Вы можете помочь в этом, выполнив Профильную оптимизацию (PGO) , сообщив компилятору, где находятся горячие точки. (Примечание: в gcc -funswitch-loops
включается только в -O3
. )
Вы должны профилировать ваш код на уровне инструкций ( VTune будет хорошим инструментом для этого), чтобы увидеть, действительно ли операторы if являются узким местом. Если это действительно так, и если, глядя на сгенерированную сборку, вы думаете, что компилятор ошибся, несмотря на PGO, вы можете попробовать самостоятельно поднять оператор if. Возможно, шаблонный код сделает его более удобным:
template<bool B> void innerLoop() {
for (int i=0; i<10000; i++) {
if (B) {
// some stuff..
} else {
// some other stuff..
}
}
}
if (user_set_flag) innerLoop<true>();
else innerLoop<false>();
Альтернативой может быть:
if(user_set_flag){
while(1){
ComputationAndOutput();
OtherComputation();
}
} else {
while(1){
OtherComputation();
}
}
, но, как уже сказал Smashery, это микрооптимизация, которая не ускорит вашу программу так сильно, как другие оптимизации, которые вы наверняка можете сделать.
Я не думаю, что это вообще возможно дальше оптимизировать. Компилятор достаточно умен, чтобы знать, что значение user_set_flag
не изменится во время выполнения цикла и сгенерирует для этого наиболее эффективный машинный код.
Это тоже в некоторой степени в области догадки компилятора. Если вы действительно действительно не знаете, что делаете, лучше придерживаться простейшего решения.
В качестве упражнения попробуйте профилировать (время) выполнение, используя как if (true),
и if (user_set_flag)
. Я предполагаю, что разница во времени выполнения будет нулевая.
Технически компилятор может оптимизировать подобные ситуации.
Например:
#include <cstdio>
int main(int argc, char* [])
{
while (true)
{
if (argc == 1) {
puts("one");
}
puts("some more");
}
}
main компилируется в (G ++ -O3):
cmpl $1, 8(%ebp)
je L9
.p2align 4,,15
L2:
movl $LC1, (%esp)
call _puts
jmp L2
L9:
movl $LC0, (%esp)
call _puts
movl $LC1, (%esp)
call _puts
movl $LC0, (%esp)
call _puts
movl $LC1, (%esp)
call _puts
jmp L9
Как вы можете видеть, Условие оценивается только один раз, чтобы определить, какой цикл запустить. И он немного развернул истинную ветвь :)
Я бы пришел к выводу, что нет причин беспокоиться об этих микрооптимизациях, если только вы не определите, что компилятор не может оптимизировать повторную оценку неизменяемого логического значения (например, если бы он был глобальным, как компилятор узнал бы, что он не будет изменен вызовами функций) и что это действительно узкое место.
Если вы знаете значение флага во время компиляции, вы можете добавить флаг компиляции, чтобы не включать оператор if, как:
while(1){
#ifdef user_set_flag
{
//do some computation and output
}
#endif
//do other computation
}
Вы говорите, что у пользователя действительно есть параметр, который может установить для этого флага значение true
или false
. Это означает, что это что-то может измениться во время выполнения. Это означает, что его нельзя оптимизировать (обычно).
В общем, компилятор может только «оптимизировать» то, что он знает во время компиляции . Что означает: в тот момент, когда вы нажимаете пункт «Build» в меню вашего редактора. Если он может измениться, его - как правило - нельзя оптимизировать.
Однако довольно легко (ну, в зависимости от того, какие части вы не показывали) самостоятельно оптимизировать. Если вас беспокоит одна инструкция сборки, которая используется внутри цикла, поместите оператор if за пределами цикла. Таким образом, она выполняется только один раз для вызова функции.
Если вы действительно хотите как можно быстрее, тогда вам нужно провести агрессивную настройку производительности. Так что забудьте о попытках угадать, что компилятор может сделать для оптимизации вашей программы.
Это робость.
Вместо этого возьмите на себя ответственность. Здесь показано, как.