18

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

该代码将文件的矩阵存储在两个相同大小的已分配矩阵中。然后它使用自己的值和邻居的值(add 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 数量或矩阵大小时问题仍然存在。请注意,我只测量算法的具体部分的时间,因此从文件中读取矩阵或其他任何内容都会影响时间测量。

发生的情况是,根据我使用的矩阵类型,我得到不同的时间,在使用零类型时获得更差的性能(每个其他矩阵都与非零类型执行相同,因为我已经尝试生成一个充满随机数的矩阵值)。

我确定这是乘法运算,就好像我将其删除并仅保留加法一样,它们的执行方式相同。请注意,对于零矩阵类型,大多数类型的求和结果将为 0,因此运算将为“0.2*0”。

这种行为对我来说肯定很奇怪,因为我认为浮点运算独立于操作数的值,这看起来不像这里的情况。我还尝试捕获并显示 SIGFPE 异常以防出现问题,但我没有得到任何结果。

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

4

2 回答 2

19

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

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

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

当网格最初填充 的常量数据时1.0,数据永远不会变得太小,因此避免了非正规停顿。

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

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

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

于 2011-03-03T21:14:14.013 回答
0

也许您的 ZeroMatrix 使用稀疏矩阵的典型存储方案:将每个非零值存储在链表中。如果是这种情况,那么为什么它的性能比典型的基于数组的存储方案更差是完全可以理解的:因为它需要为您执行的每个操作运行一次链表。在这种情况下,您可以通过使用矩阵乘法算法来加快处理速度,该算法解释了具有稀疏矩阵的情况。如果不是这种情况,请发布最少但完整的代码,以便我们可以使用它。

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

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

于 2011-03-03T11:40:43.913 回答