【问题标题】:How to write fast code for matrix multiplication in C++?如何在 C++ 中编写矩阵乘法的快速代码?
【发布时间】:2017-02-13 01:13:39
【问题描述】:

我为矩阵乘法编写了一些 C++ 代码。我使用vector<double> 来保存矩阵条目,并使用一系列3 个嵌套的for 循环来逐项计算乘法。事实证明,这非常慢(对于 900*500 和 500*500 的矩阵乘法,在我的 macbook air 上大约需要 10 秒)。是什么原因?是我使用了错误的矩阵表示还是代码中存在很大缺陷?

    for (int c_b=0;c_b<B.n_c;c_b++)
    {
        vector<double> vtmp(A.n_r);
        for (int r_a=0;r_a<A.n_r;r_a++)
        {
            sum=0;
            for (int i=0;i < A.n_c;i++)
            {
                sum=sum+A.mat[r_a+i*A.n_r]*B.mat[i+c_b*B.n_r];
            }
            vtmp[r_a]=sum;
        }
        Cvv[c_b]=vtmp;
    }

更新:此问题已通过在 Lapack 中使用子例程得到解决。

【问题讨论】:

  • 与其创建vtmp 不如只做Cvv[c_b][r_a] = sum;
  • 我建议使用许多 BLAS 实现之一。
  • 使用分析器查看您的代码在哪里花费了大部分时间与已知的快速实现。
  • 您可以从这里查看答案stackoverflow.com/questions/4455645/…
  • 我在您的代码中没有看到 vector&lt;vector&lt;double&gt;&gt;,也没有看到 ABCvv 的声明,也没有看到它们类型的详细信息。我倾向于说您的矩阵表示效率低下,但是当我实际上没有要评估的表示时,那将是推测性的。

标签: c++ matrix


【解决方案1】:

这里有一些关于性能改进的建议。

将向量移出循环
向量的创建需要时间。将声明移到任何 for 循环之前:

vector<double> vtmp(A.n_r);
for (int c_b=0;c_b<B.n_c;c_b++)
{
    for (int r_a=0;r_a<A.n_r;r_a++)
    {
        //...
    }
}

剖析分配计算。
如果没有任何分析或基准测试,赋值语句看起来占用的时间最多。将其分解为单独的步骤以帮助编译器,这样您就可以查看是否可以更优化地执行计算。
原文:

sum=sum+A.mat[r_a+i*A.n_r]*B.mat[i+c_b*B.n_r];

剖析[1]:

const int A_Index = r_a + i * A.n_r;
const int B_Index = i + c_b*B.n_r;
sum = sum + A.mat[A_Index] * B.mat[B_Index];

Dissected[2](使用更多变量):

const int temp1 = i * A.n_r;
const int temp2 = c_b * B.n_r;
const int A_Index = r_a + temp1;
const int B_Index = i + temp2;
sum = sum + A.mat[A_Index] * B.mat[B_Index];

以上内容可能有助于编译器选择最佳处理器指令。

使用局部变量
理想情况下,您希望处理器在重新加载之前在数据缓存中从矩阵中获取尽可能多的顺序位置。像这样的:

int ATemp1 = A[w];
int ATemp2 = A[x];
int ATemp3 = A[y];
int ATemp4 = A[z];

int BTemp1 = B[e];
int BTemp2 = B[f];
int BTemp3 = B[g];
int BTemp4 = B[h];

sum = sum + ATemp1 * BTemp1;
sum = sum + ATemp2 * BTemp2;
sum = sum + ATemp3 * BTemp3;
sum = sum + ATemp4 * BTemp4;

【讨论】:

  • 我发现有两个问题阻碍了性能。第一个是向量索引的计算,第二个是这两个索引可能落在处理器缓存之外。处理器在计算总和时可能会浪费时间重新加载缓存的数据。
  • 您的评论“处理器可能在计算总和时浪费时间重新加载缓存的数据”对我来说很有趣。您能否就如何避免这种情况提出一些建议?
  • 从索引计算中找出如何构造循环以使数组访问更接近。您可以使用一些加载顺序值的临时变量(例如ATemp1ATemp2),然后根据临时值计算总和。
  • 你的意思是把矩阵分成块并逐块剖析计算吗?
猜你喜欢
  • 1970-01-01
  • 2017-01-25
  • 1970-01-01
  • 2015-04-08
  • 2011-11-30
  • 2013-06-13
  • 2012-08-30
  • 2016-06-25
  • 2011-05-17
相关资源
最近更新 更多