OpenMP入门

2023-05-16

OpenMP 是 Open MultiProcessing 的缩写。可以在 Visual Studio 或者 gcc 中使用。

Hello World

把下面的代码保存为 omp.cc

#include <iostream>
#include <omp.h>

int main()
{
#pragma omp parallel for
for (char i = 'a'; i <= 'z'; i++)
std::cout << i << std::endl;

return 0;
}

然后 g++ omp.cc -fopenmp就可以了

循环的并行化

OpenMP的设计们希望提供一种简单的方式让程序员不需要懂得创建和销毁线程就能写出多线程化程序。为此他们设计了一些pragma,指令和函数来让编译器能够在合适的地方插入线程大多数的循环只需要在for之前插入一个pragma就可以实现并行化。而且,通过把这些恼人的细节都丢给编译器,你可以花费更多的时间来决定哪里需要多线程和优化数据结构

下面个这个例子把32位的RGB颜色转换成8位的灰度数据,你只需要在for之前加上一句pragma就可以实现并行化了

#pragma omp parallel for
for (int i = 0; i < pixelCount; i++) {
grayBitmap[i] = (uint8_t)(rgbBitmap[i].r * 0.229 +
rgbBitmap[i].g * 0.587 +
rgbBitmap[i].b * 0.114);
}

神奇吧,首先,这个例子使用了“work sharing”,当“work sharing”被用在for循环的时候,每个循环都被分配到了不同的线程,并且保证只执行一次。OpenMP决定了多少线程需要被打开,销毁和创建,你需要做的就是告诉OpenMP哪里需要被线程化。

OpenMP 对可以多线程化的循环有如下五个要求:

循环的变量变量(就是i)必须是有符号整形,其他的都不行。

循环的比较条件必须是< <= > >=中的一种

循环的增量部分必须是增减一个不变的值(即每次循环是不变的)。

如果比较符号是< <=,那每次循环i应该增加,反之应该减小

循环必须是没有奇奇怪怪的东西,不能从内部循环跳到外部循环,goto和break只能在循环内部跳转,异常必须在循环内部被捕获。

如果你的循环不符合这些条件,那就只好改写了

检测是否支持 OpenMP

#ifndef _OPENMP
fprintf(stderr, "OpenMP not supported");
#endif

避免数据依赖和竞争

当一个循环满足以上五个条件时,依然可能因为数据依赖而不能够合理的并行化。当两个不同的迭代之间的数据存在依赖关系时,就会发生这种情况。

// 假设数组已经初始化为1
#pragma omp parallel for
for (int i = 2; i < 10; i++) {
factorial[i] = i * factorial[i-1];
}

编译器会把这个循环多线程化,但是并不能实现我们想要的加速效果,得出的数组含有错误的结构。因为每次迭代都依赖于另一个不同的迭代,这被称之为竞态条件。要解决这个问题只能够重写循环或者选择不同的算法。

竞态条件很难被检测到,因为也有可能恰好程序是按你想要的顺序执行的。

管理公有和私有数据

基本上每个循环都会读写数据,确定那个数据时线程之间共有的,那些数据时线程私有的就是程序员的责任了。当数据被设置为公有的时候,所有的线程访问的都是相同的内存地址,当数据被设为私有的时候,每个线程都有自己的一份拷贝。默认情况下,除了循环变量以外,所有数据都被设定为公有的。可以通过以下两种方法把变量设置为私有的:

在循环内部声明变量,注意不要是static的

通过OpenMP指令声明私有变量

// 下面这个例子是错误的
int temp; // 在循环之外声明
#pragma omp parallel for
for (int i = 0; i < 100; i++) {
temp = array[i];
array[i] = doSomething(temp);
}

可以通过以下两种方法改正

// 1. 在循环内部声明变量
#pragma omp parallel for
for (int i = 0; i < 100; i++) {
int temp = array[i];
array[i] = doSomething(temp);
}

// 2. 通过OpenMP指令说明私有变量
int temp;
#pragma omp parallel for private(temp)
for (int i = 0; i < 100; i++) {
temp = array[i];
array[i] = doSomething(temp);
}

Reductions

一种常见的循环就是累加变量,对此,OpenMP 有专门的语句

例如下面的程序:

int sum = 0;
for (int i = 0; i < 100; i++) {
sum += array[i]; // sum需要私有才能实现并行化,但是又必须是公有的才能产生正确结果
}

上面的这个程序里,sum公有或者私有都不对,为了解决这个问题,OpenMP 提供了reduction语句;

int sum = 0;
#pragma omp parallel for reduction(+:sum)
for (int i = 0; i < 100; i++) {
sum += array[i];
}

内部实现中,OpenMP 为每个线程提供了私有的sum变量,当线程退出时,OpenMP 再把每个线程的部分和加在一起得到最终结果。

当然,OpenMP 不止能做累加,凡是累计运算都是可以的,如下表:

循环调度

负载均衡是多线程程序中对性能影响最大的因素了,只有实现了负载均衡才能保证所有的核心都是忙的,而不会出现空闲时间。如果没有负载均衡, 有一些线程会远远早于其他线程结束, 导致处理器空闲浪费优化的可能.

在循环中,经常会由于每次迭代的相差时间较大和破坏负载平衡。通常可以通过检查源码来发现循环的变动可能. 大多数情况下每次迭代可能会发现大概一致的时间,当这个条件不能满足的时候,你可能能找到一个花费了大概一致时间的子集。例如, 有时候所有偶数循环花费了和所有奇数循环一样的时间, 有时候可能前一半循环和后一半循环花费了相似的时间. 另一方面, 有时候你可能找不到花费相同时间的一组循环. 不论如何, 你应该把这些信息提供给 OpenMP, 这样才能让 OpenMP 有更好的机会去优化循环.

默认情况下,OpenMP认为所有的循环迭代运行的时间都是一样的,这就导致了OpenMP会把不同的迭代等分到不同的核心上,并且让他们分布的尽可能减小内存访问冲突,这样做是因为循环一般会线性地访问内存, 所以把循环按照前一半后一半的方法分配可以最大程度的减少冲突. 然而对内存访问来说这可能是最好的方法, 但是对于负载均衡可能并不是最好的方法, 而且反过来最好的负载均衡可能也会破坏内存访问. 因此必须折衷考虑.

OpenMP 负载均衡使用下面的语法

#pragma omp parallel for schedule(kind [, chunk size])

其中kind可以是下面的这些类型, 而 chunk size 则必须是循环不变的正整数

例子

#pragma omp parallel for
for (int i = 0; i < numElements; i++) {
array[i] = initValue;
initValue++;
}

显然这个循环里就有了竞态条件, 每个循环都依赖于 initValue 这个变量, 我们需要去掉它.

#pragma omp parallel for
for (int i = 0; i < numElements; i++) {
array[i] = initValue + i;
}

这样就可以了, 因为现在我们没有让 initValue 去被依赖

所以, 对于一个循环来说, 应该尽可能地把 loop-variant 变量建立在 i 上.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenMP入门 的相关文章

  • omp 的行为对嵌套并行级别至关重要

    考虑以下场景 函数A创建一层OMP并行区域 每个OMP线程调用函数B 函数B本身包含另一层OMP并行区域 那么 如果在函数 B 的并行区域内 存在一个 OMP 临界区域 那么 该区域对于函数 A 和 B 创建的所有线程来说是 全局 临界的
  • 在 #define 中使用 openmp pragma [重复]

    这个问题在这里已经有答案了 可能的重复 定义宏中的 C C 编译指示 https stackoverflow com questions 3030099 c c pragma in define macro 条件 pragma omp ht
  • OpenMP 中归约运算的执行顺序

    有没有办法知道 OpenMP 中归约运算符的执行顺序 换句话说 我想知道线程如何执行归约操作 是从左到右吗 当存在不是 2 的幂的数字时会发生什么 我想你会发现 OpenMP 只会减少关联操作 例如 and 如果您愿意 也可以是加法和乘法
  • OpenMP“master”编译指示不得包含在“parallel for”编译指示内

    为什么英特尔编译器不允许我指定 openmp 中的某些操作parallel for块应该仅由主线程执行吗 如果没有这种功能 我该如何实现我想要实现的目标 我想做的是通过并行回调更新进度条 long num items computed 0
  • 隐式私有控制循环变量

    我有疑问 并且在 OpenMP 文档中没有找到正确的答案 如果我有这样的循环 int i pragma omp parallel for for i 0 i lt 10 i do some stuff 是变量i隐式私有 我说得对吗 或者我必
  • 如何正确生成/恢复 OpenMP 未绑定任务?

    我编写了一个小型 C 程序来评估 OpenMP 在任务出现空闲时间 例如等待通信数据 时让出另一个任务的能力 include
  • OpenMp 与 IOS/Android 的兼容性

    我正在尝试做什么 我正在研究C c 为所有平台构建产品的代码 我操作系统 Android Windows 移动 桌面 Mac Linux 到目前为止我做了什么 是的 有许多在线链接讨论 OpenMp 与不同处理器和操作系统的兼容性 但很难从
  • 用于大型密集矩阵乘法的循环平铺/分块

    我想知道是否有人可以向我展示如何有效地使用循环平铺 循环阻塞进行大型密集矩阵乘法 我在做C AB具有 1000x1000 矩阵 我按照维基百科上的循环平铺示例进行操作 但使用平铺得到的结果比不使用平铺的结果更差 http en wikipe
  • C++11 随机数生成器的线程安全

    C 11 中有许多新的随机数生成器引擎和分布函数 它们线程安全吗 如果您在多个线程之间共享单个随机分布和引擎 是否安全并且您仍然会收到随机数吗 我正在寻找的场景是这样的 void foo std mt19937 64 engine stat
  • 使用 OpenMP 在两个内核上设置线程关联

    我使用的是C程序 在Windows7上用gcc 4 9 2编译 使用OpenMP 4 0 我的电脑是双核 四个线程 我想使用线程亲和力传播并使用放置在不同核心上的 2 个线程 因此 当我从 DOS 设置环境变量时 设置 OMP NUM TH
  • 顺序和并行版本给出不同的结果 - 为什么?

    我有一个嵌套循环 L 和 A 是完全定义的输入 pragma omp parallel for schedule guided shared L A reduction dummy for i k 1 i
  • 即使 num_threads(1) 时,openmp 的性能提升也难以理解

    下面几行代码 int nrows 4096 int ncols 4096 size t numel nrows ncols unsigned char buff unsigned char malloc numel unsigned cha
  • Qt 支持 OpenMP 吗?

    我在 Visual Studio 项目中使用 OpenMP 目前非常认真地考虑更改为 QT Creator Visual Studio 不是很糟糕吗 我对微软的期望更高 但无论如何 QT Creator 支持 OpenMP 吗 如果出现这种
  • 为什么 OpenMP SIMD 指令会降低性能?

    我正在学习如何在 OpenMP Fortran 中使用 SIMD 指令 我 写了简单的代码 program loop implicit none integer i j real 8 x x 0 0 do i 1 10000 do j 1
  • Gcc 卸载编译选项

    我正在尝试使用 Ubuntu 18 04 上的 gcc 10 CUDA 11 和此 CMakeLists txt 文件 或 OpenMP 版本 构建最简单的具有 GPU 卸载功能的 OpenMP 或 OpenACC C 程序 cmake m
  • Android OpenCV 并行化循环

    我知道 OpenMP 包含在 NDK 中 使用示例如下 http recursify com blog 2013 08 09 openmp on android http recursify com blog 2013 08 09 open
  • 使用 openmp 优化 N-queen

    我正在学习 OPENMP 并编写以下代码来解决 n 皇后问题 Full Code https github com Shafaet Codes blob master OPENMP Parallel 20N Queen 20problem
  • Pthreads 与 OpenMP

    我正在使用 Linux 用 C 创建一个多线程应用程序 我不确定是否应该使用 POSIX 线程 API 还是 OpenMP API 使用两者有何优缺点 Edit 有人可以澄清这两个 API 是否创建内核级 or 用户级线程 Pthreads
  • 并行迭代器

    我正在设计一个 C 数据结构 用于图形 供并行代码 使用 OpenMP 使用 假设我想要一个能够迭代所有元素 节点 的方法 当然 这个迭代将是并行的 是否可以使用迭代器来实现此目的 迭代器应该是什么样子才能实现并行访问 在这种情况下 您会建
  • 如何判断 OpenMP 是否正常工作?

    我正在尝试以并行模式运行 LIBSVM 但我的问题一般是在 OpenMP 中 根据LIBSVM 常见问题解答 http www csie ntu edu tw cjlin libsvm faq html f432 我已使用 pragma 调

随机推荐

  • 机器人知识体系

    纲 机电力算控感 知识体系体系各元素特点体系的建立和完善 机电力算控感 知识体系 机械 电子电气 力学 xff08 静力学与动力学分析 流体力学 材料力学等 xff09 计算 xff08 通用计算机和嵌入式计算机 xff09 控制理论 感知
  • OpenCV之imwrite()等基本操作

    参考 xff1a Opencv之imwrite 函数的用处 imwrite 函数用来保存图片 opencv3中的imwrite函数是用来输出图像到文件 xff0c 其声明如下 xff1a CV EXPORTS W bool imwrite
  • 麦克纳姆轮全向移动原理

    什么是麦克纳姆轮 在竞赛机器人和特殊工种机器人中 xff0c 全向移动经常是一个必需的功能 全向移动 意味着可以在平面内做出任意方向平移同时自转的动作 为了实现全向移动 xff0c 一般机器人会使用 全向轮 xff08 Omni Wheel
  • 卡尔曼滤波(KF)与扩展卡尔曼滤波(EKF)的一种理解思路及相应推导(1)

    前言 xff1a 从上个世纪卡尔曼滤波理论被提出 xff0c 卡尔曼滤波在控制论与信息论的连接上做出了卓越的贡献 为了得出准确的下一时刻状态真值 xff0c 我们常常使用卡尔曼滤波 扩展卡尔曼滤波 无迹卡尔曼滤波 粒子滤波等等方法 xff0
  • Qt Cmake添加*.qrc资源文件

    cmake minimum required VERSION 3 5 project Test LANGUAGES CXX 这里 file GLOB RECURSE QRC SOURCE FILES CMAKE CURRENT SOURCE
  • IOS 加载本地HTML

    web qtt以 folder形式添加到项目中 xff0c 注意是蓝色的颜色 创建swift项目 xff0c 写入如下代码 span class token comment span span class token comment Vie
  • C#实现:将十进制数转换为十六进制(含完整源码)

    C 实现 将十进制数转换为十六进制 含完整源码 在C 中 我们可以使用基础数据类型来存储整数值 如int long等 而十进制数是我们最常用的数制 但有些场景下需要将其转换为其它进制 如十六进制 本文将介绍如何使用C 来实现将十进制数转换为
  • 怎样用串口发送结构体-简单协议的封包和解包

    先说解决方案 xff0c 细节和实现代码都放在正文 下位机 xff1a 把结构体拆分成8位的整型数据 xff0c 加上数据包头和包尾 xff0c 然后按顺序单个单个地发出 xff1b 上位机 xff1a 把串口里的数据读取出来 xff0c
  • 计算机网络学习笔记——IP Header Checksum(校验和)的计算方法

    从TCP IP协议看到IP数据报 xff0c 看到Checksum的算法描述 xff0c 不甚了了 The checksum field is the 16 bit one s complement of the one s complem
  • 在Ubuntu18.04中更新指定python版本以及pip

    在Ubuntu18 04中更新指定python版本以及pip 更新指定python版本 xff08 eg python3 8 xff09 xff1a 参考 教你Ubuntu安装python3 7 xff0c 并更新python默认指向 xf
  • 【MATLAB数学建模编程实战】遗传算法求解最短路径(附代码及运行效果)

    欢迎关注 xff0c 本专栏主要更新MATLAB仿真 界面 基础编程 画图 算法 矩阵处理等操作 xff0c 拥有丰富的实例练习代码 xff0c 欢迎订阅该专栏 xff01 xff08 等该专栏建设成熟后将开始收费 xff0c 快快上车吧
  • stm32HAL库 串口接收不定长数据(DMA传输)

    相信大家很多初学者都会遇到串口接收不定长数据的情况 对于初学者可能看着有点难理解 xff0c 多看几遍就好 xff0c 亲测能用 话不多说上菜上菜 xff01 xff01 xff01 xff01 此代码是本人在具体工程应用 xff0c 实测
  • Flask - after_request 和 before_request

    目录 特殊的装饰器多个中间件怎么执行的 特殊的装饰器 64 app before request 在视图函数执行前执行 64 app after request 在视图函数执行后执行 span class token keyword fro
  • VScode 占用cpu风扇狂转, C/C++ IntelliSense Server for Visual Studio Code cpptools.exe占用cpu 30%

    点击下面那个红框中的东西 xff0c 然后选择暂停分析 cpu占用立马降下来了
  • 学习C++中遇到的各种问题

    拷贝构造函数到底是个是什么东西 xff1f 到底什么时候用const xff1f amp 是写在前还是写在后 xff1f 有区别 xff1f 为什么在析构函数中加了delete程序就会卡死 xff1f size t是个什么东西 xff1f
  • 【3D目标检测】稀疏卷积

    稀疏卷积实现部分 先说说实现部分 xff0c 对原理感兴趣的往后看 1 稀疏数据生成 这里的思路主要是先利用np meshgrid和np stack创建出稀疏数据补全后shape大小的点云坐标 xff0c 然后随机取前num points个
  • Unity3D之物体跟随鼠标移动和旋转

    void FixedUpdate if Input GetMouseButton 0 Vector3 aimPos 61 Camera main ScreenToWorldPoint new Vector3 Input mousePosit
  • 【寒武纪】视觉算法MLU220硬件适配(1)

    1 xff0c 环境搭建 xff1a MLU220快速上手指南 寒武纪开发者社区 安装硬件驱动和软件工具链 xff0c 也可以直接使用寒武纪官方开发平台 xff1a 寒武纪开发平台 本地开发安装完工具需要进行一些配置 xff1a 安装后配置
  • 【自动驾驶】second模型训练

    1 xff0c 数据组织 xff1a 训练验证数据生成 xff1a python create data py nuscenes data prep data path 61 NUSCENES TRAINVAL DATASET ROOT v
  • OpenMP入门

    OpenMP 是 Open MultiProcessing 的缩写 可以在 Visual Studio 或者 gcc 中使用 Hello World 把下面的代码保存为 omp cc include lt iostream gt inclu