OpenMp并行编程

目录

  • 介绍
  • 编译
  • 用法
    • >OpenMp parallel
    • >OpenMp for
    • >OpenMp private、firstprivate、lastprivate
    • >OpenMp section
    • >OpenMp reduction
    • >OpenMp single
    • >OpenMp master
    • >OpenMp barrier
  • OpenMp的API函数

介绍

OpenMp是一种并行编程模型,旨在简化多线程编程,它给开发人员提供了一种在共享内存系统中利用多个处理器并行执行任务。

OpenMp使用指令将一段串行代码标记为并行区域,告诉编译器这段代码需要并行执行。

OpenMp和PThread区别:

  • PThread需要显式明确各个线程的行为,OpenMp只需要简单的声明一段代码通过并行的方式去执行。
  • OpenMp是采用一种fork-join的执行模式,刚开始的时候只有一个主线程,当执行到OpenMp标记的这块串行代码时,它会派生出若干个线程去并行执行任务,当执行结束后,分支线程会合,将控制流程交给主线程。

编译

包含头文件omp.h

Windows下使用vs开发时,右键项目 -》属性 -》C/C++ -》语言 -》OpenMp支持 -》选择是

用法

>OpenMp parallel

parallerl 指令用于创建一个并行域,告诉编译器这段代码我要并行执行

#include <iostream>
#include <omp.h>
#include <chrono>void Func()
{int length = 5;// num_thread表示创建几个线程执行,如果不指定就创建最大线程数// omp_get_thread_num()打印当前线程id
#pragma omp parallel num_threads(3){for (int i = 0; i < length; i++)std::cout << i << ": " << omp_get_thread_num() << std::endl;}
}int main()
{auto start = std::chrono::high_resolution_clock::now();Func();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);std::cout << "执行时间:" << duration.count() << std::endl;system("pause");return 0;
}

输出:

0: 0
1: 0
2: 0
3: 0
4: 0
0: 1
1: 1
2: 1
3: 1
4: 1
0: 2
1: 2
2: 2
3: 2
4: 2
执行时间:3659

可以看到每个线程都执行了五次循环,耗时3659us

>OpenMp for

#pragma omp parallel for 告诉编译器接下来的for循环要并行执行,使用的时候需要满足以下四点:

  • 在循环的迭代器必须是可计算的并且在执行前就需要确定迭代的次数;
  • 在循环的代码块中不能包含break,return,exit;
  • 在循环的代码块中不能使用goto跳出到循环外部;
  • 迭代器只能够被for语句中的增量表达式所修改。
#include <iostream>
#include <omp.h>
#include <chrono>void Func()
{int length = 5;#pragma omp parallel for num_threads(3)for (int i = 0; i < length; i++)std::cout << i << ": " << omp_get_thread_num() << std::endl;
}int main()
{auto start = std::chrono::high_resolution_clock::now();Func();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);std::cout << "执行时间:" << duration.count() << std::endl;system("pause");return 0;
}

输出:

0: 0
1: 0
4: 2
2: 1
3: 1
执行时间:2817

可以看到3个线程共执行了五次循环,耗时2817us

>OpenMp private、firstprivate、lastprivate

它们用于给每个线程创建共享变量的副本,以确保每个线程都有自己的私有变量,不会产生竞争关系。

#include <iostream>
#include <omp.h>
#include <chrono>void Func()
{int length = 5;std::cout << "----------------------private用法--------------------" << std::endl;
#pragma omp parallel private(length) num_threads(3){int tid = omp_get_thread_num();length *= 10 * tid;std::cout << "线程" << tid << ": " << length << std::endl;}std::cout << "主线程: " << length << std::endl;std::cout << "----------------------firstprivate用法--------------------" << std::endl;
#pragma omp parallel firstprivate(length) num_threads(3){int tid = omp_get_thread_num();length *= 10 * tid;std::cout << "线程" << tid << ": " << length << std::endl;}std::cout << "主线程: " << length << std::endl;std::cout << "----------------------lastprivate用法--------------------" << std::endl;
#pragma omp parallel for lastprivate(length) num_threads(3)for(int i = 0;i < 5;i++){length = 5;length++;std::cout << "线程" << omp_get_thread_num() << ": " << length << std::endl;}std::cout << "主线程: " << length << std::endl;
}int main()
{auto start = std::chrono::high_resolution_clock::now();Func();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);std::cout << "执行时间:" << duration.count() << std::endl;system("pause");return 0;
}

输出:

----------------------private用法--------------------
线程0: 0
线程1: 0
线程2: 0
主线程: 5
----------------------firstprivate用法--------------------
线程0: 0
线程2: 100
线程1: 50
主线程: 5
----------------------lastprivate用法--------------------
线程0: 6
线程0: 6
线程2: 6
线程1: 6
线程1: 6
主线程: 6
执行时间:4754

可以看到,private用法中length在线程中的初始化值为0。

firstprivate选项:
关于firstprivate的信息:

  • firstprivate选项告诉编辑器私有变量在第一个循环会继承共享变量的值;
  • 这个私有的变量只会在每个线程的第一个循环继承,而不会在每个循环中继承;
  • 其使用方法于private几乎一致:#pragma omp parallel for firstprivate;

关于变量的拷贝:

  • 如果数据是基础数据类型,如int,double等,会将数据进行直接拷贝
  • 如果变量是一个数组,它会拷贝一个对应的数据以及大小到私有内存中;
  • 如果变量为指针,它会将变量指向的地址拷贝过去,指向相同地址;
  • 如果变量是一个类的实例,它会调用对应的构造函数构造一个私有的变量。

lastprivate选项告诉编辑器

  • 私有变量会在最后一个循环出去的时候,用私有变量的值替换掉我们共享变量的值;
  • 当负责最后一个iteration的线程离开循环的时候,它会将该私有变量的值赋值给当前共享变量的值。

>OpenMp section

OpenMP 的 section 是在并行代码中实现任务划分和分配的一种机制。section 用于将操作划分为多个独立的部分,这些部分可以被并行执行。

#include <iostream>
#include <omp.h>
#include <chrono>void Func()
{int length = 5;#pragma omp parallel num_threads(3){#pragma omp sections{#pragma omp section{int threadID = omp_get_thread_num();std::cout << "Thread " << threadID << ", Section 1" << std::endl;}#pragma omp section{int threadID = omp_get_thread_num();std::cout << "Thread " << threadID << ", Section 2" << std::endl;}#pragma omp section{int threadID = omp_get_thread_num();std::cout << "Thread " << threadID << ", Section 3" << std::endl;}}}
}int main()
{auto start = std::chrono::high_resolution_clock::now();Func();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);std::cout << "执行时间:" << duration.count() << std::endl;system("pause");return 0;
}

输出:

Thread 0, Section 1
Thread 1, Section 3
Thread 2, Section 2
执行时间:4200

通过 sections 和 section 的组合,我们可以将任务细分为多个独立的部分,让不同的线程并行地执行这些部分。这种方式可以在并行计算中提高效率,并充分利用多核处理器的性能。注意,sections 中的工作的划分是静态的,编译时就确定了各个 section 的分配情况,而不会根据实际运行时的情况动态调整。

>OpenMp reduction

reduction 是一种用于对多个线程私有变量进行合并操作的机制。它可以方便地进行诸如求和、求积、求最大值、求最小值等归约操作。

#include <iostream>
#include <omp.h>
#include <chrono>void Func()
{int length = 5;#pragma omp parallel num_threads(3) reduction(+:length){#pragma omp sections{#pragma omp section{int threadID = omp_get_thread_num();length = length + 10;std::cout << "Thread " << threadID << ": " << length << std::endl;}#pragma omp section{int threadID = omp_get_thread_num();length = length + 20;std::cout << "Thread " << threadID << ": " << length << std::endl;}#pragma omp section{int threadID = omp_get_thread_num();length = length + 30;std::cout << "Thread " << threadID << ": " << length << std::endl;}}}std::cout << "主线程: " << length << std::endl;
}int main()
{auto start = std::chrono::high_resolution_clock::now();Func();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);std::cout << "执行时间:" << duration.count() << std::endl;system("pause");return 0;
}

输出:

Thread 0: 10
Thread 1: 30
Thread 2: 20
主线程: 65
执行时间:1534

reduction 还支持其他操作符,如 -、*、max、min 等。

>OpenMp single

single 是 OpenMP 指令中的一个子句,它用于指定只有一个线程执行 single 区域中的代码。这个区域中的代码在并行环境下只会被一个线程执行,其他线程会跳过这部分代码。

#include <iostream>
#include <omp.h>
#include <chrono>void Func()
{int length = 5;#pragma omp parallel num_threads(3) firstprivate(length){#pragma omp single{length = 1;}#pragma omp barrierstd::cout << "Thread " << omp_get_thread_num() << ": " << length << std::endl;}std::cout << "主线程: " << length << std::endl;
}int main()
{auto start = std::chrono::high_resolution_clock::now();Func();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);std::cout << "执行时间:" << duration.count() << std::endl;system("pause");return 0;
}

输出:

Thread 2: 5
Thread 0: 1
Thread 1: 5
主线程: 5
执行时间:2233

在这个例子中,single 子句定义了一个代码块,它只会由一个线程执行,在代码块中,这个线程将变量 x 的值设置为1。然后使用 barrier 指令同步所有线程,确保在输出 x 前,single 区域中的代码已经执行完毕。执行完后,每个线程输出变量 x 的值,这时只有一个线程的 x 值为 1,其他线程的 x 值为 0。

需要注意的是,single 指令只保证至多一个线程执行这部分代码,但并不能保证是哪个线程执行,也不能保证是同一个线程执行。如果需要指定具体的线程执行代码,可以使用 master 指令。

>OpenMp master

master子句告诉编译器接下来紧跟的下段代码将会会由主线程执行,它不会出现等待现象。

>OpenMp barrier

barrier 是一个 OpenMP 中的指令,用于同步并行区域中的线程。它的作用是确保在指定的并行区域内的所有线程都到达 barrier 指令之前不会继续执行下去,直到所有线程都到达 barrier 之后才会继续执行后面的代码。

barrier 指令可以用来处理需要等待其他线程执行完毕才能继续执行的情况,例如需要确保某些共享数据的一致性或者需要线程之间进行数据交换等。

在使用 barrier 指令时,需要注意以下几点:

1、barrier 指令必须在并行区域内使用,否则会被忽略。
2、所有参与并行区域的线程都必须到达 barrier 之前的位置,否则可能导致死锁或其他错误。
3、barrier 之后的代码只有在所有线程都到达 barrier 之后才会执行,因此应谨慎使用 barrier,避免出现线程之间的不平衡。
4、在嵌套的并行区域中,每个并行区域都有自己的 barrier,因此需要确保每个并行区域内的所有线程都正确地使用了 barrier。

#include <iostream>
#include <omp.h>
#include <chrono>void Func()
{int length = 5;#pragma omp parallel num_threads(4){int thread_num = omp_get_thread_num();std::cout << "Thread " << thread_num << " before barrier" << std::endl;#pragma omp barrierstd::cout << "Thread " << thread_num << " after barrier" << std::endl;}
}int main()
{auto start = std::chrono::high_resolution_clock::now();Func();auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);std::cout << "执行时间:" << duration.count() << std::endl;system("pause");return 0;
}

输出:

Thread 0 before barrier
Thread 2 before barrier
Thread 3 before barrier
Thread 1 before barrier
Thread 1 after barrier
Thread 0 after barrier
Thread 2 after barrier
Thread 3 after barrier
执行时间:2670

在这个示例中,我们创建了一个并行区域,使用了 num_threads(4) 指定了并行区域中的线程数量为4。在每个线程中,我们输出了线程号并在 barrier 前后进行了打印。运行这段代码可以看到,所有线程都会在 barrier 处等待,直到所有线程都到达 barrier,然后继续执行后面的代码。

OpenMp的API函数

函数名函数作用
omp_set_num_threads(n)设置 OpenMP 并行区域中的线程数量为 n
omp_get_num_threads()获得正在运行的并行区域的线程数量。
omp_get_thread_num()获得当前线程的线程号。
omp_get_max_threads()获得可以用于并行执行的最大线程数。
omp_get_num_procs()获得当前计算机中可用于并行计算的处理器数目。
omp_set_dynamic(boolean)设置或取消运行时动态调整线程数量调度。
omp_set_nested(boolean)启用或禁用嵌套并行执行。
omp_set_num_threads设置后续并行域中的线程格式
omp_in_parallel判断当前是否在并行域中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/171571.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Spring集成MyBatis】MyBatis注解开发

文章目录 1. MyBatis的常用注解2. 基于注解的MyBatis增删改查增删改查完整代码加载映射关系测试代码 3. MyBatis的注解实现复杂映射开发一对一操作的实现一对一操作实现的第二种方式一对多操作的实现多对多操作实现 1. MyBatis的常用注解 2. 基于注解的MyBatis增删改查 使用注…

Linux加强篇004-Vim编辑器与Shell命令脚本

目录 前言 1. Vim文本编辑器 1.1 编写简单文档 1.2 配置主机名称 1.3 配置网卡信息 1.4 配置软件仓库 2. 编写Shell脚本 2.1 编写简单的脚本 2.2 接收用户的参数 2.3 判断用户的参数 3. 流程控制语句 3.1 if条件测试语句 3.2 for条件循环语句 3.3 while条件循环语…

【开源】基于JAVA的高校学院网站

项目编号&#xff1a; S 020 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S020&#xff0c;文末获取源码。} 项目编号&#xff1a;S020&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学院院系模块2.2 竞赛报名模块2.3 教…

Postman如何使用(三):使用数据文件

数据文件是非常强大的方式使用不同的测试数据来测试我们的API&#xff0c;以检查它们是否在各种情况下都能正常运行。我们可以认为数据文件是“Collection Runner”中每个请求的参数。下面&#xff0c;我们通过一个例子来说明如何使用数据文件。 这篇文章需要结合下面两个文件进…

史上最全前端知识点+高频面试题合集,十二大专题,命中率高达95%

前言&#xff1a; 下面分享一些关于阿里&#xff0c;美团&#xff0c;深信服等公司的面经&#xff0c;供大家参考一下。大家也可以去收集一些其他的面试题&#xff0c;可以通过面试题来看看自己有哪里不足。也可以了解自己想去的公司会问什么问题&#xff0c;进行有针对的复习。…

PowerShell基础

1. Tab键补全 有时候不记得指令全称&#xff0c;只记得开头几个字母&#xff0c;使用Tab键可显式建议选项&#xff0c;再次按Tab可以往后翻&#xff0c;ShiftTab可以往前翻。 2. 查看指令类型 Get-Command -Name Get-Alias 指令是遵循一定的格式规范的&#xff0c;如动词加名…

css之选择第一个或最后一个元素、第n个标签、选择偶数或奇数标签、选择最后n个标签、等差数列标签的选择、first、last、nth、child

MENU first-child选择列表中的第一个标签last-child选择列表中的最后一个标签nth-child(n)选择列表中的第n个标签nth-child(2n)选择列表中的偶数位标签nth-child(2n-1)选择列表中的奇数位标签nth-child(nm)选择从第m个到最后一个标签nth-child(-nm)选择从第1个到第m个nth-last-…

Python与设计模式--桥梁模式

11-Python与设计模式–桥梁模式 一、画笔与形状 在介绍原型模式的一节中&#xff0c;我们举了个图层的例子&#xff0c;这一小节内容&#xff0c;我们同样以类似画图的例子&#xff0c; 说明一种结构类设计模式&#xff1a;桥梁模式。 在一个画图程序中&#xff0c;常会见到这…

《数据结构与算法之美》读书笔记2

链表操作的技巧 1.理解指针 将摸个变量赋值给指针&#xff0c;实际上就是将这个变量的地址赋给指针&#xff0c;或者&#xff0c;指针中存储了这个变量的地址&#xff0c;指向了这个变量&#xff0c;所以可以通过指针找到这个变量。 2.内存泄漏或指针丢失 删除链表节点时&a…

人工智能|机器学习——循环神经网络的简洁实现

循环神经网络的简洁实现 如何使用深度学习框架的高级API提供的函数更有效地实现相同的语言模型。 我们仍然从读取时光机器数据集开始。 import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2lbatch_size, num_steps 32, 35 t…

itop4412移植lrzsz工具踩坑笔记

4412开发板在传输文件一直用的都是tftp文件传输&#xff0c;但这样效率有点慢&#xff0c;平常在linux上习惯用lrzsz工具来传输文件&#xff0c;特此记录下&#xff0c;因为不熟悉linux编译 踩坑了很多地方 在操作前 我们的虚拟机要线安装好编译环境 下载lrzsz源码&#xff0…

一起学docker系列之十docker安装tomcat实践

目录 前言1 安装tomcat的步骤步骤 1: 查找并拉取 Tomcat 镜像步骤 2: 运行 Tomcat 容器步骤 3: 管理 Tomcat 容器步骤 4: 访问 Tomcat 首页 2 解决访问首页的404访问不到的问题2.1 Tomcat 10 的默认设置问题2.2 端口映射或防火墙问题 3 推荐使用 Tomcat 8.5 版本总结 前言 当安…

最轻量级最完整的屏幕适配完全适配各个手机方案

当你看到这篇博客的时候,说明你已经迈出了惊人的一步,已经慢慢进入高级资深开发工程师行列了,这是开发之路必备技能。 当你接到一个任务时,每天按照需求原型、设计师UI图立刻积极的开发完成后,满满的兴高采烈去打包提测,板凳还没做安稳,测试人员就提了一个又一个的BUG,…

【华为OD题库-037】跳房子2-java

题目 跳房子&#xff0c;也叫跳飞机&#xff0c;是一种世界性的儿童游戏游戏。参与者需要分多个回合按顺序跳到第1格直到房子的最后一格&#xff0c;然后获得一次选房子的机会&#xff0c;直到所有房子被选完&#xff0c;房子最多的人获胜。 跳房子的过程中&#xff0c;如果有踩…

【Docker】从零开始:11.Harbor搭建企业镜像仓库

【Docker】从零开始&#xff1a;11.Harbor搭建企业镜像仓库 1. Harbor介绍2. 软硬件要求(1). 硬件要求(2). 软件要求 3.Harbor优势4.Harbor的误区5.Harbor的几种安装方式6.在线安装(1).安装composer(2).配置内核参数,开启路由转发(3).下载安装包并解压(4).创建并修改配置文件(5…

element-ui DatePicker 日期选择器-控制选择精确到时分秒-禁止选择今天之前-或者今天之后日期### 前言

前言 最近在使用芋道框架时候&#xff0c;后端使用生成代码&#xff0c;时间因为类型问题&#xff0c;只能是时间戳&#xff0c;否则为空&#xff08;1970-&#xff09; 前端其实很简单只要在日期选择器把类型改成时间错即可&#xff0c;但根据业务需求需要精确到时分秒 把时…

python+pytest接口自动化(1)-接口测试基础

一般我们所说的接口即API&#xff0c;那什么又是API呢&#xff0c;百度给的定义如下&#xff1a; API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;是一些预先定义的接口&#xff08;如函数、HTTP接口&#xff09;&#xff0c;或指软件系…

3款免费的语音视频转文本AI神器

最近有很多粉丝让我出一期关于语音转文本的免费AI神器&#xff0c;毕竟这类工具在学习和工作中经常会用到&#xff0c;那今天就给大家安排。 我亲测了好几款软件之后&#xff0c;最终评选留下了三款 剪映hugging face飞书妙记 接下来一一给大家讲解 1.剪映 剪映其实是一款视…

什么是proxy代理?

1. 什么是proxy代理 代理&#xff08;Proxy&#xff09;是 JavaScript 中一种非常强大而灵活的功能。代理允许你拦截并覆盖对象的默认行为&#xff0c;提供了一种拦截、定制和扩展对象操作的机制。 简单说&#xff0c;就是在访问对象属性或者赋值时&#xff0c;可以做一些额外…

引用、动态内存分配、函数、结构体

引用 定义和初始化 **数据类型 &引用名 目标名;**引用和目标共用同一片空间&#xff08;相当于对一片空间取别名&#xff09;。 引用的底层实现&#xff1a;数据类型 * const p&#xff1b; ------> 常指针 int const *p; -----> 修饰 *p const int *p; ----->…