Critical it y:ompcritical和ompsingle区别:

我试图了解 OpenMP 中#pragma omp critical#pragma omp single之间的确切区别:

这些的 Microsoft 定义是:

Single:允许您指定应在单个线程(不一定是主线程)上执行的代码段。

关键:指定代码一次只能在一个线程上执行。

所以这意味着在这两个,代码的确切部分之后将由一个线程执行,其他线程将不会进入该部分,例如,如果我们打印的东西,我们会看到屏幕上的结果一次,对不对?

区别如何?看起来关键照顾执行时间,但不是单一的!但我在实践中没有看到任何区别!这是否意味着一种等待或同步其他线程(不进入该部分)被认为是关键的,但没有什么可以将其他线程保持在单一的?它如何在实践中改变结果?

我很感激,如果任何人都可以澄清这一点,特别是通过一个例子。

117

singlecritical是两个非常不同的东西,正如您提到的:

single指定一段代码应该由单线程执行(不一定是主线程)

critical指定代码一次由一个线程执行

因此,前者将被执行只有一次,而后者将被执行多次有线程

例如下面的代码

int a=0, b=0;
#pragma omp parallel num_threads(4)
{
    #pragma omp single
    a++;
    #pragma omp critical
    b++;
}
printf("single: %d -- critical: %d\n", a, b);

将打印

single: 1 -- critical: 4

我希望你现在能更好地看到差异。

为了完整起见,我可以补充一点:

single非常相似,但有两个区别:

将仅由主机执行,而single可以由首先到达该区域的线程执行;和

single在区域完成时具有隐式屏障,其中所有线程等待同步,而没有任何。

atomiccritical非常相似,但仅限于选择简单的操作。

我添加了这些精度,因为这两对指令通常是人们倾向于混淆的指令...

42

singlecritical属于两个完全不同的 OpenMP 构造类。single是一个工作共享构造,与forsections一起。工作共享构造用于在线程之间分配一定数量的工作。从某种意义上说,这些构造是“集体的”,在正确的 OpenMP 程序中,所有线程

for(又名循环构造)在线程之间自动分配循环的迭代-在大多数情况下,所有线程都有工作要做;

sections在线程之间分配一系列的代码块-一些线程需要工作。这是将for构造概括为具有 100 次迭代的循环,可以表示为例如 10 个循环部分,每个循环 10 次迭代。

single挑出一个代码块只能由一个线程执行,通常是第一个遇到它的线程(实现细节)-只有一个线程得到工作。single在很大程度上等同于sections只有一个部分。

所有工作共享构造的一个共同特点是在其末端存在一个隐式屏障,该屏障可以通过将nowait子句添加到相应的 OpenMP 构造来关闭,但是该标准不需要这样的行为,并且在某些 OpenMP 运行时,尽管存在nowait,屏障可能会继续存在。

critical是一个同步构造,与atomic等一起使用。同步构造用于防止竞争条件并在执行事物时带来秩序。

critical通过防止在所谓的争用组中的线程之间同时执行代码来防止竞争条件。这意味着来自所有并行区域的遇到类似名称的关键结构的所有线程被序列化;

atomic通常通过使用特殊的汇编指令将某些简单的内存操作转换为原子操作。原子作为一个不可的单元一次完成。例如,一个线程从某个位置读取原子,同时另一个线程将原子写入同一位置,将返回旧值或更新值,但绝不会返回旧值和新值的某种中间混搭;

挑出一个代码块,仅由主线程(ID 为 0 的线程)执行。与single不同,在构造的末尾没有隐式屏障,也不要求所有线程都必须遇到构造。此外,缺少隐式屏障意味着基本上不会刷新线程的共享内存视图。

critical是一个非常通用的构造,因为它能够在程序代码的非常不同的部分中序列化不同的代码片段,即使在不同的并行区域中也是如此 (仅在嵌套并行性的情况下很重要)。每个critical构造都有一个紧跟其后的括号中提供的可选名称。匿名关键构造共享相同的实现特定名称。一旦一个线程进入这样的构造,任何其他线程

下面是上述概念的说明。

#pragma omp parallel num_threads(3)
{
   foo();
   bar();
   ...
}

结果如下:

thread 0: -----< foo() >< bar() >-------------->
thread 1: ---< foo() >< bar() >---------------->
thread 2: -------------< foo() >< bar() >------>

(线程 2 故意是一个后来者)

single构造中有foo();调用:

#pragma omp parallel num_threads(3)
{
   #pragma omp single
   foo();
   bar();
   ...
}

结果如下:

thread 0: ------[-------|]< bar() >----->
thread 1: ---[< foo() >-|]< bar() >----->
thread 2: -------------[|]< bar() >----->

线程 1 执行foo()调用,因为示例 OpenMP 运行时选择将作业分配给遇到该构造的第一个线程。

添加一个nowait子句可能会删除隐式障碍,导致类似:

thread 0: ------[]< bar() >----------->
thread 1: ---[< foo() >]< bar() >----->
thread 2: -------------[]< bar() >---->

在匿名critical构造中有foo();调用:

#pragma omp parallel num_threads(3)
{
   #pragma omp critical
   foo();
   bar();
   ...
}

结果如下:

thread 0: ------xx[< foo() >]< bar() >-------------->
thread 1: ---[< foo() >]< bar() >------------------------->
thread 2: -------------[< foo() >]< bar() >--->

xx...显示了一个线程在进入自己的构造之前等待其他线程执行相同名称的关键构造所花费的时间。

不同名称的关键结构彼此不同步。例如:

#pragma omp parallel num_threads(3)
{
   if (omp_get_thread_num() > 1) {
     #pragma omp critical(foo2)
     foo();
   }
   else {
     #pragma omp critical(foo01)
     foo();
   }
   bar();
   ...
}

结果如下:

thread 0: ------xx[< foo() >]< bar() >---->
thread 1: ---[< foo() >]< bar() >--------------->
thread 2: -------------[< foo() >]< bar() >----->

现在线程 2 不与其他线程同步,因为它的关键结构命名不同,因此对foo()进行潜在危险的同时调用。

另一方面,匿名关键构造(通常是同名构造)彼此同步,无论它们在代码中的什么位置:

#pragma omp parallel num_threads(3)
{
   #pragma omp critical
   foo();
   ...
   #pragma omp critical
   bar();
   ...
}

以及由此产生的执行时间线:

thread 0: ------xx[< foo() >]< ... >[< bar() >]------------>
thread 1: ---[< foo() >]< ... >[< bar() >]----------------------->
thread 2: -------------[< foo() >]< ... >[< bar() >]->

本站系公益性非盈利分享网址,本文来自用户投稿,不代表边看边学立场,如若转载,请注明出处

(393)
C1怎么转c2手续:C#不接受c1 c2 c3作为定义的 vars
上一篇
类方法和实例方法区别:类和实例方法有什么区别 (instance method vs class method)
下一篇

相关推荐

发表评论

登录 后才能评论

评论列表(60条)