Булево условие в для цикла, который всегда является ложью, оптимизированы далеко?

Необходимо предоставить информацию о том, какую операционную систему и тип файловой системы Вы используете. На определенных ароматах UNIX и определенных файловых систем Вы могли бы быть в состоянии использовать команды ff и ncheck как альтернативы.

5
задан Abel 27 November 2009 в 01:41
поделиться

7 ответов

Во-первых, процессоры имеют возможность, называемую предсказанием ветвлений . После нескольких запусков цикла процессор сможет заметить, что ваш оператор 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>();
14
ответ дан 18 December 2019 в 05:43
поделиться

Альтернативой может быть:

if(user_set_flag){
    while(1){
      ComputationAndOutput();
      OtherComputation();
    }
} else {
    while(1){
      OtherComputation();
    }
}

, но, как уже сказал Smashery, это микрооптимизация, которая не ускорит вашу программу так сильно, как другие оптимизации, которые вы наверняка можете сделать.

5
ответ дан 18 December 2019 в 05:43
поделиться

Я не думаю, что это вообще возможно дальше оптимизировать. Компилятор достаточно умен, чтобы знать, что значение user_set_flag не изменится во время выполнения цикла и сгенерирует для этого наиболее эффективный машинный код.

Это тоже в некоторой степени в области догадки компилятора. Если вы действительно действительно не знаете, что делаете, лучше придерживаться простейшего решения.

В качестве упражнения попробуйте профилировать (время) выполнение, используя как if (true), и if (user_set_flag) . Я предполагаю, что разница во времени выполнения будет нулевая.

6
ответ дан 18 December 2019 в 05:43
поделиться

Технически компилятор может оптимизировать подобные ситуации.

Например:

#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

Как вы можете видеть, Условие оценивается только один раз, чтобы определить, какой цикл запустить. И он немного развернул истинную ветвь :)

Я бы пришел к выводу, что нет причин беспокоиться об этих микрооптимизациях, если только вы не определите, что компилятор не может оптимизировать повторную оценку неизменяемого логического значения (например, если бы он был глобальным, как компилятор узнал бы, что он не будет изменен вызовами функций) и что это действительно узкое место.

4
ответ дан 18 December 2019 в 05:43
поделиться

Если вы знаете значение флага во время компиляции, вы можете добавить флаг компиляции, чтобы не включать оператор if, как:

while(1){
   #ifdef user_set_flag
    {
        //do some computation and output

    }
   #endif


    //do other computation
}
0
ответ дан 18 December 2019 в 05:43
поделиться

Вы говорите, что у пользователя действительно есть параметр, который может установить для этого флага значение true или false . Это означает, что это что-то может измениться во время выполнения. Это означает, что его нельзя оптимизировать (обычно).

В общем, компилятор может только «оптимизировать» то, что он знает во время компиляции . Что означает: в тот момент, когда вы нажимаете пункт «Build» в меню вашего редактора. Если он может измениться, его - как правило - нельзя оптимизировать.

Однако довольно легко (ну, в зависимости от того, какие части вы не показывали) самостоятельно оптимизировать. Если вас беспокоит одна инструкция сборки, которая используется внутри цикла, поместите оператор if за пределами цикла. Таким образом, она выполняется только один раз для вызова функции.

1
ответ дан 18 December 2019 в 05:43
поделиться

Если вы действительно хотите как можно быстрее, тогда вам нужно провести агрессивную настройку производительности. Так что забудьте о попытках угадать, что компилятор может сделать для оптимизации вашей программы.

Это робость.

Вместо этого возьмите на себя ответственность. Здесь показано, как.

0
ответ дан 18 December 2019 в 05:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: