Nit*_*ros 17 c floating-point performance x86 stencils
我正在对我之前从文件中读取的矩阵执行模板计算.我使用两种不同的矩阵(NonZero类型和Zero类型).两种类型共享边界的值(通常为1000),而其余元素对于零类型为0,对于NonZero类型为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;
}
Run Code Online (Sandbox Code Playgroud)
我暴露的情况使用固定量的500个timeSteps(外部迭代)和8192行和8192列的矩阵大小,但是在更改timeSteps或矩阵大小的数量时问题仍然存在.请注意,我只测量算法的这个具体部分的时间,因此从文件读取矩阵或其他任何因素都会影响时间度量.
它发生了什么,取决于我使用哪种类型的矩阵,我得到不同的时间,使用Zero类型时获得更差的性能(每隔一个矩阵执行与NonZero类型相同,因为我已经尝试生成一个充满随机的矩阵值).
我确定它是乘法运算,就像我删除它并只留下添加,它们执行相同的操作.注意,对于零矩阵类型,大多数类型的和的结果将为0,因此操作将为"0.2*0".
这种行为对我来说当然很奇怪,因为我认为浮点运算独立于操作数的值,这看起来不像这里的情况.我也尝试捕获并显示SIGFPE异常以防出现问题,但我没有得到任何结果.
如果有帮助,我使用的是Intel Nehalem处理器和gcc 4.4.3.
Ste*_*non 19
这个问题已经被大部分诊断出来了,但我会写出这里发生的事情.
从本质上讲,提问者是模拟扩散; 边界上的初始量扩散到整个大网格中.在每个时间步t,扩散前沿的值将为0.2 ^ t(忽略角落处的效应).
最小的归一化单精度值为2 ^ -126; 当cur_time = 55
扩散前沿的值为0.2 ^ 55时,它比2 ^ -127小一点.从此时开始,网格中的一些单元格将包含非正规值.在提问者的Nehalem上,非正规数据的操作比标准化浮点数据上的相同操作慢约100倍,这解释了减速.
当网格最初填充有恒定数据时1.0
,数据永远不会变得太小,因此避免了非正规停顿.
请注意,更改数据类型double
会延迟,但不会缓解问题.如果使用双精度计算,则在第441次迭代中将首先出现非正规值(现在小于2 ^ -1022).
以扩散前沿的精度为代价,您可以通过启用"Flush to Zero"来解决减速问题,这会导致处理器在算术运算中产生零而不是非正规结果.这是通过在FPSCR或MXSCR中切换一个位来完成的,最好是通过<fenv.h>
C库中标题中定义的函数.
另一个(hackier,less good)"fix"将最初用非常小的非零值(0x1.0p-126f
最小的正常数)填充矩阵.这也可以防止在计算中出现非正规数.