我正在为我的大学进行一项与医疗用途图像重建算法相关的研究。
我被困在长达 3 周的时间里,我需要提高以下代码的性能:
for (lor=lor0[mypid]; lor <= lor1[mypid]; lor++)
{
LOR_X = P.symmLOR[lor].x;
LOR_Y = P.symmLOR[lor].y;
LOR_XY = P.symmLOR[lor].xy;
lor_z = P.symmLOR[lor].z;
LOR_Z_X = P.symmLOR[lor_z].x;
LOR_Z_Y = P.symmLOR[lor_z].y;
LOR_Z_XY = P.symmLOR[lor_z].xy;
s0 = P.a2r[lor];
s1 = P.a2r[lor+1];
for (s=s0; s < s1; s++)
{
pixel = P.a2b[s];
v = P.a2p[s];
b[lor] += v * x[pixel];
p = P.symm_Xpixel[pixel];
b[LOR_X] += v * x[p];
p = P.symm_Ypixel[pixel];
b[LOR_Y] += v * x[p];
p = P.symm_XYpixel[pixel];
b[LOR_XY] += v * x[p];
// do Z symmetry.
pixel_z = P.symm_Zpixel[pixel];
b[lor_z] += v * x[pixel_z];
p = P.symm_Xpixel[pixel_z];
b[LOR_Z_X] += v * x[p];
p = P.symm_Ypixel[pixel_z];
b[LOR_Z_Y] += v * x[p];
p = P.symm_XYpixel[pixel_z];
b[LOR_Z_XY] += v * x[p];
}
}
对于任何想知道的人,该代码实现了 MLEM 转发功能,所有变量都是 FLOAT。
经过几次测试,我注意到这部分代码出现了很大的延迟。(你知道,90 - 10 规则)。
后来,我使用 Papi (http://cl.cs.utk.edu/papi/) 来测量 L1D 缓存未命中。正如我所想,Papi 确认性能下降是由于更多的未命中,特别是对于随机访问 b 向量(巨大的大小)。
阅读互联网上的信息到目前为止,我只知道两种提高性能的选择:提高数据局部性或减少数据污染。
为了进行第一个改进,我将尝试将代码更改为可感知缓存,就像 Ulrich Drepper 在每个程序员都应该了解的内存(www.akkadia.org/drepper/cpumemory.pdf)中提出的那样。 1 矩阵乘法。
我相信阻止 SpMV(稀疏矩阵向量乘法)会提高性能。
另一方面,每次程序尝试访问 b 向量时,我们都会遇到所谓的缓存污染。
有没有办法在不使用缓存的情况下使用 SIMD 指令从 b 向量中加载一个值?
此外,可以使用 void _mm_stream_ps(float * p , __m128 a) 之类的函数在向量 b 上存储一个浮点值而不污染缓存?
我不能使用 _mm_stream_ps 因为总是存储 4 个浮点数,但对 b 向量的访问显然是随机的。
我希望清楚我的困境。
更多信息:v 是具有 CRS 格式的稀疏矩阵存储的列值。我意识到如果我尝试将 CRS 格式更改为其他格式,则可以进行其他优化,但是,就像我之前所说的,我已经做了几个月的测试,我知道性能下降与向量 b 上的随机访问有关。从 400.000.000 L1D Misses 当我不存储在向量 b 中时,我可以转到 100~ Misses。
谢谢。