背景
我需要实现Cannon算法,这是一种平行矩阵乘法算法,用于乘积的矩阵应为方针且其维度可被处理器数目的平方根整除。我编写了以下代码,它可以正常运行,但在实际运行时并不能正确地将A x B相乘得到新的矩阵C。请您帮忙分析,指导我找出错误所在。显然,这是一道作业题。
代码
void shift_left(datatype** mat, int s, int row, int n, int amount) {
datatype* temp_buffer = malloc(sizeof(datatype) * n);
for(int col = 0; col < n; col++) {
datatype temp = mat[row][(col+amount)%s];
temp_buffer[(col+amount)%s] = mat[row][col];
temp_buffer[col] = temp;
}
memcpy(mat[row], temp_buffer, n);
free(temp_buffer);
}
void shift_up(datatype** mat, int s, int col, int n, int amount) {
datatype* temp_buffer = malloc(sizeof(datatype) * n);
for(int row = 0; row < n; row++) {
datatype temp = mat[(row+amount)%s][col];
temp_buffer[(row+amount)%s] = mat[row][col];
temp_buffer[row] = temp;
}
memcpy(&mat[0][col], temp_buffer, n);
free(temp_buffer);
}
void cannon_mul(int p_sqrt,datatype** a, datatype** b, datatype** c, int n) {
/* 2D matrices and n^2 sized only!*/
int i = 0, j = 0, k = 0;
int s = p_sqrt;
for(i = 0; i < (s-1); i++) {
shift_left(a, s, i, s-1, i); // Skew matrix a
}
for (i = 0; i < (s-1); i++) {
shift_up(b, s, i, s-1, i); // Skew matrix b
}
for(k = 0; k < (s-1); k++) {
for(i = 0; i < (s-1); i++) {
for(j = 0; j < (s-1); j++) {
c[i][j] += a[i][j]*b[i][j];
shift_left(a, s, i, s-1, 1);
shift_up(b, s, i, s-1, 1);
}
}
}
}
我认为出了什么问题?
我的直觉是移位不正确,或者我错过了算法的一个重要部分。我的原始移位函数没有使用临时缓冲区,所以这次我想使用临时缓冲区,但它没有产生任何影响。如果有帮助的话,我可以展示一些样本输出,但结果与期望的结果 完全不相近。好消息是它运行得很快 :)
结果
1.48 0.14 9.47 8.99 8.06 0.06 6.68 1.04 4.44 7.50
7.26 8.87 2.21 6.27 2.12 7.91 0.65 5.24 0.45 4.94
0.47 4.13 1.87 2.25 6.83 1.52 6.41 9.14 9.22 8.91
7.34 2.70 6.78 2.78 3.51 4.95 5.27 0.85 9.51 6.82
0.28 6.73 0.70 8.88 7.14 9.09 2.36 5.38 6.43 9.00
7.13 6.71 6.92 9.81 5.13 9.35 7.50 5.16 4.68 3.62
1.30 6.26 4.55 4.27 0.51 2.23 3.19 8.75 6.57 9.07
7.49 6.41 1.04 7.78 7.16 2.78 2.25 6.23 9.42 0.32
3.21 3.60 2.04 2.93 4.29 3.88 2.78 8.01 4.57 6.47
7.52 3.77 0.63 5.97 7.32 4.90 9.63 4.90 8.46 1.90
将上述矩阵自乘,用我的代码计算结果如下:
2.20 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
50.81 0.00 0.00 0.00 0.00 87.51 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
这个顺序程序产生了以下结果:
163.41 212.17 144.32 227.10 251.03 205.60 245.63 277.33 368.99 334.11
257.85 230.82 203.60 314.08 246.02 240.12 228.37 197.90 264.38 228.24
234.13 272.10 110.75 294.84 263.16 242.07 209.54 316.13 339.23 260.51
185.33 215.59 192.26 283.31 270.80 208.38 265.08 291.49 312.24 319.73
313.23 301.95 182.04 348.11 283.20 337.49 266.54 284.57 355.28 281.07
293.25 323.29 281.35 393.92 325.24 313.62 313.48 342.95 418.37 401.91
255.88 238.25 122.17 254.52 243.58 204.49 217.69 273.03 314.89 214.45
219.26 239.07 200.18 309.98 262.21 242.68 190.02 245.85 297.96 308.56
209.03 213.11 126.24 266.48 233.88 199.33 193.28 228.92 277.50 202.27
210.31 264.67 227.59 337.79 261.40 250.35 225.77 295.00 331.92 352.17
重要提示:我仅展示我的程序的相关部分,如果您认为需要展示更多,请告诉我,我会提供更多代码。最后,为什么“作业”标签消失了?
编辑
有人指出缓冲区太小,并且缺少“sizeof”的愚蠢错误已经被更正。我尝试过,结果相同,所以显然问题与此无关。希望在两天内,我可以开启悬赏,吸引一些人至少给我一个线索,指出问题所在。这是一个我似乎无法调试的错误,而我对该算法的理解必须承认几乎为零。我依赖于几乎没有增加我的理解的网络资源。编辑2
尝试使用calloc进行零分配缓冲区,但它并不改变结果。如此奇怪,但感谢您的建议;我忘记了内存不会自动分配零。编辑3
我尝试了这个:void shift_left(datatype** mat, int s, int row, int n, int amount) {
datatype* temp_buffer = calloc(n, sizeof(datatype) * n);
for(int col = 0; col < n; col++) {
/* temp_buffer[(col+amount)%s] = mat[row][col];
temp_buffer[col] = mat[row][(col+amount)%s]; */
temp_buffer[(col+amount)%s] = 0;
temp_buffer[col] = 0;
}
memcpy(mat[row], temp_buffer, sizeof(datatype) * n);
//free(temp_buffer);
}
void shift_up(datatype** mat, int s, int col, int n, int amount) {
datatype* temp_buffer = calloc(n, sizeof(datatype) * n);
for(int row = 0; row < n; row++) {
/* temp_buffer[(row+amount)%s] = mat[row][col];
temp_buffer[row] = mat[(row+amount)%s][col]; */
temp_buffer[(row+amount)%s] = 0;
temp_buffer[row] = 0;
}
memcpy(&mat[0][col], temp_buffer, sizeof(datatype) * n);
free(temp_buffer);
}
令人惊讶的是,结果相同。虽然我已经注释了代码并将其替换为零,应该打印所有零。我的猜测是memcpy没有起作用。
编辑 4
我确认了memcpy是罪魁祸首。但是我不知道为什么,我被难住了,如果数据类型只是double的别名,那么教授因某种奇怪的原因写下了这句话,因为它并没有使代码更易读。
但是如果我自己解决了问题,会很高兴向大家展示解决方案。