[英]Can floating point multiplication by zero be optimised at runtime?
我正在编写一个算法来查找nxn矩阵的逆。 让我们来看一个3x3矩阵的具体情况。
手动反转矩阵时,通常会查找包含一个或多个零的行/列,以便更快地执行行列式计算,因为它会消除您需要计算的项。
在C / C ++中遵循此逻辑,如果您标识具有一个或多个零的行/列,则最终将得到以下代码:
float term1 = currentElement * DetOf2x2(...);
// ^
// This is equal to 0.
//
// float term2 = ... and so on.
由于编译器无法知道currentElement
在编译时将为零,因此无法将其优化为float term = 0;
因此浮点乘法将在运行时执行。
我的问题是,这些零值是否会使浮点乘法更快,或者无论currentElement
的值如何,乘法都会占用相同的时间量? 如果无法在运行时优化乘法,那么我可以删除搜索包含零的行/列的逻辑。
除非计算是trival(例如所有常量),否则不允许编译器优化它。
原因是,DetOf2x2可能返回NAN浮点值。 将NAN与零相乘不会返回零,而是再次返回NAN。
您可以在此处使用此小测试自行尝试:
int main (int argc, char **args)
{
// generate a NAN
float a = sqrt (-1);
// Multiply NAN with zero..
float b = 0*a;
// this should *not* output zero
printf ("%f\n", b);
}
如果要优化代码,则必须自行测试零。 编译器不会为您执行此操作。
float term1 = currentElement * DetOf2x2(...);
即使currentElement为0,编译器也会调用DetOf2x2(...)
:这肯定比最终的乘法要DetOf2x2(...)
得多,无论是否为0。 原因有很多:
DetOf2x2(...)
可能有副作用(如输出到日志文件),即使currentElement
为0
也需要发生这种副作用, DetOf2x2(...)
可以返回值,无论如何都应传播到term1
的非数字/ NaN标记(如Nils Pipenbrinck首先提到的那样) 鉴于DetOf2x2(...)
几乎肯定会处理只能在运行时确定的值,后者的可能性不能在编译时排除。
如果你想避免调用Detof2x2(...)
,请尝试:
float term1 = (currentElement != 0) ? currentElement * DetOf2x2(...) : 0;
现代的CPU会比实际的分支处理乘法由零速度非常快,快于一般的乘法,并更迅速。 甚至不打算尝试优化它,除非零将通过至少几十个指令传播。
在运行时执行的优化称为JIT(即时)优化。 在翻译(编译)时执行的优化称为AOT(提前)优化。 你指的是JIT优化。 编译器可能会在您的机器代码中引入JIT优化,但它实现的优化要比常见的AOT优化要复杂得多。 优化通常基于重要性来实现,并且这种“优化”可能被视为负面地影响其他算法。 C实现不需要执行任何这些优化。
您可以手动提供优化,这将是“搜索包含零的行/列的逻辑”,或者类似这样的内容: float term1 = currentElement != 0 ? currentElement * DetOf2x2(...) : 0;
float term1 = currentElement != 0 ? currentElement * DetOf2x2(...) : 0;
当编译器可以猜测“currentElement”的值时,以下构造在编译时有效。
float term1 = currentElement? currentElement * DetOf2x2(...):0;
如果在编译时无法猜到,它将在运行时进行检查,性能取决于处理器体系结构:分支之间的权衡(包括分支延迟和重建指令管道的延迟可达10或20个周期)和平坦代码(一些处理器每个周期运行3个指令)和硬件分支预测(当硬件支持分支预测时)。
由于乘法吞吐量在x86_64处理器上接近1个周期,因此根据操作数值(如0.0,1.0,2.0或12345678.99)不存在性能差异。 如果存在这样的差异,那将被视为加密式软件中的隐蔽通道。
GCC允许在编译时检查函数参数
内联浮动myFn(float currentElement,myMatrix M)
{
#if __builtin_constant_p(currentElement)&& currentElement == 0.0
返回0.0;
#其他
return currentElement * det(M);
#万一
}
您需要在编译器中启用内联和过程间优化。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.