1

我想知道一种在使用 C++ 的 OpenMP 中的并行 for 循环中获取给定线程的值范围的方法。例如,在下面的代码中,我想知道每个线程在每个线程的循环中使用的第一个值是什么。

#pragma omp parallel for schedule(static)
for(int i=0; i<n; i++) 

让我举一个例子说明为什么我可能需要这些值。假设我想用计数的总和填充一个数组。计数之和的闭式解是n*(n+1)/2。要使用 OpenMP 做到这一点,我可以这样做:

#pragma omp parallel for schedule(static)
for(int i=0; i<n; i++) {    
    a[i] = i*(i+1)/2;
}

但是,我怀疑获得计数总和的更快方法是不使用每次迭代的封闭形式解决方案(有一个正方形),而是记住每次迭代的总和,如下所示:

int cnt = 0;
for(int i=0; i<n; i++) {
    cnt += i;
    a[i] = cnt;
}

但我能想到的使用 OpenMP 执行此操作的唯一方法是明确定义范围值,如下所示:

#pragma omp parallel
{
    const int ithread = omp_get_thread_num();
    const int nthreads = omp_get_num_threads();
    const int start = ithread*n/nthreads;
    const int finish = (ithread+1)*n/nthreads;

    int cnt = 0;
    int offset = (start-1)*(start)/2;
    for(int i=start; i<finish; i++) {
        cnt += i;
        a[i] = cnt + offset;
    }
}

如果我可以从中获得起始值, #pragma omp parallel for schedule(static)我就不必定义start, finish, ithread, and nthreads.

编辑:阅读Agner Fog 的优化 C++手册后,我意识到我正在做的事情称为归纳。他给出了一个使用归纳法更有效地计算多项式值的例子。以下是他手册中的一些示例

没有感应:

// Example 8.23a. Loop to make table of polynomial
const double A = 1.1, B = 2.2, C = 3.3; // Polynomial coefficients
double Table[100]; // Table
int x; // Loop counter
for (x = 0; x < 100; x++) {
    Table[x] = A*x*x + B*x + C; // Calculate polynomial

带感应:

// Example 8.23b. Calculate polynomial with induction variables
const double A = 1.1, B = 2.2, C = 3.3; // Polynomial coefficients
double Table[100]; // Table
int x; // Loop counter
const double A2 = A + A; // = 2*A
double Y = C; // = A*x*x + B*x + C
double Z = A + B; // = Delta Y
for (x = 0; x < 100; x++) {
    Table[x] = Y; // Store result
    Y += Z; // Update induction variable Y
    Z += A2; // Update induction variable Z
}

要使用 OpenMP 执行此操作,我需要获取每个块的起始值。使用 OpenMP 执行此操作的唯一方法是手动定义块。

4

2 回答 2

1

这是一个扩展的评论,而不是一个答案......

没有 OpenMP 例程或预定义变量来获取i(在您的情况下)每个线程将执行的值范围。您必须按照您概述的内容编写一些内容才能自己获得这些数字。

但在你这样做之前,停下来想一想。所有这些额外的代码,以及编写和维护它的努力,只是为了避免每次迭代一次乘法!即使您的代码正常工作,我也怀疑您看到的任何加速都值得付出努力。更糟糕的是,一旦您想使用不同的时间表static,就必须重新进行指数计算;对于许多其他调度选项,一个线程执行的迭代无论如何都不是一个简单的范围。

您正在逆向编程,不仅是 OpenMP,而且可能是一般的并行编程。可以在不考虑运行时可用数量或运行时系统如何划分工作并且任务之间没有依赖关系的情况下分发给线程的程序是并行化的理想选择。它们通常为大量线程提供良好的可伸缩性,而不需要大量的程序员工作。

您已经拥有的封闭式解决方案就是您所需要的。顺其自然。逆向编程将(不可避免地我会争辩)产生更复杂的代码,这些代码难以维护,并且很少会产生并行加速来补偿它们的成本。

于 2013-10-15T10:47:34.087 回答
0

可能没有办法做到这一点。即使您可以获得每个线程的范围,例如start,对于这样的单个for循环,您希望将其注入到哪里?

#pragma omp parallel for schedule(static)
for(int i=0; i<n; i++) {    
    a[i] = ...
}

omp parallel for通常假设迭代之间没有依赖关系。如果您添加诸如 之类的依赖项cnt,您可能不应该使用此指令。

于 2013-10-15T10:55:43.260 回答