OpenMP 教程(一) 深入人剖析 OpenMP reduction 子句前言在前面的教程OpenMP入门当中我们简要介绍了 OpenMP 的一些基础的使用方法,在本篇文章当中我们将从一些基础的问题开始,然后仔细介绍在 OpenMP 当中 reduction 子句的各种使用方法 。
从并发求和开始我们的任务是两个线程同时对一个变量 data
进行 ++
操作,执行 10000 次,我们看下面的代码有什么问题:
#include <stdio.h>#include <omp.h>#include <unistd.h>static int data;int main() {#pragma omp parallel num_threads(2) // 使用两个线程同时执行上面的代码块{for(int i = 0; i < 10000; i++) {data++;usleep(10);}// omp_get_thread_num 函数返回线程的 id 号 这个数据从 0 开始,0, 1, 2, 3, 4, ...printf("data = https://www.huyubaike.com/biancheng/%d tid = %d/n", data, omp_get_thread_num());}printf("In main function data = https://www.huyubaike.com/biancheng/%d/n", data);return 0;}
在上面的代码当中 , 我们开启了两个线程并且同时执行 $pragma
下面的代码块,但是上面的程序有一个问题,就是两个线程可能同时执行 data++
操作,但是同时执行这个操作的话,就存在并发程序的数据竞争问题,在 OpenMP 当中默认的数据使用方式就是?♂?线程之间是共享的比如下面的执行过程:
- 首先线程 1 和线程 2 将 data 加载到 CPU 缓存当中,当前的两个线程得到的
data
的值都是 0。 - 线程 1 和线程 2 对
data
进行 ++ 操作,现在两个线程的data
的值都是 1 。 - 线程 1 将 data 的值写回到主存当中,那么主存当中的数据的值就等于 1。
- 线程 2 将 data 的值写回到主存当中 , 那么主存当中的数据的值也等于 1。
解决求和问题的各种办法使用数组巧妙解决并发程序当中的数据竞争问题在上面的程序当中我们使用了一个函数
omp_get_thread_num
这个函数可以返回线程的 id 号,我们可以根据这个 id 做一些文章,如下面的程序:#include <stdio.h>#include <omp.h>#include <unistd.h>static int data;static int tarr[2];int main() {#pragma omp parallel num_threads(2){int tid = omp_get_thread_num();for(int i = 0; i < 10000; i++) {tarr[tid]++;usleep(10);}printf("tarr[%d] = %d tid = %d\n", tid, tarr[tid], tid);}data = https://www.huyubaike.com/biancheng/tarr[0] + tarr[1];printf("In main function data = https://www.huyubaike.com/biancheng/%d/n", data);return 0;}
在上面的程序当中我们额外的使用了一个数组 tarr
用于保存线程的本地的和,然后在最后在主线程里面讲线程本地得到的和相加起来,这样的话我们得到的结果就是正确的了 。$./lockfree01.outtarr[1] = 10000 tid = 1tarr[0] = 10000 tid = 0In main function data = https://www.huyubaike.com/biancheng/20000
在上面的程序当中我们需要知道的是 , 只有当并行域当中所有的线程都执行完成之后,主线程才会继续执行并行域后面的代码,因此主线程在执行代码data = https://www.huyubaike.com/biancheng/tarr[0] + tarr[1];printf("In main function data = https://www.huyubaike.com/biancheng/%d/n", data);
之前,OpenMP 中并行域中的代码全部执行完成,因此上面的代码执行的时候数组 tarr
中的结果已经计算出来了,因此上面的代码最终的执行结果是 2000 。reduction 子句在上文当中我们使用数组去避免多个线程同时操作同一个数据的情况,除了上面的方法处理求和问题,我们还有很多其他方法去解决这个问题,下面我们使用 reduction 子句去解决这个问题:
#include <stdio.h>#include <omp.h>#include <unistd.h>static int data;int main() {#pragma omp parallel num_threads(2) reduction(+:data){for(int i = 0; i < 10000; i++) {data++;usleep(10);}printf("data = https://www.huyubaike.com/biancheng/%d tid = %d/n", data, omp_get_thread_num());}printf("In main function data = https://www.huyubaike.com/biancheng/%d/n", data);return 0;}
在上面的程序当中我们使用了一个子句 reduction(+:data)
在每个线程里面对变量 data 进行拷贝,然后在线程当中使用这个拷贝的变量,这样的话就不存在数据竞争了,因为每个线程使用的 data 是不一样的,在 reduction 当中还有一个加号,这个加号表示如何进行规约操作,所谓规约操作简单说来就是多个数据逐步进行操作最终得到一个不能够在进行规约的数据 。例如在上面的程序当中我们的规约操作是 + ,因此需要将线程 1 和线程 2 的数据进行 + 操作,即线程 1 的 data 加上 线程 2 的 data 值,然后将得到的结果赋值给全局变量 data,这样的话我们最终得到的结果就是正确的 。
推荐阅读
- centos使用lftp备份文件
- 金铲铲之战2022.8.11更新维护内容一览
- 汉字魔法第60关怎么过
- 第4版 高性能MySQL 第一章 MySQL架构 读书笔记
- 半自动版本 PXE批量装windows
- 关于Docker的一些事--Docker概述
- 纸嫁衣4第一章异途通关流程图文攻略-纸嫁衣4红丝缠第一章怎么过
- 洛克王国2022扇谜挑战活动猜谜语答案一览-洛克王国扇谜挑战攻略大全
- 小米11使用感受_小米11使用测评
- 移动彩铃怎么设置(打电话对方听到的彩铃设置教程)