我正在进行一项任务,我将转置矩阵以减少矩阵乘法运算的缓存未命中。根据我对几个同学的理解,我应该得到8倍的提升。但是,我只得到2倍......我可能做错了什么?
void transpose(int size, matrix m) {
int i, j;
for (i = 0; i < size; i++)
for (j = 0; j < size; j++)
std::swap(m.element[i][j], m.element[j][i]);
}
void mm(matrix a, matrix b, matrix result) {
int i, j, k;
int size = a.size;
long long before, after;
before = wall_clock_time();
// Do the multiplication
transpose(size, b); // transpose the matrix to reduce cache miss
for (i = 0; i < size; i++)
for (j = 0; j < size; j++) {
int tmp = 0; // save memory writes
for(k = 0; k < size; k++)
tmp += a.element[i][k] * b.element[j][k];
result.element[i][j] = tmp;
}
after = wall_clock_time();
fprintf(stderr, "Matrix multiplication took %1.2f seconds\n", ((float)(after - before))/1000000000);
}
到目前为止我做得对吗?
仅供参考:我需要做的下一个优化是使用SIMD / Intel SSE3
答案 0 :(得分:11)
到目前为止我做得对吗?
没有。你的转置有问题。在开始担心性能之前,您应该已经看过这个问题。当您进行任何类型的黑客攻击以进行优化时,总是使用天真但次优的实现作为测试。如果没有得到正确的答案,那么实现100倍加速的优化就毫无价值。
另一个有用的优化是通过引用传递。你正在传递副本。事实上,您的matrix result
可能永远不会离开,因为您正在传递副本。再一次,你应该进行测试。
另一个有助于加速的优化是缓存一些指针。这仍然很慢:
for(k = 0; k < size; k++)
tmp += a.element[i][k] * b.element[j][k];
result.element[i][j] = tmp;
优化器可能会看到指针问题,但可能没有。至少不会,如果您不使用非标准__restrict__
关键字告诉编译器您的矩阵不重叠。缓存指针,因此您不必执行a.element[i]
,b.element[j]
和result.element[i]
。它仍然可能有助于告诉编译器这些数组与__restrict__
关键字不重叠。
<强>附录强>
查看代码后,需要帮助。首先是一个小评论。你不是在写C ++。你的代码是C,带有一丝C ++的暗示。您使用的是struct
而不是class
,malloc
而不是new
,typedef struct
,而不仅仅是struct
,C标头而不是C ++标头
由于您对struct matrix
的实施,我对复制构造函数导致的缓慢的评论不正确。它不正确甚至更糟!使用隐式定义的复制构造函数与包含裸指针的类或结构一起使用火。如果有人拨打m(a, a, a_squared)
来获取矩阵a
的平方,则会非常严重。如果有些人希望m(a, a, a)
进行a
2 的就地计算,你的工作会更加严重。
在数学上,您的代码仅涵盖矩阵乘法问题的一小部分。如果有人想将100x1000矩阵乘以1000x200矩阵怎么办?这是完全有效的,但是您的代码无法处理它,因为您的代码仅适用于方形矩阵。另一方面,你的代码会让某人将100x100矩阵乘以200x200矩阵,这没有多大意义。
从结构上讲,由于您使用了不规则的数组,您的代码几乎可以保证100%的速度。 malloc
可以在内存中喷洒矩阵的行。如果矩阵在内部表示为连续数组但是被访问就好像它是NxM矩阵,那么你将获得更好的性能。 C ++为此提供了一些很好的机制。
答案 1 :(得分:3)
如果你的作业暗示你必须转置,那么,当然,你应该纠正你的转置程序。就目前而言,它会进行两次转置,完全没有转置。 j =循环不应该读
j=0; j<size; j++
但
j=0; j<i; j++
无需转置以避免以“错误”顺序处理其中一个因子矩阵的元素。只需交换j循环和k循环。暂且不谈任何(其他)性能调整,基本的循环结构应该是:
for (int i=0; i<size; i++)
{
for (int k=0; k<size; k++)
{
double tmp = a[i][k];
for (int j=0; j<size; j++)
{
result[i][j] += tmp * b[k][j];
}
}
}