2 c multithreading openmp critical-section reduction
我目前正在学习使用C和OpenMP进行并行编程.我想编写简单的代码,其中两个共享值由多个线程递增.首先,我使用了还原指令,它的工作原理就是这样.然后我切换到使用关键指令启动关键部分 - 它也有效.出于好奇,我试图合并这两个解决方案并检查行为.我期待两个有效的,相等的值.
码:
#include <stdio.h>
#include <stdlib.h>
#include "omp.h"
#define ITER 50000
int main( void )
{
int x, y;
#pragma omp parallel reduction(+:x,y)
{
#pragma omp for
for (int i = 0; i < ITER; i++ )
{
x++;
#pragma omp critical
y++;
}
}
printf("non critical = %d\ncritical = %d\n", x, y);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
非关键= 50000
关键= 4246432
当然,当涉及到'critical'(变量y)时,输出是随机的,另一个表现为预期的并且总是50000.
x的行为是可以理解的 - 减少使其在单线程范围内是私有的.在将线程的增量值相加并传递给非局部x之后.
我不明白的是y的行为.它就像x一样私有,但它也在临界区内,所以它有多个原因让其他线程无法访问.然而,我认为,恰好是竞争条件.难道关键不知何故Ÿ公共(共享)?
我知道这段代码没有任何意义,因为它只能使用一个简化/关键.我想知道这种行为背后的原因.
您的代码的主要问题是x并且y未初始化.第二个问题是临界区中使用的变量应该是shared减少变量而不是减少变量,尽管这只会影响性能,而不是正确性.
我已经纠正了你的代码并对其进行了修改以演示如何reduce,critical并且atomic所有代码都产生相同的结果.
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int main(int argc, char* argv[])
{
int iter = (argc>1) ? atoi(argv[1]) : 50000;
int r=0, c=0, a=0;
printf("OpenMP threads = %d\n", omp_get_max_threads() );
#pragma omp parallel reduction(+:r) shared(c,a)
{
#pragma omp for
for (int i = 0; i < iter; i++ ) {
r++;
#pragma omp critical
c++;
#pragma omp atomic
a++;
}
}
printf("reduce = %d\n"
"critical = %d\n"
"atomic = %d\n", r, c, a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
icc -O3 -Wall -qopenmp -std=c99 redcrit.c
Run Code Online (Sandbox Code Playgroud)
OpenMP threads = 4
reduce = 50000
critical = 50000
atomic = 50000
Run Code Online (Sandbox Code Playgroud)
您的代码只显示未定义的行为,并且存在critical与您获得错误结果无关.
难道关键不知何故Ÿ公共(共享)?
不,它没有.它只会通过阻止并发执行线程来减慢循环.
您缺少的是减少操作的结果与减少变量的初始值组合,即与变量在并行区域之前具有的值相结合.在你的情况,都x和y具有随机初始值,所以你得到随机的结果.x在你的情况下,初始值恰好为0,这就是为什么你得到正确的结果就是UB.初始化都x和y使你的代码像预期的那样.
OpenMP规范声明:
该
reduction子句指定redu-identifier和一个或多个列表项.对于每个列表项,在每个隐式任务或SIMD通道中创建私有副本,并使用reduction-identifier的初始化值初始化.该区域的端部后,原来的列表项被更新,使用与相关联的组合器的专用副本的值减少标识符.
这是使用4个线程执行原始代码:
$ icc -O3 -openmp -std=c99 -o cnc cnc.c
$ OMP_NUM_THREADS=1 ./cnc
non critical = 82765
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 82765
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 82765
critical = 50194
$ OMP_NUM_THREADS=4 ./cnc
non critical = 82767
critical = 2112072800
Run Code Online (Sandbox Code Playgroud)
第一次运行一个线程表明它不是由于数据竞争.
用int x=0, y=0;:
$ icc -O3 -openmp -std=c99 -o cnc cnc.c
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1964 次 |
| 最近记录: |