有人可以帮我并行化这个 C++ 代码吗?

Posted

技术标签:

【中文标题】有人可以帮我并行化这个 C++ 代码吗?【英文标题】:Can someone help me parallelize this C++ Code? 【发布时间】:2019-02-04 22:48:06 【问题描述】:

所以我的任务是找到一种方法来并行化这个简单的 C++ 问题。问题是......我真的......真的在并行编程概念上苦苦挣扎,不知道该怎么做。要采取的步骤如下:

1) 取一个正整数 N 作为参数 2) 创建一个大小为 N 的整数数组 3) 填充范围 [1,1000] 中的整数4) 并行查找最大整数和数组的总和 5) 打印最大整数和数组的和。

在我进入第 4 步之前,这很容易。我不知道如何并行化这段代码。我听说过线程和多线程之类的概念,但我几乎为零知道如何在 C++ 中实现它们,并且可以真正使用一些帮助+详细解释。我还没有找到一个对我有意义的并行化 C++ 程序的具体示例。

#include  <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

int main()

cout << "Enter the Size of the Array (N):  \n ";
int N;
cin >> N;

int array[N];
int largest_number = 0;
int sum = 0;

srand(time(0));
cout << "Populating Array...\n";

//  Filling up the Array  with values
    for(int i =0; i < N; i++)
    

    array[i] =  (rand() % 1000) + 1;

    


// Finding the largest value and calculating sum of the array
    for( int j  = 0; j < N; j++)
    

    sum += array[j];

    if( array[j] > largest_number)
    largest_number = array[j];

    

cout << "Output: \n";
cout << "Maximum:  " <<  largest_number << ";" <<  "Sum: " <<  sum;
cout << "\n";

 

就目前而言,代码运行良好。我只需要它来展示并行编程的实例。

就预期而言,我可以在代码上实现一个计时器,以检查此代码的并行化速度有多快,但我想确保它首先工作。谢谢

【问题讨论】:

也许可以看看英特尔线程构建模块,尤其是 parallel_reduce 操作。 我不认为并行查找最大整数和数组之和实际上是要求您进行多线程处理。我相信要求是要求您在一次通过中找到数组的总和以及数组的最大值,从而使任务并行。 还要注意int array[N];是非法的,如果你使用gcc,使用-pedantic-errors会变成编译器错误。 【参考方案1】:

对于许多简单的并行问题,OpenMP 将是您的最佳选择。

它已经包含在所有 C++ 编译器中,它是一个成熟的行业标准,它提供卸载到 GPU 的功能,并且它对现有代码的侵入性极小,让您可以轻松地退回到串行公式。

下面,我重写了您的代码以利用 OpenMP。我还进行了其他一些更改。

我已经删除了using namespace std 行。这行代码存在潜在危险,因为标准库很大,这会将其全部拉入全局命名空间,可能会与您可能正在使用的其他库发生冲突。

我也删除了int array[N]。这段代码一开始很危险,因为只有当N 是编译时常量时才能真正定义这样的数组。您真正需要的是动态分配。在 C++ 中,最好的方法是使用 std::vector(见下文)。

最后,我们进入 for 循环。 OpenMP 最适合循环的每次迭代独立的场景。这意味着如果您在使用a[i]=3*a[i-1] 之类的代码时遇到困难。但是,您可以制作循环以适应模式。

对 OpenMP 的调用:

#pragma omp parallel for reduction(+:sum) reduction(max:largest_number)

告诉电脑

启动与内核一样多的线程,并在这些线程之间平均分配循环。为每个线程提供变量sumlargest_number 的自己的私有 副本。每个线程完成其工作后,使用+ 运算符将私有sum 变量组合成一个全局sum 变量。此外,使用max 运算符将所有私有largest_number 变量组合成一个全局largest_number 变量。

如您所见,这一行非常简洁地表示了需要完成的工作。即使您计划使用更复杂的框架,例如 Intel 的 Thread Building Blocks 或使用 std::thread 自行开发,OpenMP 也可以成为设计解决方案原型的好方法。它还可以很好地与 TBB 和 std::thread 配合使用。

//Compile with g++ main.cpp -fopenmp
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>

int main()
  std::cout << "Enter the Size of the Array (N): ";
  int N;
  std::cin >> N;

  std::vector<int> array(N);

  //WARNING: This is a poor way of choosing a seed
  srand(time(0));

  std::cout << "Populating Array...\n";
  for(int i =0; i < N; i++)
    array[i] =  (rand() % 1000) + 1; //WARNING: This is a poor way to choose random numbers

  int largest_number = 0;
  int sum = 0;

  // Finding the largest value and calculating sum of the array
  #pragma omp parallel for reduction(+:sum) reduction(max:largest_number)
  for( int j  = 0; j < N; j++)
    sum += array[j];

    if(array[j] > largest_number)
      largest_number = array[j];
  

  std::cout << "Output: \n";
  std::cout << "Maximum:  " <<  largest_number << ";" <<  "Sum: " <<  sum;
  std::cout << "\n";

【讨论】:

这真是太棒了!非常感谢您的回复。如果你不介意的话,我还有一个问题要问你。如果我想使用诸如 clik_spawn、clik_for 或 clik_sync 之类的 Clik Plus 概念来完成类似的操作,该怎么办?语法似乎更容易理解,但我仍然有点困惑如何在这样的代码上实现 clik plus,以便有人可以像这样编译它 --> g++ main.cpp -fclikplus @Caladin00:恐怕我不认识 Cilk,你的问题也没有提到。 (如果我确实知道,我很乐意帮助您 - 抱歉。)如果您想要 Cilk 特定的答案,我建议您提前提出一个单独的问题。事实上,您将获得各种符合 C++ 标准的答案。 这是我的错。不管怎样,谢谢你的解释!这很有意义。 @Richard 很棒的解释。由于reduction 参数,是否会提供自己的私人副本?或者这纯粹是为了事后的聚合?我想知道在并行循环之前声明的变量是否可以安全地在多线程环境中通过循环内的引用进行更新? 我假设我必须使用copyin() 参数来创建这些变量的副本到非主线程?【参考方案2】:

使用 C++17:

std::vector<int> array(N);
// ...
auto sum = reduce(std::execution::par, arr.begin(), arr.end(), 0);

【讨论】:

以上是关于有人可以帮我并行化这个 C++ 代码吗?的主要内容,如果未能解决你的问题,请参考以下文章

运行 SYCL 代码时结果不正确。在尝试并行化循环时

串行程序并行化

我可以使用 OpenACC 并行化调用某些函数的大代码吗?

使用 OpenMP 在 C、C++ 中并行化嵌套 for 循环的几种方法之间的区别

可以并行化 ARM NEON 的过滤器吗?

如何并行化使用 boost?