C++ 为什么g++;(4.6和4.7)将该划分的结果提升到双倍?我能阻止它吗?
我正在编写一些模板代码,用浮点和双精度对数值算法进行基准测试,以便与GPU实现进行比较 我发现我的浮点代码速度较慢,在使用英特尔Vtune放大器进行调查后,我发现g++生成了额外的x86指令(cvtps2pd/cvtpd2ps和unpcklps/unpcklpd),以将一些中间结果从浮点转换为双精度,然后再转换回来。此应用程序的性能下降近10% 在使用标志-Wdouble promotion(顺便说一句-Wall或-Wextra不包含该标志)编译之后,g++警告我结果正在升级 我将其简化为一个简单的测试用例,如下所示。请注意,C++代码的排序会影响生成的代码。复合语句(td1=log(r)/r;)会产生警告,而单独的版本不会产生警告(td=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 testC++ 为什么g++;(4.6和4.7)将该划分的结果提升到双倍?我能阻止它吗?,c++,templates,g++,floating-point-precision,intel-vtune,C++,Templates,G++,Floating Point Precision,Intel Vtune,我正在编写一些模板代码,用浮点和双精度对数值算法进行基准测试,以便与GPU实现进行比较 我发现我的浮点代码速度较慢,在使用英特尔Vtune放大器进行调查后,我发现g++生成了额外的x86指令(cvtps2pd/cvtpd2ps和unpcklps/unpcklpd),以将一些中间结果从浮点转换为双精度,然后再转换回来。此应用程序的性能下降近10% 在使用标志-Wdouble promotion(顺便说一句-Wall或-Wextra不包含该标志)编译之后,g++警告我结果正在升级 我将其简化为一个简
#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;
}
#包括
#包括
#包括
模板
tf()
{
Tr=静态_铸造(0.001);
//没有双重晋升警告
td=对数(r);
d/=r;
//提升到双倍
T d1=对数(r)/r;
返回d+d1;
}
int main()
{
float f1=f();
问题是
log(r)
在这个实现中,全局命名空间中唯一的<代码>日志<代码>是C库函数,<代码>双日志(double)< /C>。记住,没有指定C++库中的C库头是否将它们的定义转储到全局命名空间中,以及<代码>命名空间STD< /COD>
你想要
std::log(r)
以确保由C++库定义的额外重载可用。
看起来您是正确的。我认为日志将调用重载版本浮动日志(浮点X)。因此,为什么分离的版本不产生促销?至于为什么<代码> t d=log(r);
没有警告,可能是gcc优化了(float)log((double)x)
到logf(x)
当x是浮点时。@amckinley:这会产生转换,而不是提升。您需要-Wconversion
来捕捉它。好的,我发现缺少警告实际上是问题所在。在使用objdump检查生成的代码后,这两个语句使用额外的cvtps2pd/cvtpd2ps和unpcklps生成几乎完全相同的代码/取消CKLPD指令。所以问题是双日志(double)
正在被调用,而且在第二种情况下,g++没有警告双重升级。编辑:显然不应该这样做。我需要-Wconversion才能捕捉到这一点。@marglisse:这不太可能,除非编译器知道两个操作对所有输入都给出完全相同的结果(它们可能不会)。这不是在这里发生的(至少在我的GCC版本中是这样),用-Wconversion
编译会证明这一点。-Wconversion
也会产生警告