我有以下紧密循环组成了我的代码的串行瓶颈。理想情况下,我会并行化调用此函数的函数,但这是不可能的。
//n is about 60
for (int k = 0;k < n;k++)
{
double fone = z[k*n+i+1];
double fzer = z[k*n+i];
z[k*n+i+1]= s*fzer+c*fone;
z[k*n+i] = c*fzer-s*fone;
}
是否可以进行任何优化,例如矢量化或一些可以帮助此代码的邪恶内联?
我正在寻找三对角矩阵的特征解。 http://www.cimat.mx/~posada/OptDoglegGraph/DocLogisticDogleg/projects/adjustedrecipes/tqli.cpp.html
答案 0 :(得分:8)
简短回答:将矩阵的内存布局从行主要顺序更改为列主要顺序。
答案很长: 您似乎正在访问以行主顺序存储的矩阵的第(i)和第(i + 1)列 - 可能是一个整体不适合CPU缓存的大矩阵。基本上,在每次循环迭代时,CPU必须等待RAM(大约100个周期)。经过几次迭代后,从理论上讲,地址预测应该启动,CPU应该在循环访问之前推测性地加载数据项。这应该有助于RAM延迟。但是仍然存在代码无效地使用内存总线的问题:CPU和内存从不交换单个字节,只交换缓存行(当前处理器上为64字节)。在加载和存储的每64字节高速缓存行中,您的代码仅接触16个字节(或四分之一)。
转置矩阵并以原生主要顺序访问它将使内存总线利用率提高四倍。由于这可能是代码的瓶颈,因此您可以期望大约相同的订单加速。
是否值得,取决于算法的其余部分。由于内存布局的改变,其他部分当然可能会受到影响。
答案 1 :(得分:1)
我认为你正在旋转某些东西(或者更确切地说,很多东西,以相同的角度(s是罪,c是cos))?
向后计数总是很有趣,并且为每次迭代删除变量比较,并且应该在这里工作。使计数器成为索引也可以节省一点时间(如同其他人所说的那样,减少一些算术运算)。
for (int k = (n-1) * n + i; k >= 0; k -= n)
{
double fone=z[k+1];
double fzer=z[k];
z[k+1]=s*fzer+c*fone;
z[k] =c*fzer-s*fone;
}
这里没什么戏剧性的,但如果没别的话,它看起来更整洁。
答案 2 :(得分:1)
首先,我会在这个循环中缓存指针:
//n is about 60
double *cur_z = &z[0*n+i]
for (int k = 0;k < n;k++)
{
double fone = *(cur_z+1);
double fzer = *cur_z;
*(cur_z+1)= s*fzer+c*fone;
*cur_z = c*fzer-s*fone;
cur_z += n;
}
其次,我认为最好制作这个功能的模板化版本。因此,如果您的矩阵包含整数值(因为FPU操作较慢),您可以获得良好的性能优势。