2020-10-19

2023-05-16

OpenMP 使用介绍

  1. OpenMP 基本概念
    Open Multi-Processing的缩写,是一个应用程序接口(API),可用于显式指导多线程、共享内存的并行性。
    在项目程序已经完成好的情况下不需要大幅度的修改源代码,只需要加上专用的pragma来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。当选择忽略这些pragma,或者编译器不支持OpenMp时,程序又可退化为通常的程序(一般为串行),代码仍然可以正常运作,只是不能利用多线程来加速程序执行。OpenMP提供的这种对于并行描述的高层抽象降低了并行编程的难度和复杂度,这样程序员可以把更多的精力投入到并行算法本身,而非其具体实现细节。对基于数据分集的多线程程序设计,OpenMP是一个很好的选择。
    OpenMP支持的语言包括C/C++、Fortran;而支持OpenMP的编译器VS、gcc、clang等都行。可移植性也很好:Unix/Linux和Windows

  2. OpenMP编程模型
      内存共享模型:OpenMP是专为多处理器/核,共享内存机器所设计的。底层架构可以是UMA和NUMA。即(Uniform Memory Access和Non-Uniform Memory Access)

2.1基于线程的并行性
• OpenMP仅通过线程来完成并行
• 一个线程的运行是可由操作系统调用的最小处理单
• 线程们存在于单个进程的资源中,没有了这个进程,线程也不存在了
• 通常,线程数与机器的处理器/核数相匹配,然而,实际使用取决与应用程序
2.2明确的并行
• OpenMP是一种显式(非自动)编程模型,为程序员提供对并行化的完全控制
• 一方面,并行化可像执行串行程序和插入编译指令那样简单
• 另一方面,像插入子程序来设置多级并行、锁、甚至嵌套锁一样复杂
2.3 Fork-Join模型

• OpenMP就是采用Fork-Join模型
• 所有的OpenML程序都以一个单个进程——master thread开始,master threads按顺序执行知道遇到第一个并行区域
• Fork:主线程创造一个并行线程组
• Join:当线程组完成并行区域的语句时,它们同步、终止,仅留下主线程
2.4 数据范围
• 由于OpenMP时是共享内存模型,默认情况下,在共享区域的大部分数据是被共享的
• 并行区域中的所有线程可以同时访问这个共享的数据
• 如果不需要默认的共享作用域,OpenMP为程序员提供一种“显示”指定数据作用域的方法
2.5嵌套并行
• API提供在其它并行区域放置并行区域
• 实际实现也可能不支持
2.6动态线程
• API为运行环境提供动态的改变用于执行并行区域的线程数
• 实际实现也可能不支持

3.openmp使用
  需要使用openmp就需要引入omp.h库文件。然后在编译时添加参数 -fopenmp即可。 在具体需要进行并行运算的部分,使用 #pragma omp 指令[子句] 来告诉编译器如何并行执行对应的语句。 常用的指令如下:
parallel:用在一个结构块之前,表示这段代码将被多个线程并行执行;
for:用于for循环语句之前,表示将循环计算任务分配到多个线程中并行执行,以实现任务分担,必须由编程人员自己保证每次循环之间无数据相关性;parallel for:parallel和for指令的结合,也是用在for循环语句之前,表示for循环体的代码将被多个线程并行执行,它同时具有并行域的产生和任务分担两个功能;
sections:用在可被并行执行的代码段之前,用于实现多个结构块语句的任务分担,可并行执行的代码段各自用section指令标出(注意区分sections和section);
parallel sections:parallel和sections两个语句的结合,类似于parallel for;
single:用在并行域内,表示一段只被单个线程执行的代码;
critical:用在一段代码临界区之前,保证每次只有一个OpenMP线程进入;
flush:保证各个OpenMP线程的数据影像的一致性;
barrier:用于并行域内代码的线程同步,线程执行到barrier时要停下等待,直到所有线程都执行到barrier时才继续往下执行;
atomic:用于指定一个数据操作需要原子性地完成;
master:用于指定一段代码由主线程执行;
threadprivate:用于指定一个或多个变量是线程专用,后面会解释线程专有和私有的区别。
  常用的子句如下:
private:指定一个或多个变量在每个线程中都有它自己的私有副本;
firstprivate:指定一个或多个变量在每个线程都有它自己的私有副本,并且私有变量要在进入并行域或任务分担域时,继承主线程中的同名变量的值作为初值;
lastprivate:是用来指定将线程中的一个或多个私有变量的值在并行处理结束后复制到主线程中的同名变量中,负责拷贝的线程是for或sections任务分担中的最后一个线程;
reduction:用来指定一个或多个变量是私有的,并且在并行处理结束后这些变量要执行指定的归约运算,并将结果返回给主线程同名变量;
nowait:指出并发线程可以忽略其他制导指令暗含的路障同步;
num_threads:指定并行域内的线程的数目;
schedule:指定for任务分担中的任务分配调度类型;
shared:指定一个或多个变量为多个线程间的共享变量;
ordered:用来指定for任务分担域内指定代码段需要按照串行循环次序执行;
copyprivate:配合single指令,将指定线程的专有变量广播到并行域内其他线程的同名变量中;
copyin:用来指定一个threadprivate类型的变量需要用主线程同名变量进行初始化;
default:用来指定并行域内的变量的使用方式,缺省是shared  
  另外,openmp还提供了一些列的api函数来获取并行线程的状态或控制并行线程的行为,常用的OpenMP API函数以及说明:
omp_in_parallel - 判断当前是否在并行域中。
omp_get_thread_num - 获取线程号
omp_set_num_threads - 设置并行域中线程格式
omp_get_num_threads - 返回并行域中线程数
omp_get_dynamic - 判断是否支持动态改变线程数目
omp_get_max_threads - 获取并行域中可用的最大的并行线程数目
omp_get_num_procs - 返回系统中处理器的个数  
  
环境变量
OpenMP中定义一些环境变量,可以通过这些环境变量控制OpenMP程序的行为,常用的环境变量:
OMP_SCHEDULE:用于for循环并行化后的调度,它的值就是循环调度的类型;
OMP_NUM_THREADS:用于设置并行域中的线程数;
OMP_DYNAMIC:通过设定变量值,来确定是否允许动态设定并行域内的线程数;
OMP_NESTED:指出是否可以并行嵌套。
4.openmp 实例1

#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <time.h>

using namespace std;

const int NumChunks       = 8;
const int ChunkSize       = 1024*16;
const int NumElements     = NumChunks*ChunkSize;

#define RAND_MAX_A 64
#define RAND_MAX_B 256
#define EPISILON  0.00001

float srcA  [NumElements];
float srcB  [NumElements];
float dst   [NumElements];
float Golden[NumElements];

void vadd_openmp(float *a, float *b, float *c, int size)
{
    #pragma omp target map(to:a[0:size],b[0:size]) map(from:c[0:size])   
    {
        int i;
        #pragma omp parallel for
        for (i = 0; i < size; i++)
            c[i] = a[i] + b[i];

    }

}

#pragma omp declare target
void compute(float *a, float *b, float *c, int si, int size);
#pragma omp end declare target

void vadd_openmp_t(float *a, float *b, float *c, int NumChunks, int ChunkSize)
{
    int NumElements = NumChunks*ChunkSize;
#pragma omp target map(to:a[0:NumElements],b[0:NumElements],NumChunks,ChunkSize) \
                   map(from:c[0:NumElements])
    {
        int i;
        #pragma omp parallel
        {
            #pragma omp master
            for (i = 0; i < NumChunks; i++)
            {
                int start_index = i * ChunkSize;
                #pragma omp task firstprivate (start_index, ChunkSize)
                compute(a, b, c, start_index, ChunkSize);
            }    
        }
    }
}

void compute(float *a, float *b, float *c, int si, int size)
{
    int i;
    for (i = si; i < si+size; i++)
        c[i] = a[i] + b[i];
}

int main()
{
    int    num_errors = 0;
    const int    print_nerrors = 12;
    
    /* ---------------------------------------------------------------- */
    /*  Initialized input arrays with random test data.                 */
    /* ---------------------------------------------------------------- */
    /* Initialize Random Number Seed */
    srand(time(NULL));
    
    for (int i=0; i < NumElements; ++i) 
    {
        srcA[i] = (rand() % RAND_MAX_A + 1) * 1.0; 
        srcB[i] = (rand() % RAND_MAX_B + 1) * 1.0;
        dst[i] = 0;
        Golden[i]   =   srcA[i] + srcB[i];
    }   

    /* ---------------------------------------------------------------- */
    /* Call vadd_openmp target code                                     */
    /* ---------------------------------------------------------------- */
    vadd_openmp(srcA,srcB,dst,NumElements);
    
    /* ---------------------------------------------------------------- */
    /* Perform error checking                                           */
    /* ---------------------------------------------------------------- */
    for (int i=0; i < NumElements; ++i)
    {
        if (Golden[i] - dst[i] < -EPISILON || Golden[i] - dst[i] > EPISILON) 
        { 
            if((num_errors += 1) < print_nerrors)
                printf("Error %d: %f <==> %f\n", i, Golden[i], dst[i]);
        }
    }
    if (num_errors > 0)  cout << "FAIL with " << num_errors << " errors!\n";
    else                  cout << "PASS!" << endl; 

    /* ---------------------------------------------------------------- */
    /* Call vadd_openmp target code                                     */
    /* ---------------------------------------------------------------- */
    vadd_openmp_t(srcA,srcB,dst,NumChunks, ChunkSize);
    
    /* ---------------------------------------------------------------- */
    /* Perform error checking                                           */
    /* ---------------------------------------------------------------- */
    for (int i=0; i < NumElements; ++i)
    {
        if (Golden[i] - dst[i] < -EPISILON || Golden[i] - dst[i] > EPISILON) 
        { 
            if((num_errors += 1) < print_nerrors)
                printf("Error %d: %f <==> %f\n", i, Golden[i], dst[i]);
        }
    }
    if (num_errors > 0)  cout << "FAIL with " << num_errors << " errors!\n";
    else                  cout << "PASS!" << endl; 

    return 0;
}

5.openmp 实例2

#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <time.h>

using namespace std;

const int NumElements     = 8*1024;

#define RAND_MAX_A 64
#define RAND_MAX_B 256
#define EPISILON  0.00001

float srcA  [NumElements];
float srcB  [NumElements];
float dst   [NumElements];
float Golden[NumElements];

void vadd_sub_section(float *a, float *b, float *c, int size)
{
    int start = 0;
    #pragma omp target data map(to:a[start:size], b[start:size]) map(tofrom:c[start:size])
    {
        /* At this point a,b have been copied to the device and space for c has also 
         * been allocated on the device */
        int i;
        int chunk_size = size/2;
        /* Operate on half of Array's a and b to generate half of c 
         * At this point, sub-sections are created out of the parent array sections a,b,c */
        #pragma omp target map(to:a[start:chunk_size], b[start:chunk_size]) \
                           map(tofrom:c[start:chunk_size])
        {
            #pragma omp parallel for private(i)
            for (i = start; i < start+chunk_size; i++)
                c[i] = a[i] + b[i];
        }
        
        /* Operate on the other half, simply by incrementing the start offset */
        start = chunk_size;
        #pragma omp target map(to:a[start:chunk_size], b[start:chunk_size]) \
                           map(tofrom:c[start:chunk_size])
        {
            #pragma omp parallel for private(i)
            for (i = start; i < start+chunk_size; i++)
                c[i] = a[i] + b[i];
        }
    }
}

int main()
{
    int    num_errors = 0;
    const int    print_nerrors = 12;
    
    /* ---------------------------------------------------------------- */
    /*  Initialized input arrays with random test data.                 */
    /* ---------------------------------------------------------------- */
    /* Initialize Random Number Seed */
    srand(time(NULL));
    
    for (int i=0; i < NumElements; ++i) 
    {
        srcA[i] = (rand() % RAND_MAX_A + 1) * 1.0; 
        srcB[i] = (rand() % RAND_MAX_B + 1) * 1.0;
        dst[i] = 0;
        Golden[i]   =   srcA[i] + srcB[i];
    }   

    /* ---------------------------------------------------------------- */
    /* Call vadd_sub_section                                            */
    /* ---------------------------------------------------------------- */
    vadd_sub_section(srcA,srcB,dst,NumElements);
    
    /* ---------------------------------------------------------------- */
    /* Perform error checking                                           */
    /* ---------------------------------------------------------------- */
    for (int i=0; i < NumElements; ++i)
    {
        if (Golden[i] - dst[i] < -EPISILON || Golden[i] - dst[i] > EPISILON) 
        { 
            if((num_errors += 1) < print_nerrors)
                printf("Error %d: %f <==> %f\n", i, Golden[i], dst[i]);
        }
    }
    if (num_errors > 0)  cout << "FAIL with " << num_errors << " errors!\n";
    else                  cout << "PASS!" << endl; 

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

2020-10-19 的相关文章

  • 8、docker+k8s+kubesphere:nfs安装(2020-08-02更新)

    8 docker 43 k8s 43 kubesphere nfs安装 server端安装在node151 yum y span class token function install span nfs utils rpcbind 配置文
  • 9、docker+k8s+kubesphere:Kubernetes安装(2020-08-02更新)

    9 docker 43 k8s 43 kubesphere Kubernetes安装 官网说明一定详细查看 span class token punctuation span 本文用的是2 1 1 span class token punc
  • 2020阿里云学生服务器操作步骤!

    前言 年龄在12岁 24岁之间的大陆个人实名认证用户 和 大陆全日制在校大学生在学生认证有效期内 xff0c 满足上述任一条件即可享受优惠价格 xff0c 同一用户只能保有一台学生优惠弹性计算产品 xff0c 一台数据库RDS产品 xff0
  • 2020年acwifi拆过的无线路由器配置汇总

    转载自 xff1a https www smyz net luyouqi 10990 html 2020年acwifi拆过的无线路由器配置汇总 2020 12 31 22 05 整合编辑 xff1a 64 数码一族 acwifi只是一个人
  • 【新书推荐】【2020】无人机系统设计

    从系统角度全面介绍无人飞机系统的设计和分析 Provides a comprehensive introduction to the design and analysis of unmanned aircraft systems with
  • 北航2系921 2021考研历年真题及参考答案(2020-2004)

    需要自取 百度网盘 提取码 xff1a iwbg 关于2020北航921试题 相信大家都听说了 xff0c 2020年的921试题整体难度较2019年小 2019考完后 xff0c 群里面怨声载道 xff0c 信号10年没考电路题了怎么就今
  • 12-IDEA配置JDK版本(2020.2.3版本)

    1 配置当前项目的JDK版本 File gt Project Structure gt Project SDKs xff0c 也可以直接点击右上角的图标 2 配置之后创建的新项目JDK版本 类似于全局配置 File gt New Proje
  • 2020-12-22

    云计算主要包含哪些关键技术 xff1f 1 虚拟化技术 xff1a 云计算的虚拟化技术不同于传统的单一虚拟化 xff0c 它是涵盖整个IT架构的 xff0c 包括资源 网络 应用和桌面在内的全系统虚拟化 xff0c 它的优势在于能够把所有硬
  • 2020-11-12

    一 什么是PID PID控制器是工业过程控制中广泛采用的一种控制算法 xff0c 其特点是结构简单灵活 技术成熟 适应性强 P I D分别为比例 xff08 Proportion xff09 积分 xff08 Integral xff09
  • 六级(2020/7-1) Text1

    People often discuss the dangers of too much stress xff0c but lately最近 a very different view of stress is gaining popula
  • 2020-12-29 Matlab自动化控制-Adrc自抗扰控制参数调节

    Matlab自动化控制 Adrc自抗扰控制参数调节 上篇参看 xff1a https blog csdn net qingfengxd1 article details 111830762 以最简单的线性组合方法 xff08 1 xff09
  • pycharm安装matplotlib失败(2020最新)

    欢迎关注 xff1a 天际使徒的个人博客 请修复网络 使用国内镜像速度会快很多 xff1a 临时使用 xff1a pip install i https pypi tuna tsinghua edu cn simple some packa
  • 【亲测可用】kali linux 2020.1 设置为中文方法

    目录 0x00 提示0x01 更换更新源0x02 默认语言选择0x03 安装中文字体0x04 重启 xff0c 完成0x05 参考文章 kali 2020 1可用 进入我们的正题 xff0c 修改为中文的步骤 0x00 提示 由于kali
  • 2020/05/25 Prometheus监控k8s企业级应用 1

    2 2 课程介绍及课程大纲 普罗米修斯的配置很难 2 3 Prometheus监控软件概述 prometheus是一名google的前员工写的 xff0c 也是go语言写的 xff0c K8S是第一个托管的项目 xff0c promethe
  • 2020-11-13

    三角翼无人机制作指南设计 一 接线 飞控 xff1a pixhawk 915MHz电台 xff0c 空速计 xff0c GPS xff1b 动力 xff1a Tmotor电机 电调 F150KV700 舵机 xff1a S43A标准mini
  • 【slam-2020-01-02】扩展应用

    一篇比较全面的slam博客 一 VR 43 AR 1 VR和AR的关系 AR MR是平台 xff0c 覆盖面比VR更广 xff0c VR是一种媒体形式 xff0c 任何用得到媒体的场景 xff0c 如娱乐 教育等 xff0c 都会有VR的影
  • 2020计算机技术类,部分人工智能与软件工程SCI一区期刊列表(基于letpub数据)

    网上找了很久将计算机技术作为独立大区的期刊列表 xff0c 还是没有找到 所以我决定根据letpub的数据 xff0c 自己整理下 xff0c 方便以后查看 注 xff1a 由于2020与2019年的数据存在一些冲突 xff0c 部分数据可
  • 数字电视的格式(BT.601 BT.709 BT.2020)和接口(BT.656 BT.1120 BT.2077)

    规格和格式接口特性 标清数字电视 xff08 SDTV xff09 BT 601BT 656 高清数字电视 xff08 HDTV xff09 BT 709BT 1120 超高清数字电视 xff08 UHDTV xff09 BT 2020BT
  • Matlab读取高光谱遥感数据.Mat 和 .tif(2020.12.27)

    Matlab读取高光谱遥感数据 1 高光谱遥感数据简介2 两个开源的高光谱遥感数据集3 高光谱遥感数据常用格式3 1 Mat3 2 Tif 4 Matlab读取高光谱遥感数据4 1 Matlab读取 Mat格式的高光谱遥感数据4 1 1 M
  • 2020-09-28

    通用异步收发器 xff08 Universal Asynchronous Receiver Transmitter xff0c 通常称作UART xff0c 是一种串行 异步 全双工的通信协议 xff0c 在嵌入式领域应用的非常广泛 UAR

随机推荐