将大方矩阵乘以它的转置比大方矩阵慢,只是乘以...如何修复?

时间:2014-10-14 06:26:11

标签: c matrix transpose

显然,转置矩阵然后乘以它比仅仅乘以两个矩阵更快。但是,我的代码现在不这样做,我不知道为什么...(正常乘法只是三嵌套for循环,它给我大约1.12secs乘以1000x1000矩阵,而这段代码给了我8花时间(这么慢而不是更快)...我迷失了,现在任何帮助都会受到赞赏!:D

A = malloc (size*size * sizeof (double));
B = malloc (size*size * sizeof (double));
C = malloc (size*size * sizeof (double));



/* initialise array elements */
for (row = 0; row < size; row++){
    for (col = 0; col < size; col++){
      A[size * row + col] = rand();
      B[size * row + col] = rand();
    }
  }

t1 = getTime();
要测量的

/ *代码在这里* /

T = malloc (size*size * sizeof(double));

for(i = 0; i < size; ++i) {
  for(j = 0; j <= i ; ++j) {
    T[size * i + j] = B[size * j + i];
  }
}

for (j = 0; j < size; ++j) {
  for (k = 0; k < size; ++k) {
    for (m = 0; m < size; ++m) {
      C[size * j + k] = A[size * j + k] * T[size * m + k];
        }
  }
}


t2 = getTime();

2 个答案:

答案 0 :(得分:1)

我看到了几个问题。

  1. 您只是设置C[size * j + k]的值而不是递增它。即使这是计算中的错误,也不应影响性能。此外,您需要在最内层循环开始之前将C[size * j + k]初始化为0.0。否则,您将增加未初始化的值。这是一个严重的问题,可能导致溢出。

  2. 乘法术语是错误的。

    请记住,您的乘法项需要代表:

          C[j, k] += A[j, m] * B[m, k], which is
          C[j, k] += A[j, m] * T[k, m]
    

    而不是

          C[size * j + k] = A[size * j + k] * T[size * m + k];
    

    你需要

          C[size * j + k] += A[size * j + m] * T[size * k + m];
                      //  ^  ^                 ^^^^^^^^^^^^^^^^
                      //  |  |                 Need to get T[k, m], not T[m, k]
                      //  |  ^^^^^^^^^^^^^^^^
                      //  |  Need to get A[j, m], not A[j, k]
                      //  ^^^^ Increment, not set.
    
  3. 我认为,除了错误之外,伤害表现的主要罪魁祸首是你使用T[size * m + k]。当你这样做时,有很多内存跳转(m是循环中变化最快的变量)来获取数据。当您使用正确的术语T[size * k + m]时,会有更少的术语,您应该会看到性能提升。

    总之,使用:

    for (j = 0; j < size; ++j) {
       for (k = 0; k < size; ++k) {
          C[size * j + k] = 0.0;
          for (m = 0; m < size; ++m) {
             C[size * j + k] += A[size * j + m] * T[size * k + m];
          }
       }
    }
    

    您可以使用以下方式获得更多性能:

    double* a = NULL;
    double* c = NULL;
    double* t = NULL;
    
    for (j = 0; j < size; ++j) {
       a = A + (size*j);
       c = C + (size*j);
       for (k = 0; k < size; ++k) {
          t = T + size*k;
          c[k] = 0.0;
          for (m = 0; m < size; ++m) {
             c[k] += a[m] * t[m];
          }
       }
    }
    

    PS 我还没有测试过代码。只是给你一些想法。

答案 1 :(得分:0)

您的转置可能比此测试中的乘法运行得慢,因为转置是数据从内存加载到缓存的位置,而矩阵乘法用完了缓存,至少1000x1000与许多现代处理器(24) MB适用于许多英特尔至强处理器的缓存。

在任何情况下,你的转置和乘法都非常低效。你的转置会破坏TLB,所以你应该使用32左右的阻塞因子(参见https://github.com/ParRes/Kernels/blob/master/SERIAL/Transpose/transpose.c代码示例)。

此外,在x86上,最好连续编写(由于缓存行锁定和阻塞存储的工作原理 - 如果你仔细使用非时态存储,这可能会改变),而在PowerPC的某些变体上,特别是Blue Gene变体,您希望连续读取(因为有序执行,非阻塞存储和直写缓存)。有关示例代码,请参阅https://github.com/jeffhammond/HPCInfo/blob/master/tuning/transpose/transpose.c

最后,我不在乎你说的话(&#34;我特别要这样做,虽然&#34;),你需要使用BLAS进行矩阵乘法。故事结局。如果您的主管或其他同事告诉您,他们是无能的,在完全重新教育之前不应该允许他们谈论代码。如果您不想亲自告诉他们,请将它们推荐给这篇文章。