我正在编写一些模板代码来对使用浮点数和双精度数的数值算法进行基准测试,以便与 GPU 实现进行比较。
我发现我的浮点代码速度较慢,在使用 Intel 的 Vtune Amplifier 进行调查后,我发现 g++ 正在生成额外的 x86 指令(cvtps2pd/cvtpd2ps 和 unpcklps/unpcklpd),以将一些中间结果从 float 转换为 double,然后再转换回来。此应用程序的性能下降了近 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++ 仅使用浮点数进行此计算吗?
编辑:由迈克·西摩解决。需要使用 std::log 来确保获取日志的重载版本,而不是调用 Cdouble log(double)
。不会为分离的语句生成警告,因为这是转换而不是升级。