取决于C中操作数的浮动乘法执行速度较慢

我正在对之前从文件读取的矩阵执行模板计算。 我使用两种不同的矩阵(非零型和零型)。 两种类型共享边界值(通常为1000),而其余元素对于零类型为0,非零类型为1。

代码将文件的矩阵存储在两个相同大小的已分配矩阵中。 然后使用它自己的值和邻居值(添加x 4和mul x 1)在一个矩阵的每个元素中执行一个操作,并将结果存储在第二个矩阵中。 一旦计算完成,矩阵的指针就会被交换,并且执行相同的操作的次数是有限的。 在这里你有核心代码:

#define GET(I,J) rMat[(I)*cols + (J)]
#define PUT(I,J) wMat[(I)*cols + (J)]

for (cur_time=0; cur_time<timeSteps; cur_time++) {
    for (i=1; i<rows-1; i++) {
        for (j=1; j<cols-1; j++) {
            PUT(i,j) = 0.2f*(GET(i-1,j) + GET(i,j-1) + GET(i,j) + GET(i,j+1) + GET(i+1,j));
        }
    }
    // Change pointers for next iteration
    auxP = wMat;
    wMat = rMat;
    rMat = auxP;
}

我公开的案例使用固定数量的500 timeSteps(外迭代)和8192行和8192列的矩阵大小,但问题在改变timeSteps或矩阵大小时仍然存在。 请注意,我只测量算法的这个具体部分的时间,所以从文件中读取矩阵也不会影响时间度量。

发生的情况是,根据我使用的矩阵类型,我得到不同的时间,使用Zero类型时性能会差得多(每个其他矩阵的性能与NonZero类型相同,因为我已经试图生成一个满足随机数的矩阵值)。

我确信这是乘法操作,就好像我删除它并只留下添加,他们执行相同的操作。 请注意,对于零矩阵类型,总和结果的大部分类型将为0,因此操作将为“0.2 * 0”。

这种行为对我来说确实很奇怪,因为我认为浮点操作与操作数的值无关,在这里看起来并不像这种情况。 如果出现问题,我也尝试捕获并显示SIGFPE异常,但我没有获得任何结果。

如果有帮助,我使用的是Intel Nehalem处理器和gcc 4.4.3。


这个问题已经大部分被诊断出来了,但是我会写出这里发生的事情。

本质上,提问者是建模扩散; 边界上的初始量扩散到整个大网格中。 在每个时间步t,扩散前沿的值将是0.2 ^ t(忽略拐角处的影响)。

最小的标准化单精度值是2 ^ -126; 当cur_time = 55 ,扩散前沿的值为0.2 ^ 55,略小于2 ^ -127。 从这个时间步向前,网格中的一些单元格将包含非正常值。 在提问者的Nehalem上,对非规范化数据的操作比规范化浮点数据上的操作慢大约100倍,这解释了放慢速度。

当网格最初填充1.0常量数据时,数据永远不会太小,因此可以避免反常规失速。

请注意,将数据类型更改为double会延迟,但不会缓解问题。 如果计算使用双精度,则非正规值(现在小于2 ^ -1022)将首先在第441次迭代中出现。

以扩散前沿精度为代价,您可以通过启用“Flush to Zero”来解决减速问题,这会导致处理器在算术运算中产生零而非反常结果。 这可以通过在FPSCR或MXSCR中切换一点来完成,最好是通过C库中<fenv.h>头文件定义的函数。

另一个(hackier,不太好)的“修复”将是最初用非常小的非零值( 0x1.0p-126f ,最小的正常数)填充矩阵。 这也可以防止计算中出现非正常现象。


也许你的ZeroMatrix使用Sparse Matrices的典型存储方案:将每个非零值存储在链表中。 如果是这样的话,为什么它的性能比典型的基于阵列的存储方案更糟糕,这是完全可以理解的:因为每执行一次操作,它都需要运行一次链表。 在这种情况下,你可以通过使用一个占用稀疏矩阵的矩阵乘法算法来加速进程。 如果情况并非如此,请发布最少但完整的代码,以便我们可以使用它。

这是有效乘以稀疏矩阵的可能性之一:

http://www.cs.cmu.edu/~scandal/cacm/node9.html

链接地址: http://www.djcxy.com/p/85527.html

上一篇: Floating multiplication performing slower depending of operands in C

下一篇: Is multiplication of two numbers a constant time algorithm?