[英]OpenMP. Parallelization of the loop in N threads
我正在尝试使用多个线程并行化 5000 万次迭代的循环 - 首先是 1,然后是 4、8 和 16。下面是实现此功能的代码。
#include <iostream>
#include <omp.h>
using namespace std;
void someFoo();
int main() {
someFoo();
}
void someFoo() {
long sum = 0;
int numOfThreads[] = {1, 4, 8, 16};
for(int j = 0; j < sizeof(numOfThreads) / sizeof(int); j++) {
omp_set_num_threads(numOfThreads[j]);
start = omp_get_wtime();
#pragma omp parallel for
for(int i = 0; i<50000000; i++) {
sum += i * 10;
}
#pragma omp end parallel
end = omp_get_wtime();
cout << "Result: " << sum << ". Spent time: " << (end - start) << "\n";
}
}
预计在 4 个线程中程序将比在 1 中运行得快,在 8 个线程中比在 4 个线程中快,在 16 个线程中比在 8 个线程中快,但实际上情况并非如此 - 一切都在混乱中执行速度和几乎没有区别。 此外,在任务管理器中看不到程序已并行化。 我有一台具有 8 个逻辑处理器和 4 个内核的计算机。
请告诉我我在哪里犯了错误以及如何正确并行化 N 个线程中的循环。
您的代码中存在竞争条件,因为sum
是同时从多个线程读取/写入的。 这应该会导致错误的结果。 您可以使用减少指令#pragma omp parallel for reduction(+:sum)
来解决此问题。 请注意,OpenMP 不会检查您的循环是否可以并行化,这是您的责任。
此外,并行计算可能比顺序计算慢,因为聪明的编译器可以看到sum = 50000000*(50000000-1)/2*10 = 12499999750000000
(AFAIK,Clang 就是这样做的)。 结果,基准肯定是有缺陷的。 请注意,这肯定大于long
类型可以包含的内容,因此您的代码中肯定存在溢出。
此外,AFAIK,没有指令#pragma omp end parallel
之类的东西。
最后,请注意,您可以使用OMP_NUM_THREADS
环境变量控制线程数,这通常比在应用程序中设置它更方便(在应用程序代码中硬连线给定数量的线程通常不是一个好主意,即使对于基准测试也是如此)。
请告诉我我在哪里犯了错误以及如何正确并行化 N 个线程中的循环。
首先,您需要修复代码示例中的一些编译器问题。 就像删除#pragma omp end parallel
之类的编译指示,正确声明变量等等。 其次,您需要在更新变量sum
期间修复竞争条件。 该变量在线程之间共享并同时更新。 最简单的方法是使用 OpenMP 的reduction
子句,您的代码如下所示:
#include <stdio.h>
#include <omp.h>
int main() {
someFoo();
}
void someFoo() {
int numOfThreads[] = {1, 4, 8, 16};
for(int j = 0; j < sizeof(numOfThreads) / sizeof(int); j++) {
omp_set_num_threads(numOfThreads[j]);
double start = omp_get_wtime();
double sum = 0;
#pragma omp parallel for reduction(+:sum)
for(int i = 0; i<50000000; i++) {
sum += i * 10;
}
double end = omp_get_wtime();
printf("Result: '%d' : '%f'\n", sum, (end - start));
}
}
使用多核运行时,您应该会获得一些加速。
注意:为了解决@Jérôme Richard 首先提到的溢出问题,我将 'sum' 变量从long更改为double 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.