c ++矩阵乘法2D阵列比1D快

时间:2017-08-19 21:31:12

标签: c++ arrays

我有以下代码,他们使用openmp进行矩阵多播 我们假设a=m x nb=n x k

for (i = 0; i < m; i++)
    for (j = 0; j < k; j++)
        for (l = 0; l < n; l++)
            c[i * k + j] += a[i * n + l] * b[l * k + j];

for (i = 0; i < m; i++)
    for (j = 0; j < k; j++)
        for (l = 0; l < n; l++)
            c[i][j] += a[i][l] * b[l][j];

后者使用g++使用-O3时运行速度提高约1.8倍。这是为什么?据我所知,2D阵列就像我的一维码一样工作,这就是我迷茫的原因

<小时/> 注意:问题仍然存在,没有openmp以及

2 个答案:

答案 0 :(得分:1)

你的问题有一个red-herring,所以我会稍微偏离所有这一点,以及矩阵乘法中的快速和慢速。

首先,你不应该手动进行矩阵乘法。考虑使用OpenBLAS等高性能库。 OpenBLAS使用BLAS接口,这是令人讨厌的,你可以避免直接使用它提供一个好的和漂亮的C ++接口/包装如Armadillo的库,你可以链接到你选择的任何BLAS库。< / p>

话虽如此,让我们讨论为什么矩阵乘法快或慢的情况。

现在为什么第二个比第一个更快,我不知道。这可能是由于某些编译器优化并不那么明显。必须分析汇编代码。然而...

2D一般比1D快的原因是因为你没有正确地进行1D乘法。您正在省略一项非常重要的性能优化,即矢量化。

第一种方式应该比第二种方式快得多。这是因为处理器可以在1条指令中将连续元素的所有乘法放在内存中(考虑阅读this以获取更多信息)。当你确保你的乘法元素在内存中是连续的时就是这种情况。要做到这一点,只需在进行乘法之前转置其中一个矩阵。

还有其他问题使得1D乘法更快,例如cache-localityfalse sharing(在多线程程序中)。

On scientific computing of SE, this question讨论了针对矩阵乘法进行了高度优化的跳动库。当然这种可能性很小,但是你可以通过允许编译器利用向量化来找到一个可能接近这样做的例子。

答案 1 :(得分:1)

对我而言,同时

毡mult.cpp:

#define DIM 1000

#ifdef D2
double a[DIM][DIM], b[DIM][DIM], c[DIM][DIM];
#else
double a[DIM*DIM], b[DIM*DIM], c[DIM*DIM];
#endif

void Test() {
    int i, j, l;
    int k=DIM, m=DIM, n=DIM;
    for (i=0; i<DIM; i++)
    for (j=0; j<DIM; j++) {
#ifdef D2
        a[i][j] = i + j;
        b[i][j] = i - j;
        c[i][j] = 0;
#else
        a[i*DIM+j] = i + j;
        b[i*DIM+j] = i - j;
        c[i*DIM+j] = 0;
#endif
    }

#ifdef D2
    for (i = 0; i < m; i++)
    for (j = 0; j < k; j++)
        for (l = 0; l < n; l++)
        c[i][j] += a[i][l] * b[l][j];
#else
    for (i = 0; i < m; i++)
    for (j = 0; j < k; j++)
        for (l = 0; l < n; l++)
        c[i * k + j] += a[i * n + l] * b[l * k + j];
#endif
}

main() {
    Test();
}

结果:

[~/CPP] g++ -O3 -o mat-mult mat-mult.cpp
[~/CPP] ./mat-mult
2.248u 0.064s 0:02.36 97.4% 0+0k 0+0io 0pf+0w
[~/CPP] ./mat-mult
2.272u 0.020s 0:02.32 98.7% 0+0k 0+0io 0pf+0w
[~/CPP] g++ -DD2 -O3 -o mat-mult mat-mult.cpp
[~/CPP] ./mat-mult
2.244u 0.040s 0:02.33 97.8% 0+0k 0+0io 0pf+0w
[~/CPP] ./mat-mult
2.220u 0.032s 0:02.30 97.8% 0+0k 0+0io 0pf+0w

您应该提供最小可重现性示例。可重复性最小的示例是完整代码,我们可以使用鼠标和运行进行复制,以及编译标记。代码应最小,即仅包含重现问题所需的内容。如果可以从代码中删除某些内容,并且问题仍然存在,那么它就不是最小的了。

目前,我们不知道您的数组的尺寸是什么,甚至除了-O3之外还使用了哪些标记。

目前,你的问题是一个不好问题的典型例子。你抛出一些不完整的信息,希望我们可以通过心灵感应填补缺失的东西。