<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      OpenMP學(xué)習(xí) 第十一章 同步與OpenMP內(nèi)存模型

      第十一章 同步與OpenMP內(nèi)存模型


      內(nèi)存一致性模型

      OpenMP線程在共享內(nèi)存中執(zhí)行,共享內(nèi)存是組中所有線程都可以訪問的地址空間,其中存儲(chǔ)著變量.使共享內(nèi)存系統(tǒng)高效運(yùn)行的唯一方法是允許線程保持一個(gè)臨時(shí)的內(nèi)存視圖,該視圖駐留在處理器和內(nèi)存RAM之間的內(nèi)存結(jié)構(gòu)中.

      當(dāng)線程通過共享內(nèi)存中的變量進(jìn)行交互時(shí),線程必須使它們對(duì)內(nèi)存的臨時(shí)試圖與共享視圖一致.線程通過 flush構(gòu)造 來(lái)實(shí)現(xiàn)這點(diǎn).

      沖刷集(flush-set) 是應(yīng)用于沖刷的共享變量的集合.默認(rèn)情況下,沖刷集是線程可用的所有共享變量.

      • 進(jìn)行顯式?jīng)_刷的構(gòu)造:
      #pragma omp flush [(flush-set)]
      

      除了解決內(nèi)存一致性的問題,沖刷還和管理編譯器何時(shí)可以重新排序指令的規(guī)則進(jìn)行交互.如果指令使用了沖刷集的變量,編譯器是不允許圍繞沖刷來(lái)重新排序指令的.

      • sequenced-before關(guān)系:
        單個(gè)線程執(zhí)行的事件之間的偏序.A排序在B之前,則A sequenced-before B.

      順序點(diǎn)(sequenced point): 程序執(zhí)行中的點(diǎn),在此處所涉及的有關(guān)語(yǔ)句以及與語(yǔ)句相關(guān)的任何附帶后果均已完成.如果A的評(píng)估(包括A所隱含的任何附帶后果)在B的執(zhí)行之前完成,那么我們說(shuō)A被排序在B之前.

      常見的順序點(diǎn):

      • 一個(gè)完整表達(dá)式的結(jié)束.包括if,while,for和return等.
      • 在邏輯運(yùn)算符,條件運(yùn)算符以及逗號(hào)運(yùn)算符處.
      • 在函數(shù)調(diào)用時(shí),所有參數(shù)的評(píng)估之后,但在調(diào)用前.
      • 在函數(shù)返回之前.

      事實(shí)上對(duì)于程序中順序點(diǎn)的排序,我們有三種情況:

      • sequenced-before:順序點(diǎn)之間的關(guān)系是一個(gè)順序點(diǎn)接著另一個(gè)順序點(diǎn).舉例:
      int a=1,b=2;
      //由于賦值操作和逗號(hào)運(yùn)算符為順序點(diǎn),所以該式為順序點(diǎn)
      //由于逗號(hào)運(yùn)算符有順序,因而兩個(gè)順序點(diǎn)之間有sequenced-before關(guān)系
      
      • indeterminately sequenced:順序點(diǎn)之間的關(guān)系是,它們以某種順序執(zhí)行,但這個(gè)順序沒有被定義.例如:
      c = func(a)+func(b);
      //由于函數(shù)調(diào)用與賦值為順序點(diǎn),但是加法不為順序點(diǎn),所以func(a)與func(b)之間順序不確定
      //所以在func(a)+func(b)中,兩個(gè)順序點(diǎn)之間順序不確定,存在indeterminately sequenced關(guān)系
      
      • unsequenced:當(dāng)順序點(diǎn)發(fā)生沖突并導(dǎo)致未定義的結(jié)果時(shí),該情況成立.例如:
      a=a++;
      //由于賦值為順序點(diǎn),完整的表達(dá)式為一個(gè)順序點(diǎn),對(duì)a的增量操作與加法卻不是
      //子表達(dá)式是無(wú)序的,對(duì)a的賦值與增量操作可能沖突,存在unsequenced關(guān)系
      

      而對(duì)于單個(gè)線程,有一種happens-before關(guān)系直接沿用了sequenced-before關(guān)系的概念.

      • happens-before:如果事件A是排序在事件B之前的.那么就說(shuō):A happens-befor B.

      為了定義多個(gè)線程之間的happens-before關(guān)系,我們必須同步線程,于是我們將同步視為兩個(gè)或多個(gè)線程之間的特定事件.我們通過一個(gè)synchronized-with關(guān)系來(lái)實(shí)現(xiàn).

      • synchronized-with:當(dāng)線程圍繞一個(gè)事件協(xié)調(diào)執(zhí)行,以定義其執(zhí)行的順序約束時(shí),synchronized-with關(guān)系在線程之間成立.可以將線程的執(zhí)行看作是一個(gè)事件序列,將每個(gè)事件看作是一個(gè)順序點(diǎn).

      線程中事件的同步用$\leftrightarrow$表示.

      成對(duì)同步

      在前面的第八章中,我們注意到OpenMP通用核心中并不支持成對(duì)的或點(diǎn)對(duì)點(diǎn)(point-to-point)的同步.我們將考慮通過 生產(chǎn)者-消費(fèi)者模式 來(lái)推動(dòng)這一討論,這是并行計(jì)算中常用的模式.

      在該模式下,生產(chǎn)者進(jìn)行一些工作以產(chǎn)生一些結(jié)果;消費(fèi)者等待生產(chǎn)者完成工作,然后消費(fèi)結(jié)果.將這兩個(gè)步驟之間的處理序列化在流水線中十分常見.

      流水線并行 中,將生產(chǎn)者與消費(fèi)者步驟顯示為兩個(gè)線性序列:一個(gè)線程用于運(yùn)行生產(chǎn)者任務(wù),另一個(gè)線程用于運(yùn)行消費(fèi)者任務(wù),只要流水線階段的數(shù)量足夠大,這些流水線并行程序的性能就會(huì)不錯(cuò).

      先觀察下面這樣的一個(gè)生產(chǎn)者-消費(fèi)者模式程序:

      bool flag=false;
      #pragma omp parallel shared(A,B,flag)
      {
          int id = omp_get_thread_num();
          int nthrds = omp_get_num_threads();
      
          if((id==0)&&(nthrds<2))
              exit(-1);
      
          if(id==0){//生產(chǎn)者
              produce(A);
              flag=true;
          }
          if(id==1){//消費(fèi)者
              while(!flag)
                  //自旋鎖(spin mutex)結(jié)構(gòu)
              consume(A);
          }
      }
      

      該程序邏輯上是正確的,但是實(shí)際上卻是錯(cuò)誤的.

      在并行中,同步 有兩個(gè)方面: 數(shù)據(jù)同步線程同步.

      對(duì)于 數(shù)據(jù)同步 ,我們需要兩個(gè)線程在內(nèi)存中看到同一個(gè)變量的一致值.我們通過 完全沖刷 實(shí)現(xiàn).

      在OpenMP通用核心中,以下幾點(diǎn)都隱含了沖刷:

      • 當(dāng)一個(gè)新的線程組被parallel構(gòu)造fork時(shí).
      • 當(dāng)一個(gè)critical構(gòu)造被線程加入時(shí).
      • 當(dāng)一個(gè)線程完成一個(gè)critical構(gòu)造并退出臨界區(qū)時(shí).
      • 進(jìn)入task區(qū)域時(shí).
      • 從task區(qū)域退出時(shí).
      • 在退出taskwait時(shí).
      • 在退出顯式barrier構(gòu)造時(shí).
      • 在退出隱式柵欄構(gòu)造時(shí).(無(wú)nowait)

      對(duì)于 線程同步 ,我們需要在兩個(gè)線程之間建立synchronized-with關(guān)系.我們通過 自旋鎖 實(shí)現(xiàn).

      于是,在調(diào)整后,我們得到了下面的又一個(gè)邏輯上似乎沒有錯(cuò)誤的程序:

      int flag=0;
      omp_set_num_threads(2);
      
      #pragma omp parallel shared(A,flag)
      {
          int id = omp_get_thread_num();
          int nthrds = omp_get_num_threads();
      
          if((id==0)&&(nthrds<2))
              exit(-1);
      
          if(id==0){
              produce(A);
              #pragma omp flush
              flag = 1;
              #pragma omp flush(flag);
          }
      
          if(id==1){
              #pragma omp flush(flag)
              while(flag==0)
                  #pragma omp flush(flag)
              #pragma omp flush
              consume(A);
          }
      }
      

      然而這個(gè)程序?qū)嶋H上仍然是錯(cuò)誤的.

      與現(xiàn)代程序設(shè)計(jì)語(yǔ)言一致, 只有通過原子操作才能建立synchronized-with關(guān)系 .而將flag設(shè)置為1及將其加載到內(nèi)存中都不是原子操作,存在數(shù)據(jù)競(jìng)爭(zhēng).

      于是為了使得這個(gè)程序真正正確,關(guān)鍵是需要修改自旋鎖,以便通過原子加載和存儲(chǔ)操作建立synchronized-with關(guān)系.

      首先,對(duì)于生產(chǎn)者,將flag變量的賦值放在一個(gè)atomic write構(gòu)造中.

      然后,對(duì)于消費(fèi)者,先把while循環(huán)變成一個(gè)無(wú)限循環(huán),再為了避免變量flag上的任何讀寫沖突,將值存儲(chǔ)到一個(gè)臨時(shí)的flag變量中,并測(cè)試該變量的值來(lái)決定何時(shí)脫離循環(huán).

      最終,我們得到一個(gè)如下的程序:

      int flag=0;
      omp_set_num_threads(2);
      
      #pragma omp parallel shared(A,flag)
      {
          int id = omp_get_thread_num();
          int nthrds = omp_get_num_threads();
      
          if((id==0)&&(nthrds<2))
              exit(-1);
      
          if(id==0){
              produce(A);
              #pragma omp flush
              #pragma atomic write
              flag = 1;
          }
      
          if(id==1){
              while(1){
                  #pragma omp atomic read
                      flag_temp=flag;
                  if(flag_temp!=0)
                      break;
              }
              #pragma omp flush
              consume(A);
          }
      }
      

      鎖(mutex)及其使用

      OpenMP的鎖與pthreads的鎖功能基本相同,它是用來(lái)圍繞互斥建立同步協(xié)議的.

      與critical構(gòu)造不同,OpenMP的鎖是作為庫(kù)例程來(lái)實(shí)現(xiàn)的. 在使用鎖之前必須對(duì)其進(jìn)行初始化.

      • 使用鎖來(lái)保證互斥:
      omp_lock_t lck;
      omp_init_lock(&lck);
      
      #pragma parallel shared(lck)
      {
          omp_set_lock(&lck);
          //...do somethine
          omp_unset_lock(&lck);
      }
      omp_destroy_lock(&lck);
      

      鎖的設(shè)置和解除設(shè)置意味著一次沖刷,它們隱含著所需的內(nèi)存移動(dòng),用相互排斥功能來(lái)支持內(nèi)存一致性.

      當(dāng)設(shè)置鎖時(shí),意味著一次沖刷,這樣線程更新的值與內(nèi)存中的值一致;當(dāng)解鎖時(shí),值會(huì)被沖刷,所以當(dāng)下一個(gè)線程抓取鎖更新值時(shí),它將看到正確的值.

      C++內(nèi)存模型與OpenMP

      C++11定義了原子操作,用于定義synchronized-with關(guān)系.

      C++中最常用的內(nèi)存順序包括以下幾種:

      • seq_cst:對(duì)所有線程來(lái)說(shuō),內(nèi)存的加載和存儲(chǔ)將以相同的順序發(fā)生.這個(gè)順序?qū)⑹撬芯€程上執(zhí)行的任何語(yǔ)義上有效的指令交叉.
      • release:在釋放操作R之前順序的存儲(chǔ)操作不得重新排序?yàn)槌霈F(xiàn)R之后.
      • acquire:在獲取A之后順序的加載操作不得重新排序,使得其看起來(lái)發(fā)生在A后.
      • acquire_release:圍繞一個(gè)acquire_release操作,加載和存儲(chǔ)操作不能重新排序.
      posted @ 2024-01-26 08:26  Mesonoxian  閱讀(178)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 国产人妻一区二区三区四区五区六| 国产美女被遭强高潮免费一视频| 亚洲欧洲精品国产二码| 性欧美VIDEOFREE高清大喷水| 1024你懂的国产精品| 午夜大片免费男女爽爽影院| 国产一二三五区不在卡| 疯狂做受XXXX高潮国产| 国产成人高清精品免费软件| 亚洲中文字幕无码av永久| 亚洲 成人 无码 在线观看| 成人午夜电影福利免费| 久久一本人碰碰人碰| 亚洲成色av网站午夜影视| 天天澡日日澡狠狠欧美老妇| 92国产精品午夜福利免费| 强奷乱码中文字幕| 日韩精品二区三区四区| 国产av永久无码天堂影院| 99久久精品国产一区二区蜜芽| 成年午夜免费韩国做受视频| 国产JJIZZ女人多水喷水| 无码人妻斩一区二区三区| 五月综合婷婷开心综合婷婷| 光山县| 中文字幕乱码人妻综合二区三区| 国产成人综合亚洲精品国产| 91久久精品美女高潮不断 | 国产在线观看91精品亚瑟| 亚洲综合国产激情另类一区 | 乌什县| 国产成人精品区一区二区| 深夜释放自己在线观看| 大胸少妇午夜三级| 蜜臀在线播放一区在线播放| 亚洲国产成人无码电影| 亚洲色成人网站www永久| 国产精品www夜色视频| 精品超清无码视频在线观看| 久久国产精品成人免费| 高清在线一区二区三区视频|