У меня есть функция, которая находит следующую мощность двух для заданного целого. Если целое число равно двум, оно возвращает мощность.Почему компилятор C++ не может оптимизировать «if (test) --foo» to «foo - = test»?
Довольно прямо вперед:
char nextpow2if(int a)
{
char foo = char(32 - __builtin_clz(a));
bool ispow2 = !(a & a-1);
if (ispow2) --foo;
return foo;
}
Однако после компиляции с GCC 6 с -O2, после осмотра сгенерированного сборки, я вижу, что это компилируется, казалось бы, бесполезные инструкции cmovne
после вычисления Foo-1. Еще хуже с gcc5 и старше я получаю фактический jne
ветвь в коде.
Чем быстрее способ собрать это было бы, как если бы я написал следующую функцию:
char nextpow2sub(int a)
{
char foo = char(32 - __builtin_clz(a));
bool ispow2 = !(a & a-1);
return foo - ispow2;
}
Этот код правильно составленная все компиляторами кратчайший (и быстрым), монтаж возможен с sete
и вычитанием для bool.
Почему компилятор не может оптимизировать первый? Это похоже на очень простой случай идентификации. Почему gcc 5 и старше компилируют это в фактическую ветку jne
? Есть ли граничный случай между двумя версиями, который я не вижу, что может привести к тому, что они будут вести себя по-другому?
PS: Живая демонстрация here
Edit: Я не тестировал производительность с помощью GCC 6, но с ССЗ 5 последних примерно в два раза быстрее (ну на синтетическом тесте performanse, по крайней мере). Именно это и побудило меня задать этот вопрос.
Не спам-теги для несвязанных языков! – Olaf
* «Более быстрый способ скомпилировать это будет так, как если бы я написал следующую функцию:« * Вы измерили это? Насколько это быстрее? –
Вы сравниваете производительность по количеству сгенерированных ассемблерных кодов? Это не лучший способ сделать это (хотя в некоторых случаях это может быть правдой). – Arunmu