【问题标题】:My code is realy slow and i need optimization problem我的代码真的很慢,我需要优化问题
【发布时间】:2021-06-09 18:12:13
【问题描述】:
#include <iostream>
#include <chrono>
using namespace std;
int main()
{
    const unsigned int m = 200;
    const unsigned int n = 200;
    srand(static_cast<unsigned int>(static_cast<std::chrono::duration<double>
    >(std::chrono::high_resolution_clock::now().time_since_epoch()).count()));
    double** matrixa;
    double** matrixb;
    double** matrixc;
    matrixa = new double* [m];
    matrixb = new double* [m];
    matrixc = new double* [m];
    unsigned int max = static_cast<unsigned int>(1u << 31);
    for (unsigned int i = 0; i < m; i++)
        matrixa[i] = new double[n];
    for (unsigned int i = 0; i < m; i++)
        matrixb[i] = new double[n];
    for (unsigned int i = 0; i < m; i++)
        matrixc[i] = new double[n];
    for (unsigned int i = 0; i < m; i++)
        for (unsigned int j = 0; j < n; j++)
            matrixa[i]
            [j] = static_cast<double>(static_cast<double>(rand()) / max * 10);
    for (unsigned int i = 0; i < m; i++)
        for (unsigned int j = 0; j < n; j++)
            matrixb[i]
            [j] = static_cast<double>(static_cast<double>(rand()) / max * 10);
    auto start = std::chrono::high_resolution_clock::now();
    for (unsigned int i = 0; i < m; i++)
        for (unsigned int j = 0; j < n; j++)
            for (unsigned int k = 0; k < m; k++)
                for (unsigned int l = 0; l < m; l++)
                    matrixc[i][j] += matrixa[k][l] * matrixb[l][k];
    auto stop = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> time_diff = stop - start;
    cout << "Czas wykonania programu " << time_diff.count() << " sekund." <<
        endl;
    for (unsigned int i = 0; i < m; i++)
        delete[] matrixa[i];
    for (unsigned int i = 0; i < m; i++)
        delete[] matrixb[i];
    for (unsigned int i = 0; i < m; i++)
        delete[] matrixc[i];
    delete[] matrixa;
    delete[] matrixb;
    delete[] matrixc;
    return 0;
}

我有这段代码,我想优化它,不幸的是我完全不知道如何去做。也许有人有想法并想帮助我?我已经到了 400 个数组的程序执行 105 秒但仍然太多的地步,我想优化这段代码以更快地运行。我找到了 OpenMP 库和线程类,但我不知道如何在我的程序中使用它。

【问题讨论】:

  • 数组数组可以分散整个存储使用的内存,从而降低缓存的效率。有时效果会差几个数量级。制作一个大的一维数组并假装它是二维的。 Here's an example.
  • 启发我们,请解释这段代码应该做什么。此外,如果您添加 -O3 标志,它将立即执行(因为您的代码没有可观察到的结果)。
  • 如果 m!=n,您将遇到超出范围的问题。而不是 rand(在某些系统上,它只返回 15 位)使用 C++ &lt;random&gt; 标头。
  • 旁注:high_resolution_clock 不应用于计时。它可能别名为system_clock,并且容易受到系统时间更改的影响。时钟重新同步和夏令时更改是搞砸测量的经典之作。请改用steady_clockHere's the advice of one of the people most responsible for chono.
  • 内部 k & l 循环不依赖于 i 或 j 值(matrixc 中的初始值除外,您不对其进行初始化),因此您可以运行 k/l 循环一次,然后将该结果应用于 matrixc 的所有元素。

标签: c++ optimization


【解决方案1】:

首先,您的矩阵乘法算法比普通算法过于复杂(或者它是错误的),您可以参考 wiki 获取 typical algorithm

输入:矩阵 A 和 B

令C为合适大小的新矩阵

对于 i 从 1 到 n:

对于j从1到p:

设 sum = 0

对于从 1 到 m 的 k:

设置 sum ← sum + Aik × Bkj

设置 Cij ← sum

返回 C

您的代码中存在严重错误,您尚未初始化结果矩阵。

所以固定的代码可能是这样的:

#include <chrono>
#include <iostream>
using namespace std;
int main() {
  const unsigned int m = 200;
  const unsigned int n = 201;
  const unsigned int p = 202;

  srand(static_cast<unsigned int>(
      static_cast<std::chrono::duration<double> >(
          std::chrono::high_resolution_clock::now().time_since_epoch())
          .count()));
  double** matrixa;
  double** matrixb;
  double** matrixc;
  matrixa = new double*[m];
  matrixb = new double*[n];
  matrixc = new double*[m];
  unsigned int max = static_cast<unsigned int>(1u << 31);
  for (unsigned int i = 0; i < m; i++) matrixa[i] = new double[n];
  for (unsigned int i = 0; i < n; i++) matrixb[i] = new double[p];
  for (unsigned int i = 0; i < m; i++) {
    matrixc[i] = new double[p];
    std::fill(matrixc[i], matrixc[i] + p, 0.0);
  }
  for (unsigned int i = 0; i < m; i++)
    for (unsigned int j = 0; j < n; j++)
      matrixa[i][j] =
          static_cast<double>(static_cast<double>(rand()) / max * 10);
  for (unsigned int i = 0; i < n; i++)
    for (unsigned int j = 0; j < p; j++)
      matrixb[i][j] =
          static_cast<double>(static_cast<double>(rand()) / max * 10);
  auto start = std::chrono::high_resolution_clock::now();
  for (unsigned int i = 0; i < m; i++)
    for (unsigned int j = 0; j < p; j++)
      for (unsigned int k = 0; k < n; k++)
        matrixc[i][j] += matrixa[i][k] * matrixb[k][j];
  auto stop = std::chrono::high_resolution_clock::now();
  std::chrono::duration<double> time_diff = stop - start;
  cout << "Czas wykonania programu " << time_diff.count() << " sekund." << endl;

  for (unsigned int i = 0; i < m; i++) delete[] matrixa[i];
  for (unsigned int i = 0; i < n; i++) delete[] matrixb[i];
  for (unsigned int i = 0; i < m; i++) delete[] matrixc[i];
  delete[] matrixa;
  delete[] matrixb;
  delete[] matrixc;
  return 0;
}

现在它比有问题的要快得多。

稍微修改一下还是可以更快的:

#include <chrono>
#include <iostream>
using namespace std;
int main() {
  const unsigned int m = 200;
  const unsigned int n = 201;
  const unsigned int p = 202;

  srand(static_cast<unsigned int>(
      static_cast<std::chrono::duration<double> >(
          std::chrono::high_resolution_clock::now().time_since_epoch())
          .count()));
  double** matrixa;
  double** matrixb;
  double** matrixc;
  matrixa = new double*[m];
  matrixb = new double*[n];
  matrixc = new double*[m];
  unsigned int max = static_cast<unsigned int>(1u << 31);
  for (unsigned int i = 0; i < m; i++) matrixa[i] = new double[n];
  for (unsigned int i = 0; i < n; i++) matrixb[i] = new double[p];
  for (unsigned int i = 0; i < m; i++) {
    matrixc[i] = new double[p];
    std::fill(matrixc[i], matrixc[i] + p, 0.0);
  }
  for (unsigned int i = 0; i < m; i++)
    for (unsigned int j = 0; j < n; j++)
      matrixa[i][j] =
          static_cast<double>(static_cast<double>(rand()) / max * 10);
  for (unsigned int i = 0; i < n; i++)
    for (unsigned int j = 0; j < p; j++)
      matrixb[i][j] =
          static_cast<double>(static_cast<double>(rand()) / max * 10);
  auto start = std::chrono::high_resolution_clock::now();
  for (unsigned int i = 0; i < m; i++)
    for (unsigned int k = 0; k < n; k++)
      for (unsigned int j = 0; j < p; j++)
        matrixc[i][j] += matrixa[i][k] * matrixb[k][j];
  auto stop = std::chrono::high_resolution_clock::now();
  std::chrono::duration<double> time_diff = stop - start;
  cout << "Czas wykonania programu " << time_diff.count() << " sekund." << endl;

  for (unsigned int i = 0; i < m; i++) delete[] matrixa[i];
  for (unsigned int i = 0; i < n; i++) delete[] matrixb[i];
  for (unsigned int i = 0; i < m; i++) delete[] matrixc[i];
  delete[] matrixa;
  delete[] matrixb;
  delete[] matrixc;
  return 0;
}

这段代码对缓存更友好,explanation can be found here

代码仍然可以通过并行算法进行改进,以加速之前使用 OpenMP 的代码,只需更改一行:

添加我们需要添加构建选项-fopenmp来编译它。

#include <chrono>
#include <iostream>
using namespace std;
int main() {
  const unsigned int m = 200;
  const unsigned int n = 201;
  const unsigned int p = 202;

  srand(static_cast<unsigned int>(
      static_cast<std::chrono::duration<double> >(
          std::chrono::high_resolution_clock::now().time_since_epoch())
          .count()));
  double** matrixa;
  double** matrixb;
  double** matrixc;
  matrixa = new double*[m];
  matrixb = new double*[n];
  matrixc = new double*[m];
  unsigned int max = static_cast<unsigned int>(1u << 31);
  for (unsigned int i = 0; i < m; i++) matrixa[i] = new double[n];
  for (unsigned int i = 0; i < n; i++) matrixb[i] = new double[p];
  for (unsigned int i = 0; i < m; i++) {
    matrixc[i] = new double[p];
    std::fill(matrixc[i], matrixc[i] + p, 0.0);
  }
  for (unsigned int i = 0; i < m; i++)
    for (unsigned int j = 0; j < n; j++)
      matrixa[i][j] =
          static_cast<double>(static_cast<double>(rand()) / max * 10);
  for (unsigned int i = 0; i < n; i++)
    for (unsigned int j = 0; j < p; j++)
      matrixb[i][j] =
          static_cast<double>(static_cast<double>(rand()) / max * 10);
  auto start = std::chrono::high_resolution_clock::now();
#pragma omp parallel for
  for (unsigned int i = 0; i < m; i++)
    for (unsigned int k = 0; k < n; k++)
      for (unsigned int j = 0; j < p; j++)
        matrixc[i][j] += matrixa[i][k] * matrixb[k][j];
  auto stop = std::chrono::high_resolution_clock::now();
  std::chrono::duration<double> time_diff = stop - start;
  cout << "Czas wykonania programu " << time_diff.count() << " sekund." << endl;

  for (unsigned int i = 0; i < m; i++) delete[] matrixa[i];
  for (unsigned int i = 0; i < n; i++) delete[] matrixb[i];
  for (unsigned int i = 0; i < m; i++) delete[] matrixc[i];
  delete[] matrixa;
  delete[] matrixb;
  delete[] matrixc;
  return 0;
}

最好使用std::vector而不是动态分配的数组,工作留给你。

【讨论】:

  • 非常感谢,我还有一个问题,因为我必须使用线程函数重写相同的代码,然后使用 POSIX 线程函数并通过 fork() 函数有人可以帮助我吗?
猜你喜欢
  • 1970-01-01
  • 2021-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多