Floating multiplication performing slower depending of operands in C
我正在对以前从文件中读取的矩阵执行模板计算。我使用两种不同的矩阵(非零类型和零类型)。这两种类型都共享边界值(通常为1000),而其余元素为0(零类型)和1(非零类型)。
代码将文件的矩阵存储在两个相同大小的分配矩阵中。然后,它使用自己的值和邻居的值(加上x 4和mul x 1)在一个矩阵的每个元素中执行操作,并将结果存储在第二个矩阵中。计算完成后,交换矩阵的指针,并在有限的时间内执行相同的操作。下面是核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #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个时间步(外部迭代),矩阵大小为8192行和8192列,但在更改时间步数或矩阵大小时,问题仍然存在。注意,我只测量算法的这个具体部分的时间,所以从文件中读取矩阵或其他任何东西都不会影响时间测量。
会发生的是,根据我使用的矩阵类型,我得到不同的时间,在使用零类型时获得更差的性能(其他每个矩阵都与非零类型执行相同,因为我已经尝试生成一个充满随机值的矩阵)。
我确信这是乘法运算,就好像我把它去掉,只留下加法,它们执行相同的运算。注意,对于零矩阵类型,大多数类型的求和结果将为0,因此操作将为"0.2*0"。
这种行为对我来说确实很奇怪,因为我认为浮点操作独立于操作数的值,这里的情况与此不同。我还尝试捕获并显示SIGFPE异常,以防出现问题,但没有得到任何结果。
如果有帮助的话,我使用的是IntelNehalem处理器和GCC4.4.3。
这个问题基本上已经被诊断出来了,但我会把这里发生的事情写下来。
本质上,发问者是在模拟扩散;边界上的初始量扩散到一个大网格的整体中。在每个时间步骤t,扩散前缘的值将为0.2^t(忽略拐角处的影响)。
最小归一化单精度值为2^-126;当
当网格初始填充了
请注意,将数据类型更改为
以扩散前沿的精度为代价,您可以通过启用"flush to zero"来解决速度减慢的问题,这将导致处理器在算术运算中产生零而不是非非正规结果。这是通过在fpscr或mxscr中切换一位来完成的,最好是通过C库中
另一个(hackier,不太好)"fix"是用非常小的非零值(
也许您的ZeroMatrix使用了稀疏矩阵的典型存储方案:将每个非零值存储在一个链接列表中。如果是这种情况,那么可以理解为什么它的性能比典型的基于阵列的存储方案差:因为它需要对您执行的每个操作运行一次链接列表。在这种情况下,您可以通过使用一个矩阵乘法算法来加速这个过程,该算法考虑到有一个稀疏的矩阵。如果不是这样的话,请发布最少但完整的代码,这样我们就可以玩了。
以下是有效地乘法稀疏矩阵的一种可能性:
http://www.cs.cmu.edu/~scandal/cacm/node9.html
号