从使用 OpenMP 的教程中计算 Pi 算法
Posted
技术标签:
【中文标题】从使用 OpenMP 的教程中计算 Pi 算法【英文标题】:Calculate Pi algorithm from a tutorial using OpenMP 【发布时间】:2018-11-11 01:01:42 【问题描述】:我正在学习关于 OpenMP 的 this 教程,我在第 19 页遇到了这个练习。这是一个我必须并行化的 pi 计算算法:
static long num_steps = 100000;
double step;
void main ()
int i;
double x, pi
double sum = 0.0;
step = 1.0 / (double)num_steps;
for(i = 0; i < num_steps; i++)
x = (I + 0.5) * step;
sum = sum + 4.0 / (1.0 + x*x);
pi = step * sum;
到目前为止,我无法使用#pragma parallel for。我只能使用:
#pragma omp parallel
omp_get_thread_num();
omp_set_num_threads(int);
omp_get_num_threads();
我的实现如下所示:
#define NUM_STEPS 800
int main(int argc, char **argv)
int num_steps = NUM_STEPS;
int i;
double x;
double pi;
double step = 1.0 / (double)num_steps;
double sum[num_steps];
for(i = 0; i < num_steps; i++)
sum[i] = 0;
omp_set_num_threads(num_steps);
#pragma omp parallel
x = (omp_get_thread_num() + 0.5) * step;
sum[omp_get_thread_num()] += 4.0 / (1.0 + x * x);
double totalSum = 0;
for(i = 0; i < num_steps; i++)
totalSum += sum[i];
pi = step * totalSum;
printf("Pi: %.5f", pi);
通过使用 sum 数组忽略问题(稍后解释它需要使用 #pragma omp critical 或 #pragma omp atomic 为 sum 值定义临界区),上述实施仅适用于有限数量的线程(在我的情况下为 800),其中串行代码使用 100000 步。有没有办法只使用前面提到的 OpenMP 命令来实现这一点,还是我必须使用 #pragma omp parallel for,这在教程中还没有提到?
非常感谢您抽出宝贵时间,我真的很想通过 OpenMP 掌握 C 语言中的并行化概念。
【问题讨论】:
你可以使用#pragma omp atomic
吗?
@IncreasinglyIdiotic 后面会解释#pragma omp atomic 的用处,但仅涉及和值。如何使用它来解决“线程过多”的问题?有没有办法,不用parallel for?
本教程定期将困惑的学习者发送到 ***。我建议您寻找遵循更惯用的高级方法的学习材料,而不是自下而上地解释 OpenMP。如果您正在参加现场研讨会,也许它会起作用,但在在线阅读/观看材料时肯定不会。
@Zulan [ad]***:自 2008 年以来将混乱转化为知识:-D[/ad]
【参考方案1】:
您需要找到一种方法,使您的并行算法在某种程度上独立于线程数。
最简单的方法是这样做:
int tid = omp_get_thread_num();
int n_threads = omp_get_num_threads();
for (int i = tid; i < num_steps; i += n_threads)
// ...
这样,无论线程数如何,工作都会被分配到所有线程中。
如果有 3 个线程和 9 个步骤:
线程 0 将执行步骤 0、3、6 线程 1 将执行步骤 1、4、7 线程 2 将执行步骤 2、5、8这可行,但如果每个线程都从某个共享数组访问数据,这并不理想。如果线程出于locality 的目的访问附近的数据部分会更好。
在这种情况下,您可以将步骤数除以线程数,并为每个线程分配一组连续的任务,如下所示:
int tid = omp_get_thread_num();
int n_threads = omp_get_num_threads();
int steps_per_thread = num_steps / n_threads;
int start = tid * steps_per_thread;
int end = start + steps_per_thread;
for (int i = start; i < end; i++)
// ...
现在执行 9 个步骤的 3 个线程看起来像:
线程 0 执行步骤 0、1、2 线程 1 执行步骤 3、4、5 线程 2 执行步骤 6、7、8这种方法实际上是使用#pragma omp for
时最有可能发生的情况。在大多数情况下,编译器只是根据线程的数量来划分任务,并为每个线程分配一个部分。
因此,给定一组 2 个线程和 100 次循环迭代,编译器可能会将迭代 0-49 分配给线程 0,将迭代 50-99 分配给线程 1。
请注意,如果迭代次数不能除以线程数,则需要显式处理余数。
【讨论】:
谢谢,这是一个非常有用的答案,因为它可以帮助我阐明并行化的“内部工作原理”与#pragma omp parallel for 等功能有关。以上是关于从使用 OpenMP 的教程中计算 Pi 算法的主要内容,如果未能解决你的问题,请参考以下文章
使用非线程安全随机数生成器在 C 中为 pi monte carlo 更正 OpenMP 编译指示