我迫不及待地想要为速度优化一大块C代码而且我正在寻找一种算法---最好是一个C“片段”--- 转换矩形源矩阵 u[r][c]
任意大小(r
行数,c
列数)到目标矩阵{{1 } {(v[s][d]
行数,s = c
列数)“缓存友好” i。即数据位置尊重的方式。 d = r
的典型大小约为5000 ... 15000行,50到500列,很明显,元素的行方式访问非常缓存效率低。
在网络上有很多关于这个主题的讨论(在thread附近),但据我所知,所有这些都讨论了方形矩阵,u
等空间情况,或者定义了维度数组,e。 G。 u[r][r]
,而不是上面提到的 Numerical Recipes 上下文提到的“数组数组”(等长)(背景见here)。
我非常感谢任何有助于让我“重新发明轮子”的暗示。
马丁
答案 0 :(得分:1)
我认为数组数组通常比线性数组更难转置。但是如果你在每个数组中都有50列,那听起来很糟糕:隐藏指针解除引用的开销可能还不够。
我认为缓存友好实现的整体策略是相同的:在拼贴中处理矩阵,根据实验选择效果最佳的拼块大小。
template<int BLOCK>
void TransposeBlocked(Matrix &dst, const Matrix &src) {
int r = dst.r, c = dst.c;
assert(r == src.c && c == src.r);
for (int i = 0; i < r; i += BLOCK)
for (int j = 0; j < c; j += BLOCK) {
if (i + BLOCK <= r && j + BLOCK <= c)
ProcessFullBlock<BLOCK>(dst.data, src.data, i, j);
else
ProcessPartialBlock(dst.data, src.data, r, c, i, j, BLOCK);
}
}
当 r = 10000, c = 500(float
类型)时,我尝试优化最佳情况。在我的本地机器上,128 x 128瓷砖可以提供2.5倍的加速。此外,我尝试使用SSE来加速换位,但不显着改变时序。我认为这是因为问题是内存限制的。
以下是Core2 E4700 2.6GHz上各种实现的完整时序(每次启动100次):
Trivial: 6.111 sec
Blocked(4): 8.370 sec
Blocked(16): 3.934 sec
Blocked(64): 2.604 sec
Blocked(128): 2.441 sec
Blocked(256): 2.266 sec
BlockedSSE(16): 4.158 sec
BlockedSSE(64): 2.604 sec
BlockedSSE(128): 2.245 sec
BlockedSSE(256): 2.036 sec
以下是使用的full code。
答案 1 :(得分:0)
所以,我猜你有一个浮点数/双打数组。此设置对于缓存性能已经非常糟糕。原因在于,使用一维数组,编译器可以输出导致预取操作的代码,并且(在非常新的编译器的情况下)生成SIMD /矢量化代码。使用指针数组,每个步骤都有一个deference操作,使预取更加困难。更不用说内存对齐没有任何保证。
如果这是一项作业而您别无选择,只能从头开始编写代码,我建议您查看CBLAS如何做到这一点(请注意,您仍然需要将数组“展平” “)。否则,使用高度优化的BLAS实现,你可以更好地 OpenBLAS。它经过了近十年的优化,可以为目标处理器生成最快的代码(调整缓存大小和向量指令集等)。
tl; dr是使用数组数组将导致可怕的性能,无论如何。通过使用#define访问数组元素来展平数组并使代码更易于阅读。