78

我试图编译以下代码:

#pragma omp parallel shared (j)
{
   #pragma omp for schedule(dynamic)
   for(i = 0; i != j; i++)
   {
      // do something
   }
}

但我收到以下错误:错误:无效的控制谓词

OpenMP 标准规定,对于构造函数,parallel for它“仅”允许以下运算符之一:<, <=, > >=.

我不明白不允许的理由i != j。我可以理解,在 的情况下static schedule,因为编译器需要预先计算分配给每个线程的迭代次数。但是我不明白为什么在这种情况下会出现这种限制。有什么线索吗?


编辑:即使我 make for(i = 0; i != 100; i++),虽然我可以放 "<" 或 "<=" 。

4

5 回答 5

79

.

我向 OpenMP 开发人员发送了一封关于此主题的电子邮件,得到的答复是:

对于有符号整数,环绕行为是未定义的。如果我们允许!=,程序员可能会得到意外的tripcount。问题是编译器是否可以生成代码来计算循环的行程计数。

对于一个简单的循环,例如:

for( i = 0; i < n; ++i )

如果 n>=0 ,编译器可以确定有 'n' 次迭代,如果n < 0 则确定有零次迭代。

对于像这样的循环:

for( i = 0; i != n; ++i ) 

同样,如果 n >= 0,编译器应该能够确定存在“n”次迭代;如果 n < 0,我们不知道它有多少次迭代。

对于像这样的循环:

for( i = 0; i < n; i += 2 )

编译器可以生成代码来计算行程计数(循环迭代计数),如果 n >= 0 则为 floor((n+1)/2)如果 n < 0 则为 0

对于像这样的循环:

for( i = 0; i != n; i += 2 )

编译器无法确定“i”是否会命中“n”。如果'n'是奇数怎么办?

对于像这样的循环:

for( i = 0; i < n; i += k )

编译器可以生成代码来计算行程计数,如果 n >= 0 则为 floor((n+k-1)/k)如果 n < 0 则为 0 ,因为编译器知道循环必须向上计数;在这种情况下,如果k < 0,则它不是合法的 OpenMP 程序。

对于像这样的循环:

for( i = 0; i != n; i += k )

编译器甚至不知道我是向上还是向下计数。它不知道“i”是否会命中“n”。这可能是一个无限循环。

学分:OpenMP ARB

于 2012-11-15T18:14:14.870 回答
19

与它的外观相反,它schedule(dynamic)不适用于动态数量的元素。相反,将迭代块分配给线程是动态的。对于静态调度,此分配是在工作共享构造的开始时预先计算的。使用动态调度,迭代块按照先到先得的原则分配给线程。

OpenMP 标准非常清楚,一旦遇到工作共享构造,就会预先计算迭代的数量,因此不能在循环体内修改循环计数器(OpenMP 3.1 规范,§2.5.1 - 循环构造):

每个关联循环的迭代计数在进入最外层循环之前计算。如果任何关联循环的执行更改了用于计算任何迭代计数的任何值,则行为未指定。

用于计算折叠循环的迭代计数的整数类型(或类型,对于 Fortran)是实现定义的。

工作共享循环具有编号为 0,1,...,N-1 的逻辑迭代,其中 N 是循环迭代的次数,逻辑编号表示如果执行相关循环,则执行迭代的顺序通过一个线程。该 schedule子句指定相关循环的迭代如何划分为连续的非空子集,称为块,以及这些块如何分布在团队的线程中。每个线程在其隐式任务的上下文中执行其分配的块。大小使用在循环构造中设为私有的任何变量的原始列表项来评估表达式。未指定是否以什么顺序或多少次出现此表达式的求值的任何副作用。在循环构造的子句表达式中使用变量schedule会导致在所有封闭构造中隐式引用该变量。

这些关系运算符限制背后的基本原理非常简单——它清楚地指示了循环的方向,它可以轻松计算迭代次数,并且它提供了与 C/C++ 和 Fortran 中的 OpenMP 工作共享指令类似的语义. 此外,其他关系操作需要仔细检查循环体,以了解循环如何进行,这在许多情况下是不可接受的,并且会使实现变得麻烦。

OpenMP 3.0 引入了显式task构造,它允许以未知的迭代次数并行化循环。但是有一个问题:任务引入了一些严重的开销,并且每个循环迭代的一个任务只有在这些迭代需要相当长的时间才能执行时才有意义。否则开销将支配执行时间。

于 2012-11-09T17:19:52.727 回答
5

答案很简单。OpenMP 不允许提前终止一组线程。使用 == 或 !=,OpenMP 无法确定循环何时停止。1. 一个或多个线程可能达到终止条件,这可能不是唯一的。2. OpenMP 无法关闭其他可能永远不会检测到该条件的线程。

于 2012-11-15T17:25:14.910 回答
2

如果我看到声明

for(i = 0; i != j; i++)

用于代替语句

for(i = 0; i < j; i++)

我会想知道为什么程序员会做出这样的选择,更不用说它可能意味着同样的事情。可能 OpenMP 正在做出艰难的语法选择,以强制实现一定的代码清晰度。

这是对使用提出挑战的代码,!=可能有助于解释为什么不允许使用。

#include <cstdio>

int main(){
    int j=10;
   #pragma omp parallel for
   for(int i = 0; i < j; i++){
    printf("%d\n",i++);
   }
}

请注意,ifor语句以及循环本身中都会增加,导致无限循环的可能性(但不是保证)。

如果谓词是,<那么循环的行为仍然可以在并行上下文中很好地定义,而编译器不必检查循环内的更改i并确定这些更改将如何影响循环的边界。

如果谓词是!=,则循环的行为不再是明确定义的,它的范围可能是无限的,从而阻止了简单的并行细分。

于 2012-11-09T18:10:03.037 回答
0

我认为除了扩展现有功能之外,可能没有什么好的理由来达到这一点。

IIRC 最初这些必须是静态的,以便它可以在编译时确定如何生成循环代码......它可能只是一个后遗症。

于 2012-11-09T17:19:49.017 回答