2017-01-06 2 views
3

Сегодня я начал играть с ветвлением, которая проверяет наличие двух булевых элементов. Я был уверен, что на каком-то уровне оптимизации они просто добавятся, а затем будут проверены, но это не так с gcc и clang. Почему gcc не оптимизирует две проверки bool, заменяя их добавлением и одной проверкой? Позвольте мне показать вам пример:Почему, если проверка не оптимизирована

void test(bool a, bool b) 
{ 
    // Branch 1 
    if (a && b) 
    { 
     std::cout << "Branch 1"; 
    } 

    // Branch 2 
    if (static_cast<int>(a) + static_cast<int>(b)) 
    { 
     std::cout << "Branch 2"; 
    } 
} 

GCC (даже с максимальным уровнем оптимизации) генерирует следующий код для филиала 1:

test dil,dil 
je  400794 <test(bool, bool)+0x14> 
test sil,sil 
jne 4007b0 <test(bool, bool)+0x30> 

в то время как он генерирует следующий код для ветви 2:

movzx ebx,bl 
movzx ebp,bpl 
add ebx,ebp 
jne 4007cf <test(bool, bool)+0x4f> 

Не должно быть двух ветвей (test + je) медленнее, чем добавление и ветвь (добавить + jne)?

Редактировать: то, что я на самом деле имел в виду, было умножением, потому что в случае сложения true и false (1 + 0) дает true (1), но умножение дает правильный результат (0).

+0

Я не забыл об этом, но как это повлияло на мой код? –

+1

Для начала добавление не '&&', это '||'. Кроме того, хотя и не уверен в текущем стандарте C++, но представление используется как '0' для' false' и все остальное для 'true'. Таким образом, вы могли бы иметь «-1» и «+ 1» для двух истинных значений, и добавление их было бы не очень хорошим. Точно так же вы могли бы иметь «1» и «2», а побитовые «и» не были бы хороши для них. – Jester

+5

@Jester: переменные 'bool' для C++ могут быть только« true »или' false' (I * think *, что верно и для значений C 'bool'.) При преобразовании в' int', они * должны * конвертировать в 1 или 0 соответственно. –

ответ

-1

Вы забываете, что логические операторы short-circuit evaluation.

Для логического и, если левая сторона ложна, тогда правая часть не оценивается.

+4

Правда, но не имеет значения, поскольку оба уже являются логическими, ничего не« оценивать », компилятор может очень хорошо его оптимизировать , – Jester

+2

Как оценочный порядок влияет на мой код? Они проверили в порядке или просто добавили, что даст тот же результат. Нет никакого способа, чтобы результаты были разными, поэтому это можно оптимизировать. –

6

На уровне абстрактной машины && заставляет второе выражение не оцениваться, если первое ложное. По правилу as-if компилятор может в любом случае решить оценить второе выражение - если он может доказать, что он определил поведение (или неопределенное поведение не имеет значения) и никаких побочных эффектов; однако писатели-компиляторы явно решили, что это не стоит.

Если вы не хотите короткую резку, & может быть полезно (с комментариями).

+0

Разве это не странно, что оптимизатор не видит, что короткое замыкание не требуется? Например. a && b даст тот же результат, что и a + b (или a * b), поэтому его можно использовать вместо этого. –

+0

@ElvissStrazdins Они могут не делать этого, потому что они делают это в общем. 'a && b && c && d && e && f' сможет не оценивать что-либо, кроме' a', если 'a' является ложным, где, как и при умножении, ему нужно будет оценить/умножить весь результат вместе. – NathanOliver

+1

@ElvissStrazdins: Примечание 'a && b' не приведет к тому же результату, что и' a + b' во всех возможных случаях. Рассмотрим 'a = true' и' b = false'. –

2

Почему gcc не оптимизирует две проверки bool, заменив их добавлением и одной проверкой?

Предлагаемая оптимизация неверна. дополнение не является надлежащей заменой для оператора &&, поскольку первый будет оцениваться до true, если не менее (не оба) одно условие - true.

Вопрос остается, как не может быть оптимизирован?

C++ Стандартные гарантии, что bool значения преобразуются в int с определенными значениями: false преобразует в 0 и true превращается в 1. Следовательно, следующая конструкция является полностью законным эквивалентом (при условии, что оба a и b просто bool переменные):

if (a & b) // OK, integral promotions take place (bool ---> int) 

Предполагая, что компилятор всегда хранит bool значения с одной и той же внутреннее представление для true (например,0x1) и false (0x0), условие может непосредственно переведены на x86 test инструкции:

test sil, dil 

Вот хитрая часть. По-видимому, компилятор GCC изменил свое поведение между основными линиями 4.6 и 4.7. Даже при явном нажатии на int он сохраняет два отдельных перехода. Условие:

if (static_cast<int>(a) & static_cast<int>(b)) 

генерирует код (GCC 6.2.0 с уровнем -O3 оптимизации):

test dil, dil 
je  .L1 
test sil, sil 
je  .L1 

С другой стороны, как ICC и MSVC 2015 компиляторы "истинного" побитовое и:

movzx eax, BYTE PTR b$[rsp] 
test al, BYTE PTR a$[rsp] 
je SHORT [email protected] 

, который также в случае GCC до версии 4.7 (GCC 4.6.3 с -O3):

movzx edi, dil 
test esi, edi 
+1

Выглядит как [упущенная возможность оптимизации] (https://godbolt.org/g/JcJAZG) для GCC и Clang. Как вы отметили, как ICC, так и MSVC верны и испускают простую инструкцию 'test'. Кто-то должен подать отчет о дефектах как для GCC, так и для Clang. Я не могу придумать ни одной причины, по которой нужно было бы самостоятельно проверять значения, и это, безусловно, не оптимально. –

Смежные вопросы