我们找到了各种技巧来替换 std::sqrt
( 时序平方根)和一些 std::exp
( 使用更快的指数近似),但我找不到可以替换的东西 std::log
它是我程序中循环的一部分,它被多次调用,虽然 exp 和 sqrt 被优化,但英特尔 VTune 现在建议我优化 std::log
,之后似乎只有我的设计选择会受到限制。
For now I use a 3rd order taylor approximation of ln(1+x)
with x
between -0.5
and +0.5
(90% of the case for max error 4%)并回退到 std::log
否则。这给了我 15% 的加速。
原文由 qwark 发布,翻译遵循 CC BY-SA 4.0 许可协议
在着手设计和部署性能超越函数的定制实现之前,强烈建议在算法级别以及通过工具链进行优化。不幸的是,我们没有任何关于这里要优化的代码的信息,也没有关于工具链的信息。
在算法层面,检查所有对超越函数的调用是否真的有必要。也许有一个数学变换需要更少的函数调用,或者将超越函数转换为代数运算。是否有任何超越函数调用可能是多余的,例如因为计算不必要地进出对数空间?如果精度要求适中,整个计算能否以单精度执行,始终使用
float
而不是double
?在大多数硬件平台上,避免double
计算可以显着提升性能。编译器倾向于提供各种影响数字密集型代码性能的开关。除了将一般优化级别提高到
-O3
,通常还有一种方法可以关闭非正规支持,即打开清零或 FTZ 模式。这在各种硬件平台上具有性能优势。此外,通常有一个“快速数学”标志,其使用会导致准确性略有降低,并消除了处理特殊情况(如 NaN 和无穷大)以及处理errno
的开销。一些编译器还支持代码的自动矢量化并附带 SIMD 数学库,例如英特尔编译器。A custom implementation of a logarithm function typically involves separating a binary floating-point argument
x
into an exponente
and am
, such thatx = m * 2
e
,因此log(x) = log(2) * e + log(m)
。选择m
使其接近于一,因为这提供了有效的近似,例如log(m) = log(1+f) = log1p(f)
通过 极小多项式近似。C++ 提供了
frexp()
函数将浮点操作数分离为尾数和指数,但在实践中,通常使用更快的机器特定方法,通过将浮点数据重新解释为相同大小的整数。下面的单精度对数代码logf()
演示了这两种变体。 Functions__int_as_float()
and__float_as_int()
provide for the reinterpretation of anint32_t
into an IEEE-754binary32
floating-point number and vice-versa.此代码严重依赖于大多数当前处理器、CPU 或 GPU 的硬件中直接支持的融合乘加操作 FMA。在fmaf()
映射到软件仿真的平台上,此代码的速度会慢得令人无法接受。如代码注释中所述,上面的实现提供了忠实四舍五入的单精度结果,并且它处理与 IEEE-754 浮点标准一致的例外情况。通过消除特殊情况支持、消除对非规范参数的支持以及降低准确性,可以进一步提高性能。这导致以下示例性变体: