我试图编译以下代码:
#pragma omp parallel shared (j)
{
#pragma omp for schedule(dynamic)
for(i = 0; i != j; i++)
{
// do something
}
}
我收到此错误:错误:无效控制谓词。
我检查openMP reference guide并且它表示对于它的并行“仅”允许以下运算符之一:< < => > =。
我不明白为什么不允许i != j
。我可以理解它是否是静态调度,因为openMP需要预先计算分配给每个线程的迭代次数。但我无法理解为什么在这种情况下这种限制为例。有线索吗?
编辑:即使我制作for(i = 0; i != 100; i++)
,虽然我可以放“<”或“< =”。
答案 0 :(得分:62)
对于signed int,环绕行为是未定义的。如果我们允许!=
,程序员可能会获得意外的tripcount。问题在于编译器是否可以生成代码来计算循环的行程计数。
对于一个简单的循环,例如:
for( i = 0; i < n; ++i )
编译器可以确定存在'n'次迭代,如果n> = 0 ,并且零次迭代如果n&lt; 0 强>
对于像这样的循环:
for( i = 0; i != n; ++i )
再次,编译器应该能够确定存在'n'次迭代,如果n> = 0 ; 如果n&lt; 0 ,我们不知道它有多少次迭代。
对于像这样的循环:
for( i = 0; i < n; i += 2 )
如果n> = 0 ,编译器可以生成代码来计算行程计数(循环迭代计数)为 floor((n + 1)/ 2),如果n为n,则编译0 &LT; 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 。 0 ,因为编译器知道循环必须计数;在这种情况下,如果 k < 0 ,这不是一个合法的OpenMP计划。
对于像这样的循环:
for( i = 0; i != n; i += k )
编译器甚至不知道我是在向上还是向下计数。它不知道'我'是否会击中'n'。它可能是一个无限循环。
积分:OpenMP ARB
答案 1 :(得分:17)
与它的外观相反,schedule(dynamic)
不适用于动态数量的元素。而是将迭代块分配给线程是动态的。使用静态调度,此分配在工作共享构造的开头预先计算。通过动态调度,迭代块以先到先得的方式给出线程。
OpenMP标准很清楚,一旦遇到工作共享构造,就会预先计算迭代次数,因此循环计数器可能不会在循环体内被修改(OpenMP 3.1规范,§2.5.1 - 循环结构) :
在进入最外层之前计算每个相关循环的迭代计数 环。如果任何相关循环的执行改变了用于计算任何值的任何值 迭代计数,然后行为未指定。
用于计算迭代计数的整数类型(或类型,用于Fortran) 折叠循环是实现定义的。
工作共享循环具有编号为0,1,...,N-1的逻辑迭代,其中N是数字 循环迭代,逻辑编号表示迭代的顺序 如果相关的循环由单个线程执行,则将执行。该
schedule
子句指定关联循环的迭代如何划分 连续的非空子集,称为块,以及这些块是如何分布的 在团队的线程之间。每个线程在上下文中执行其分配的块 它的隐含任务。使用在循环结构中为私有的任何变量的原始列表项来评估 chunk_size 表达式。没有说明评估该表达的任何副作用是以何种顺序或多少次发生的。在循环结构的schedule
子句表达式中使用变量会导致对所有封闭结构中的变量进行隐式引用。
这些关系运算符限制背后的基本原理非常简单 - 它提供了关于循环方向的明确指示,它可以轻松计算迭代次数,并且它提供了C /中OpenMP工作共享指令的类似语义C ++和Fortran。此外,其他关系操作需要仔细检查循环体,以便了解循环如何在许多情况下无法解决,并且会使实现变得麻烦。
OpenMP 3.0引入了显式task
构造,允许并行化具有未知迭代次数的循环。但是有一个问题:任务引入了一些严重的开销,每个循环迭代的一个任务只有在这些迭代需要花费相当长的时间才能执行时才有意义。否则,开销将主导执行时间。
答案 2 :(得分:4)
答案很简单。 OpenMP不允许提前终止一组线程。 使用==或!=,OpenMP无法确定循环何时停止。 1.一个或多个线程可能会达到终止条件,这可能不是唯一的。 2. OpenMP无法关闭可能永远不会检测到该情况的其他线程。
答案 3 :(得分: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++);
}
}
请注意i
语句中的for
以及循环内部都会增加,导致无限循环的可能性(但不是保证)。
如果谓词是<
,那么循环的行为仍然可以在并行上下文中很好地定义,而编译器不必在循环内检查i
的更改并确定这些更改将如何影响循环的界限。
如果谓词是!=
,则循环的行为不再明确定义,并且在范围上可能是无限的,从而阻止了简单的并行细分。
答案 4 :(得分:0)
我认为除了扩展现有功能以实现这一目标之外,可能没有其他充分的理由。
IIRC最初这些必须是静态的,以便它可以在编译时确定如何生成循环代码......它可能只是一个宿醉。