Bor*_*rov 6 c++ g++ compiler-optimization
我有一个函数,它找到给定整数的下一个2的幂.如果整数是2的幂,则返回功率.
非常直截了当:
char nextpow2if(int a)
{
char foo = char(32 - __builtin_clz(a));
bool ispow2 = !(a & a-1);
if (ispow2) --foo;
return foo;
}
Run Code Online (Sandbox Code Playgroud)
但是在使用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;
}
Run Code Online (Sandbox Code Playgroud)
所有编译器都将此代码正确编译为最短(和最快)可能的程序集,sete并使用bool的减法.
为什么编译器无法优化第一个?这似乎是一个非常容易识别的案例.为什么gcc 5和更早版本将它编译成实际的jne分支?两个版本之间是否存在边缘情况,我看不到,这可能导致它们的行为不同?
PS:现场演示在这里
编辑:我没有用gcc 6测试性能,但是使用gcc 5,后者的速度提高了两倍(至少在合成性能测试中).这实际上是让我提出这个问题的原因.
我相信其原因可能是bool通常存储在一个字节内。因此,编译器可能无法安全地假设实际内存恰好等于1。true/false检查可能只是与零进行比较。然而,减法可能是一个不同的故事,有副作用。
请参阅Ideone 上的示例代码:
#include <iostream>
using namespace std;
union charBool
{
unsigned char aChar;
bool aBool;
};
int main()
{
charBool var;
charBool* varMemory = &var;
var.aBool = 65;
std::cout << "a boolean = " << var.aBool << std::endl;
std::cout << "a char = " << var.aChar << std::endl;
std::cout << "varMemory = " << (*(reinterpret_cast<unsigned char*>(varMemory))) << std::endl;
var.aChar = 98; // note: Ideone C++ compiler resolves this to zero, hence bit0 seems to be the only checked
std::cout << "a boolean = " << var.aBool << std::endl;
std::cout << "a char = " << var.aChar << std::endl;
std::cout << "varMemory = " << (*(reinterpret_cast<unsigned char*>(varMemory))) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
结果是:
a boolean = 1
a char =
varMemory =
a boolean = 0
a char = b
varMemory = b
Run Code Online (Sandbox Code Playgroud)
(注:前两个字符不可打印)