为什么 C++ 中的 fetestexcept 被编译为函数调用而不是内联
Why is fetestexcept in C++ compiled to a function call rather than inlined
我正在评估浮点异常在性能关键/“热”代码中的使用(清除和查询)。查看生成的二进制文件,我注意到 GCC 和 Clang 都没有将调用扩展到我期望的内联指令序列;相反,它们似乎生成对运行时库的调用。这对我的应用程序来说太贵了。
考虑以下最小示例:
#include <fenv.h>
#pragma STDC FENV_ACCESS on
inline int fetestexcept_inline(int e)
{
unsigned int mxcsr;
asm volatile ("vstmxcsr" " %0" : "=m" (*&mxcsr));
return mxcsr & e & FE_ALL_EXCEPT;
}
double f1(double a)
{
double r = a * a;
if(r == 0 || fetestexcept_inline(FE_OVERFLOW)) return -1;
else return r;
}
double f2(double a)
{
double r = a * a;
if(r == 0 || fetestexcept(FE_OVERFLOW)) return -1;
else return r;
}
以及 GCC 生成的输出:https://godbolt.org/z/jxjzYY
编译器似乎知道他可以使用 CPU-family-dependent AVX-instructions 作为目标(它使用“vmulsd”进行乘法运算)。但是,无论我尝试使用哪种优化标志,它总是会产生对 glibc 的函数调用,而不是(据我所知)应该执行相应 glibc 函数所执行的操作的程序集。
这不是投诉,我可以添加内联程序集。我只是想知道是否存在我忽略的细微差别,这可能是内联汇编版本中的错误。
需要支持long double
算术。 fetestexcept
需要合并 SSE 和 FPU 状态,因为 long double
操作只更新 FPU 状态,而不更新 MXSCR 寄存器。因此,内联的好处有所降低。
我正在评估浮点异常在性能关键/“热”代码中的使用(清除和查询)。查看生成的二进制文件,我注意到 GCC 和 Clang 都没有将调用扩展到我期望的内联指令序列;相反,它们似乎生成对运行时库的调用。这对我的应用程序来说太贵了。
考虑以下最小示例:
#include <fenv.h>
#pragma STDC FENV_ACCESS on
inline int fetestexcept_inline(int e)
{
unsigned int mxcsr;
asm volatile ("vstmxcsr" " %0" : "=m" (*&mxcsr));
return mxcsr & e & FE_ALL_EXCEPT;
}
double f1(double a)
{
double r = a * a;
if(r == 0 || fetestexcept_inline(FE_OVERFLOW)) return -1;
else return r;
}
double f2(double a)
{
double r = a * a;
if(r == 0 || fetestexcept(FE_OVERFLOW)) return -1;
else return r;
}
以及 GCC 生成的输出:https://godbolt.org/z/jxjzYY
编译器似乎知道他可以使用 CPU-family-dependent AVX-instructions 作为目标(它使用“vmulsd”进行乘法运算)。但是,无论我尝试使用哪种优化标志,它总是会产生对 glibc 的函数调用,而不是(据我所知)应该执行相应 glibc 函数所执行的操作的程序集。
这不是投诉,我可以添加内联程序集。我只是想知道是否存在我忽略的细微差别,这可能是内联汇编版本中的错误。
需要支持long double
算术。 fetestexcept
需要合并 SSE 和 FPU 状态,因为 long double
操作只更新 FPU 状态,而不更新 MXSCR 寄存器。因此,内联的好处有所降低。