沧州三亚菏泽经济预测自然
投稿投诉
自然科学
知识物理
化学生物
地理解释
预测理解
本质社会
人类现象
行为研究
经济政治
心理结构
关系指导
人文遗产
菏泽德阳
山西湖州
宝鸡上海
茂名内江
三亚信阳
长春北海
西安安徽
黄石烟台
沧州湛江
肇庆鹤壁
六安韶关
成都钦州

OpenMPParallelConstruct实现原理与源码

11月19日 寒霜坞投稿
  前言
  在本篇文章当中我们将主要分析OpenMP当中的parallelconstruct具体时如何实现的,以及这个construct调用了哪些运行时库函数,并且详细分析这期间的参数传递!Parallel分析编译器角度
  在本小节当中我们将从编译器的角度去分析该如何处理parallelconstruct。首先从词法分析和语法分析的角度来说这对编译器并不难,只需要加上一些处理规则,关键是编译器将一个parallelconstruct具体编译成了什么?
  下面是一个非常简单的parallelconstruct。pragmaompparallel{}
  编译器在遇到上面的parallelconstruct之后会将代码编译成下面的样子:voidsubfunction(voiddata){}GOMPparallelstart(subfunction,data,numthreads);subfunction(data);GOMPparallelend();
  首先parallelconstruct中的代码块会被编译成一个函数subfunction,当然了函数名不一定是这个,然后会在使用pragmaompparallel的函数当中将一个parallelconstruct编译成OpenMP动态库函数的调用,在上面的伪代码当中也指出了,具体会调用OpenMP的两个库函数GOMPparallelstart和GOMPparallelend,并且主线程也会调用函数subfunction,我们在后面的文章当中在仔细分析这两个动态库函数的源代码。深入剖析Parallel动态库函数参数传递动态库函数分析
  在本小节当中,我们主要去分析一下在OpenMP当中共享参数是如何传递的,以及介绍函数GOMPparallelstart的几个参数的含义。
  首先我们分析函数GOMPparallelstart的参数含义,这个函数的函数原型如下:voidGOMPparallelstart(void(fn)(void),voiddata,unsignednumthreads)
  上面这个函数一共有三个参数:第一个参数fn是一个函数指针,主要是用于指向上面编译出来的subfunction这个函数的,因为需要多个线程同时执行这个函数,因此需要将这个函数传递过去,让不同的线程执行。第二个参数是传递的数据,我们在并行域当中会使用到共享的或者私有的数据,这个指针主要是用于传递数据的,我们在后面会仔细分析这个参数的使用。第三个参数是表示numthreads子句指定的线程个数,如果不指定这个子句默认的参数是0,但是如果你使用了IF子句并且条件是false的话,那么这个参数的值就是1。这个函数的主要作用是启动一个或者多个线程,并且执行函数fn。voidGOMPparallelend(void)这个函数的主要作用是进行线程的同步,因为一个parallel并行域需要等待所有的线程都执行完成之后才继续往后执行。除此之外还需要释放线程组的资源并行返回到之前的ompinparallel()表示的状态。参数传递分析
  我们现在使用下面的代码来具体分析参数传递过程:includestdio。hincludeomp。hintmain(){intdata100;inttwo100;printf(start);pragmaompparallelnumthreads(4)default(none)shared(data,two){printf(tidddatadtwod,ompgetthreadnum(),data,two);}printf(finished);return0;}
  我们首先来分析一下上面的两个变量data和two的是如何被传递的,我们首先用图的方式进行表示,然后分析一下汇编程序并且对图进行验证。
  上面的代码当中两个变量data和two在内存当中的布局结构大致如下所示(假设data的初始位置时0x0):
  那么在函数GOMPparallelstart当中传递的参数data就是0x0也就是指向data的内存地址,如下图所示:
  那么根据上面参数传递的情况,我们就可以在subfunction当中使用(int)data得到data的值,使用((int)((char)data4))得到two的值,如果是private传递的话我们就可以先拷贝这个数据再使用,如果是shared的话,那么我们就可以直接使用指针就行啦。
  上面的程序我们用pthread大致描述一下,则pthread对应的代码如下所示:includepthread。hincludestdio。hincludestdint。htypedefstructdatainmainfunction{}pthreadtthreads〔4〕;voidsubfunction(voiddata){inttwo((datainmainfunction)data)intdata((datainmainfunction)data)printf(tidlddatadtwod,pthreadself(),data,two);returnNULL;}intmain(){在主函数申请8个字节的栈空间data。data100;data。two100;for(inti0;i4;i){pthreadcreate(threads〔i〕,NULL,subfunction,data);}for(inti0;i4;i){pthreadjoin(threads〔i〕,NULL);}return0;}汇编程序分析
  在本节当中我们将仔细去分析上面的程序所产生的汇编程序,在本文当中的汇编程序基础x8664平台。在分析汇编程序之前我们首先需要了解一下x86函数的调用规约,具体来说就是在进行函数调用的时候哪些寄存器保存函数参数以及是第几个函数参数。具体的规则如下所示:
  寄存器
  含义
  rdi
  第一个参数
  rsi
  第二个参数
  rdx
  第三个参数
  rcx
  第四个参数
  r8
  第五个参数
  r9
  第六个参数
  我们现在仔细分析一下上面的程序的main函数的反汇编程序:00000000004006cdmain:4006cd:55pushrbp4006ce:4889e5movrsp,rbp4006d1:4883ec10sub0x10,rsp4006d5:c745fc64000000movl0x64,0x4(rbp)4006dc:c745f89cffffffmovl0xffffff9c,0x8(rbp)4006e3:bff4074000mov0x4007f4,edi4006e8:e893feffffcallq400580putsplt4006ed:8b45fcmov0x4(rbp),eax4006f0:8945f0moveax,0x10(rbp)4006f3:8b45f8mov0x8(rbp),eax4006f6:8945f4moveax,0xc(rbp)4006f9:488d45f0lea0x10(rbp),rax4006fd:ba04000000mov0x4,edx400702:4889c6movrax,rsi400705:bf3d074000mov0x40073d,edi40070a:e861feffffcallq400570GOMPparallelstartplt40070f:488d45f0lea0x10(rbp),rax400713:4889c7movrax,rdi400716:e822000000callq40073dmain。ompfn。040071b:e870feffffcallq400590GOMPparallelendplt400720:8b45f0mov0x10(rbp),eax400723:8945fcmoveax,0x4(rbp)400726:8b45f4mov0xc(rbp),eax400729:8945f8moveax,0x8(rbp)40072c:bffa074000mov0x4007fa,edi400731:e84afeffffcallq400580putsplt400736:b800000000mov0x0,eax40073b:c9leaveq40073c:c3retq
  从上面的反汇编程序我们可以看到在主函数的汇编代码当中确实调用了函数GOMPparallelstart和GOMPparallelend,并且subfunction为main。ompfn。0,它对应的汇编程序如下所示:000000000040073dmain。ompfn。0:40073d:55pushrbp40073e:4889e5movrsp,rbp400741:4883ec10sub0x10,rsp400745:48897df8movrdi,0x8(rbp)400749:e852feffffcallq4005a0ompgetthreadnumplt40074e:488b55f8mov0x8(rbp),rdx400752:8b4a04mov0x4(rdx),ecx400755:488b55f8mov0x8(rbp),rdx400759:8b12mov(rdx),edx40075b:89c6moveax,esi40075d:bf03084000mov0x400803,edi400762:b800000000mov0x0,eax400767:e844feffffcallq4005b0printfplt40076c:c9leaveq40076d:c3retq40076e:6690xchgax,axGOMPparallelstart详细参数分析void(fn)(void),我们现在来看一下函数GOMPparallelstart的第一个参数,根据我们前面谈到的第一个参数应该保存在rdi寄存器,我们现在分析一下在main函数的反汇编程序当中在调用函数GOMPparallelstart之前rdi寄存器的值。我们可以看到在main函数位置为4006f8的地方的指令mov0x40073d,edi可以看到rdi寄存器的值为0x40073d(edi寄存器是rdi寄存器的低32位),我们可以看到函数main。ompfn。0的起始地址就是0x40073d,因此我们就可以在函数GOMPparallelstart使用这个函数指针了,最终在启动的线程当中调用这个函数。voiddata,这是函数GOMPparallelstart的第二个参数,根据前面的分析第二个参数保存在rsi寄存器当中,我现在将main数当中和rsi相关的指令选择出来:00000000004006cdmain:4006cd:55pushrbp4006ce:4889e5movrsp,rbp4006d1:4883ec10sub0x10,rsp4006d5:c745fc64000000movl0x64,0x4(rbp)4006dc:c745f89cffffffmovl0xffffff9c,0x8(rbp)4006ed:8b45fcmov0x4(rbp),eax4006f0:8945f0moveax,0x10(rbp)4006f3:8b45f8mov0x8(rbp),eax4006f6:8945f4moveax,0xc(rbp)4006f9:488d45f0lea0x10(rbp),rax400702:4889c6movrax,rsi
  上面的汇编程序的栈空间以及在调用函数之前GOMPparallelstart部分寄存器的指向如下所示:
  最终在调用函数GOMPparallelstart之前rsi寄存器的指向如上图所示,上图当中rsi的指向的内存地址作为参数传递过去。根据上文谈到的subfunction中的参数可以知道,在函数main。ompfn。0当中的rdi寄存器(也就是第一个参数data)的值就是上图当中rsi寄存器指向的内存地址的值(事实上也就是rsi寄存器的值)。大家可以自行对照着函数main。ompfn。0的汇编程序对rdi寄存器的使用就可以知道这其中的参数传递的过程了。unsignednumthreads,根据前文提到的保存第三个参数的寄存器是rdx,在main函数的位置4006fd处,指令为mov0x4,edx,这和我们自己写的程序是一致的都是4(0x4)。动态库函数源码分析GOMPparallelstart源码分析
  我们首先来看一下函数GOMPparallelstart的源代码:voidGOMPparallelstart(void(fn)(void),voiddata,unsignednumthreads){numthreadsgompresolvenumthreads(numthreads,0);gompteamstart(fn,data,numthreads,gompnewteam(numthreads));}
  在这里我们对函数gompteamstart进行分析,其他两个函数gompresolvenumthreads和gompnewteam只简单进行作用说明,太细致的源码分析其实是没有必要的,感兴趣的同学自行分析即可,我们只需要了解整个执行流程即可。gompresolvenumthreads,这个函数的主要作用是最终确定需要几个线程去执行任务,因为我们可能并没有使用numthreads子句,而且这个值和环境变量也有关系,因此需要对线程的个数进行确定。gompnewteam,这个函数的主要作用是创建包含numthreads个线程数据的线程组,并且对数据进行初始化操作。gompteamstart,这个函数的主要作用是启动numthreads个线程去执行函数fn,这其中涉及一些细节,比如说线程的亲和性(affinity)设置。
  由于gompteamstart的源代码太长了,这里只是节选部分源程序进行分析:Launchnewthreads。for(;i,startdata){这行代码就是将subfunction函数指针进行保存最终在函数gompthreadstart当中进行调用这里保存函数subfunction的函数参数startdatats。线程的所属组startdatats。workshareteamworkshares〔0〕;startdatats。lastworkshareNULL;startdatats。线程的id我们可以使用函数ompgetthreadnum得到这个值startdatats。levelteamprevts。level1;startdatats。activelevelthrts。ifdefHAVESYNCBUILTINSstartdatats。singlecount0;endifstartdatats。statictrip0;startdatataskteamimplicittask〔i〕;gompinittask(startdatatask,task,icv);teamimplicittask〔i〕。icv。如果使用了线程的亲和性那么还需要进行亲和性设置if(gompcpuaffinity!NULL)gompinitthreadaffinity(attr);errpthreadcreate(pt,attr,gompthreadstart,startdata);if(err!0)gompfatal(Threadcreationfailed:s,strerror(err));}
  上面的程序就是最终启动线程的源程序,可以看到这是一个for循环并且启动nthreads个线程,pthreadcreate是真正创建了线程的代码,并且让线程执行函数gompthreadstart可以看到线程不是直接执行subfunction而是将这个函数指针保存到startdata当中,并且在函数gompthreadstart真正去调用这个函数,看到这里大家应该明白了整个parallelconstruct的整个流程了。
  gompthreadstart的函数题也相对比较长,在这里我们选中其中的比较重要的几行代码,其余的代码进行省略。对比上面线程启动的pthreadcreate语句我们可以知道,下面的程序真正的调用了subfunction,并且给这个函数传递了对应的参数。staticvoidgompthreadstart(voidxdata){Extractwhatweneedfromdata。localfn(localdata);returnNULL;}GOMPparallelend分析
  这个函数的主要作用就是一个同步点,保证所有的线程都执行完成之后再继续往后执行,这一部分的源代码比较杂,其核心原理就是使用路障barrier去实现的,这其中是OpenMP自己实现的一个barrier而不是直接使用pthread当中的barrier,这一部分的源程序就不进行仔细分析了,感兴趣的同学可以自行阅读,可以参考OpenMP锁实现原理。总结
  在本篇文章当中主要给大家介绍了parallelconstruct的实现原理,以及他的动态库函数的调用以及源代码分析,大家只需要了解整个流程不太需要死扣细节(这并无很大的用处)只有当我们自己需要去实现OpenMP的时候需要去了解这些细节,不然我们只需要了解整个动态库的设计原理即可!
投诉 评论

13大冷门,法甲第2轰然倒下,连续8场不败终结,大巴黎笑了法甲联赛9次冠军得主、赛前排名第2的马赛队迎来了法甲第22轮的较量,马赛队此役坐镇自己的主场PK法甲联赛4次冠军得主、赛前排名第9的尼斯队。赛前马赛队已经连续8场各项比赛……亚洲杯比赛第三场,中国男篮大胜省队中国台北今晚21点开始的亚洲杯比赛,中国男篮VS省队中国台北队,此战周琦复出表现全面,由于身体没有完全康复,所以上场时间也有限制,全场比赛周琦出战17分钟48秒,投篮10中5,拿下10……不求人被邀请带货,实则是绝版皮肤返场活动,火箭少女呼声最高若是要说到在和平精英直播界哪一位主播人气最高?这就必须得抬出不求人了,不求人作为虎牙和平精英板块的知名主播之一,不仅仅深受虎牙官方重视,就连和平精英游戏官方对此也宠爱有加。频频……iOS16新技巧!查看电池还能用多久iOS16自出道以后,热度就居高不下。不过频频翻车,引得苹果紧急发布iOS16。1正式版力挽狂澜。目前从果粉的反馈来看,新版本无论是优化后的电量百分比功能,还是修复粘贴弹……吴亚军壕掷55亿也救不回的龙湖,到底怎么了?10日28日,龙湖集团(00960。HK)发布董事会变更公告。根据公告显示,公司创始人吴亚军因年龄及身体的原因,辞任龙湖集团执行董事、董事会主席,董事会主席将由执行董事及……山东泰山悬了!想躺赢进决赛,对手却获利好,或再败于老冤家脚下山东泰山悬了!本想躺赢进决赛,未曾想对手却突迎利好,或再败于老冤家脚下,无缘以3连创造历史。2022赛季,山东泰山在联赛中,虽和武汉三镇同分,但却因为净胜球不如武汉三镇,……OpenMPParallelConstruct实现原理与源码前言在本篇文章当中我们将主要分析OpenMP当中的parallelconstruct具体时如何实现的,以及这个construct调用了哪些运行时库函数,并且详细分析这期间……多家股份行城商行跟进下调存款利率专家预计将有更多银行跟进图据新华社继多家国有大行下调存款利率后,多家股份制商业银行、城商行也跟进下调存款利率。9月16日,中信银行、光大银行、民生银行、平安银行、浦发银行、广发银行、华夏银……灰熊续约莫兰特并非头等大事,内线才是最大X因素静待小浓眉进化顶薪续约莫兰特成为了灰熊在休赛期最大的事情,这样的运作也让他们将后场的核心轮换保持完整。面对新赛季,灰熊显然希望想要继续打出好成绩,而莫兰特的续约并不是灰熊打出好成绩的真正保障……苹果设备现在开始支持任天堂的经典游戏手柄苹果公司昨天在其一轮Mac、iPhone、iPad和AppleTV更新中悄悄地给了我们一个小小的惊喜,它增加了对任天堂一系列经典游戏控制器的支持。正如开发者SteveTroug……没得黑,周琦用一个夏天完成了蜕变,狠狠的打了霍楠的脸不知道大家有没有感觉,周琦从休赛期到现在加盟NBL联赛整个人都彻底的变了一样的。曾经霍楠周琦是一个街溜子,被新疆队的主教练阿的江怒怼。但其实霍楠说出了所有中国球迷的心声,周琦确……聊聊今年最让我感到惊艳的笔记本几乎每年都有让我感到惊艳的笔记本电脑,它们都是笔电行业中的翘楚,值得所有厂商学习。但是今天这台电脑不一样,虽然它是2022年最令我惊艳的作品,但实用性一般,更像是一台炫技……
学生党可爱扎发发型各种可爱扎发随便选关于人际交往的小故事带感悟则晒柚子皮要去掉白色的东西吗绍兴的真情告白苏州文明码积分的获取途径有哪些博物造句用博物造句大全看樱花去NikeKobeXIEliteLow4KB新色登場前脚卖新东方股票,后脚挖董宇辉,腾讯算得一手好账?如何钱生钱造就工薪阶层的亿万富翁梦脸大怎么选发型微胖女生发型西安九价预约不上怎么办西安疫苗九价什么时候还有第一次参加运动会的周记比别人多坚持一会儿去干二锅头我是个做台小姐背影陈酱九八七酱香酒6个小常识,喝不喝酒都要知道十部研究心理学该看的电影指尖造句用指尖造句大全如何去除白色桑蚕丝衣服上的红酒渍全身肥胖的人试试按摩减肥吧关于与世长辞的造句有哪些吴前关键时刻掉链子,杜锋不满闫军瞎吹,广东赢球却有坏消息

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找