Вот две функции, которые я утверждаю, делают то же самое:
bool fast(int x)
{
return x & 4242;
}
bool slow(int x)
{
return x && (x & 4242);
}
Логически они делают то же самое, и только чтобы быть на 100% уверенным, что я написал тест, который управлял всеми четырьмя миллиардами возможных входов через оба из них, и они соответствовали. Но код сборки - это другая история:
fast:
andl $4242, %edi
setne %al
ret
slow:
xorl %eax, %eax
testl %edi, %edi
je .L3
andl $4242, %edi
setne %al
.L3:
rep
ret
Я был удивлен, что GCC не смог совершить прыжок логики, чтобы устранить избыточный тест. Я пробовал g++ 4.4.3 и 4.7.2 с -O2, -O3 и -Os, все из которых сгенерировали один и тот же код. Платформа - это Linux x86_64.
Может кто-нибудь объяснить, почему GCC не должен быть достаточно умным, чтобы генерировать один и тот же код в обоих случаях? Я также хотел бы знать, могут ли другие компиляторы работать лучше.
Отредактируйте, чтобы добавить тестовый жгут:
#include <cstdlib>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
// make vector filled with numbers starting from argv[1]
int seed = atoi(argv[1]);
vector<int> v(100000);
for (int j = 0; j < 100000; ++j)
v[j] = j + seed;
// count how many times the function returns true
int result = 0;
for (int j = 0; j < 100000; ++j)
for (int i : v)
result += slow(i); // or fast(i), try both
return result;
}
Я тестировал выше с clang 5.1 на Mac OS с -O3. Потребовалось 2,9 секунды, используя fast()
и 3.8 секунды, используя slow()
. Если вместо этого я использую вектор всех нулей, то между этими двумя функциями нет существенной разницы в производительности.