我正在编写一些模板代码来对使用浮点数和双精度数的数值算法进行基准测试,以便与 GPU 实现进行比较。
我发现我的浮点代码速度较慢,在使用英特尔的 Vtune Amplifier 进行调查后,我发现 g++ 正在生成额外的 x86 指令(cvtps2pd/cvtpd2ps 和 unpcklps/unpcklpd)以将一些中间结果从浮点数转换为双精度然后再返回。此应用程序的性能下降近 10%。
在使用标志 -Wdouble-promotion 编译后(顺便说一句,-Wall 或 -Wextra 不包含该标志),果然 g++ 警告我结果正在提升。
我将其简化为一个简单的测试用例,如下所示。请注意,c++ 代码的顺序会影响生成的代码。复合语句 (T d1 = log(r)/r;) 会产生警告,而单独的语句不会 (T d = log(r); d/=r;)。
以下使用 g++-4.6.3-1ubuntu5 和 g++-4.7.3-2ubuntu1~12.04 编译,结果相同。
编译标志是:
g++-4.7 -O2 -Wdouble-promotion -Wextra -Wall -pedantic -Werror -std=c++0x test.cpp -o test
#include <cstdlib>
#include <iostream>
#include <cmath>
template <typename T>
T f()
{
T r = static_cast<T>(0.001);
// Gives no double promotion warning
T d = log(r);
d/=r;
// Promotes to double
T d1 = log(r)/r;
return d+d1;
}
int main()
{
float f1 = f<float>();
std::cout << f1 << std::endl;
}
我意识到 c++11 标准允许编译器在这里自行决定。但是为什么顺序很重要?
我可以明确指示 g++ 仅将浮点数用于此计算吗?
编辑:由 Mike Seymour 解决。需要使用 std::log 来确保获取重载版本的 log 而不是调用 C double log(double)
。没有为分隔语句生成警告,因为这是转换而不是提升。