龙空技术网

PAC在线丨高性能计算编程入门之OpenMP

木泉 83

前言:

此时小伙伴们对“mp算法代码”可能比较讲究,咱们都想要了解一些“mp算法代码”的相关文章。那么小编也在网络上汇集了一些有关“mp算法代码””的相关知识,希望我们能喜欢,各位老铁们快快来学习一下吧!

武汉疫情来势汹汹,新型冠状病毒肺炎感染人数人在不断攀升!全国上下都投入到了抗击疫情中,PAC组委会提醒大家不信谣,不传谣,立足本职工作,多通风,戴口罩,少聚集,共克时艰。

目前诸多企业、高校都下发延迟开工、开学的通知!然而:

停课,并不意味着学习就此停滞!

全国并行应用挑战赛,保持在线!

引 言

在并行应用的开发与优化一文中,我们提到两种常用的同构代码并行开发技术:OpenMP和MPI。OpenMP作为入门级并行编程技术,往往成为众多代码开发者对串行程序并行优化的第一个步骤。下面,本文将对OpenMP并行技术做进一步介绍。

一、OpenMP技术简介

OpenMP是一种用于共享内存系统的多线程程序设计编译处理方案,包含了众多程序接口,并且支持很多语言进行编程,例如C/C++和Fortran等,而且包括GNU、Intel等众多编译器也都支持OpenMP。在使用OpenMP技术并行化原有算法时,开发者可以在仅做出少量代码修改的情况下实现原始算法基于共享内存系统的并行算法。开发者只要在原有算法中加入一个专用的#pragma语句来指明自己的意图,然后使用支持OpenMP的编译器编译程序,就可以实现对原有算法的并行化,但是如果编译器不支持OpenMP或者选择忽略#pragma语句时,程序又可以被视为普通的串行程序进行编译,可以正常得到串行的结果,只是没有达到并行加速的效果。

对基于数据分集的多线程程序设计,OpenMP是一个很好的选择,因为它具有很强的灵活性,可以比较容易地适应不同配置的并行系统。同时,OpenMP也提供了很多库函数来帮助开发者解决线程粒度和负载平衡等传统多线程程序设计中的难题。但是使用OpenMP的前提是服务器使用共享存储方式存储数据。

二、OpenMP并行编程

2.1 OpenMP执行模式

OpenMP的执行模型采用fork-join模型,fork即创建新线程或者唤醒已有线程,join即多线程的汇合。fork-join执行模型在刚开始执行时,只有一个主线程在运行,主线程在运行过程中,当遇到并行区域时,再由主线程派生出子线程来执行并行任务。在并行执行任务时,主线程和派生子线程共同工作。在并行执行结束后,派生子线程退出或者阻塞,不再工作,控制流程回到主线程中。

OpenMP的开发者需要在可以实现并行的部分用制导指令向编译器指出该部分的并行属性,同时,OpenMP还支持这些并行部分出现嵌套的情况。并行域是在成对的fork和join之间的区域,它表示一段代码或可执行区间。

在OpenMP中,主线程和派生子线程一般是操作系统的线程或进程,对应一个物理核心的硬件资源,所以当硬件资源的物理核心数增加时,应用OpenMP技术的并行程序往往也能获得性能提升。

以C语言为例,OpenMP提供的一些常用制导语句包括:

parallel——用于声明一个将被多个线程并行执行的代码结构块。

parallelfor——用在for循环语句之前,表示紧接着的for循环体将被多个线程自动并行执行,但是编译器不能判断循环体中是否存在数据依赖,需要由开发者自己确认。如果存在数据依赖,那么产生的结果将不可控。

single——表示该段代码只被一个线程串行执行,其他线程将在single结构结束处等待该线程执行完毕。

critical——表示该段代码每次只被一个线程串行执行,其他线程将在critical结构开始处等待,并依次执行该段代码。

master——表示该段代码由主线程执行,其他线程跳过该段代码,继续执行。

barrier——在并行域内线程同步,线程执行到barrier时需要等待,在全部线程执行到barrier之后,才继续执行。

atomic——指定某个数据的操作需要原子操作运行,包括++、--、+=、-=等等。

OpenMP中常用的子句包括:

private——指定一些变量在每个线程中都有自己的私有副本。

firstprivate——指定一些变量在每个线程中都有自己的私有副本,并且在进入并行域时,初始化每个线程中的私有副本的值,继承这些变量在主线程中的值。

lastprivate——指定最后一个退出并行域的线程将一些私有变量的值复制给同名的主线程变量中。

reduction——对变量进行规约操作,在退出并行域时,将返回变量规约之后的值。

OpenMP中一些常用的运行库函数包括:

voidomp_set_num_threads(int)——设置并行域的线程数函数。设置OpenMP程序的运行线程数有多种方法,除此以外还可以通过环境变量的方法进行全局设置。

int omp_get_thread_num()——获得线程号函数。通过调用该函数,可以获得每个线程的线程号,从而区分各个线程并对各个线程进行不同的任务划分。

double omp_get_wtime()——获得当前时间戳。由于OpenMP的多线程运行机制,使用传统的clock()计时函数会将全部线程的经过时间统计到一起,需要改用此计时函数记录OpenMP多线程程序的真实墙钟时间。

2.2 OpenMP编程实例

本节将对上文提到的一些制导语句进行编程测试,展示运行效果。以下代码运行环境为Linux操作系统8线程并行,通过icc-qopenmp test.c命令编译程序。

2.2.1 single语句

#include<omp.h>

#include<stdio.h>

int main(int argc,char *argv[])

{

int tid;

#pragma omp parallel private(tid)

{

#pragma omp single{

tid = omp_get_thread_num();/*获取线程号*/

printf("here is single 1 ,num%d is here!\n",tid);

}

#pragma omp single{

tid = omp_get_thread_num();/*获取线程号*/

printf("here is single 2 ,num%d is here!\n",tid);

}

#pragma omp single{

tid = omp_get_thread_num();/*获取线程号*/

printf("here is single 3 ,num%d is here!\n",tid);

}

}

return 0;

}

程序运行截图:

可以看到,每个single区域均只有一个线程进入并执行区域内语句。

2.2.2 master语句

#include<omp.h>

#include<stdio.h>

int main(int argc,char *argv[])

{

int tid;

#pragma omp parallel private(tid)

{

#pragma omp master

{

tid = omp_get_thread_num();/*获取线程号*/

printf("here is masterrelion, num %d is here!\n",tid);

}

tid = omp_get_thread_num();/*获取线程号*/

printf("Hello World fromOpenMP thread %d\n",tid);

}

}

程序运行截图:

可以看到,只有0号线程主线程进入master区域,其他子线程跳过master区域直接执行后续代码。

2.2.3 barrier语句

#include<omp.h>

#include<stdio.h>

int main(int argc,char *argv[])

{

int tid;

#pragma omp parallel private(tid)

{

#pragma omp master

{

tid = omp_get_thread_num();/*获取线程号*/

printf("here is masterrelion, num %d is here!\n",tid);

}

#pragma omp barrier

tid = omp_get_thread_num();/*获取线程号*/

printf("Hello World fromOpenMP thread %d\n",tid);

}

}

程序运行截图:

可以看到,在加入barrier语句后,先执行到的线程等待后执行到的线程,当全部线程执行完毕后,再执行后续代码。

2.3 利用OpenMP并行计算Pi值

以下为利用近似法求解Pi值的OpenMP并行实现代码,其算法原理是利用梯形公式近似求解积分

#include<omp.h>

#include<stdio.h>

static longnum_steps = 100000;

double step;

void main ()

{

int i,id=0;double x, pi, sum, start_time,end_time;

step = 1.0/(double) num_steps;pi = 0.0;

start_time=omp_get_wtime();

#pragma omp parallel private(i,id,x,sum)

{

intNUM_THREADS = omp_get_num_threads() ;

id = omp_get_thread_num();sum = 0.0;

for (i=id;i< num_steps;i+=NUM_THREADS){

x = (i+0.5)*step; sum +=4.0/(1.0+x*x);

}

#pragma omp atomic

pi += sum/num_steps;

}

end_time=omp_get_wtime();

printf("Pi=%lf\n Running time%lf\n",pi,end_time-start_time);

}

程序运行截图:

三、总 结

OpenMP并行技术是一种实现较为简单的共享内存多线程并行实现方案,可以通过对原始算法进行简单分析及改动后实现。当程序的计算量及数据量达到较大的规模后,OpenMP并行优化可以让程序实现可观的性能提升。

本文作者刘帅,北京并行科技股份有限公司优化应用工程师、PAC竞赛技术支持、英特尔大学讲者。本科就读于中国地质大学(北京),并于同校取得计算机科学与技术硕士学位,研究方向为高性能计算,具有4年并行计算相关研究经验。熟悉Linux集群环境下Fortran及C语言程序的编译与调试,具有丰富Vtune、ITAC等多种并行调优工具使用及优化经验。

欢迎大家留言提问,共同战“疫”,我们在岗。PAC与您同行,中国加油!武汉加油!

标签: #mp算法代码 #编译openmp #openmp编译器