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

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

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

      OpenMP學習 第十章 超越通用核心的多線程

      第十章 超越通用核心的多線程


      基于通用核心的附加子句

      • 并行構造的附加子句:
        • num_threads(integer-expression)
          用于設置線程總數.
        • if(scalar-expression)
          用于為并行構造提供條件分支.
        • copyin(list)
        • proc_bind(master|close|spread)

      為了測試num_threads子句與if子句的用法,構造下面所示原型:

      #include <iostream>
      #include <omp.h>
      
      int main()
      {
          int NTHREAD, x;
          std::cin >> x;
      
          #pragma omp parallel if(x>1) \ 
                  num_threads(x/2)    //通過\來實現跨行
          {
              if(omp_get_thread_num()==0)
                  NTHREAD = omp_get_num_threads();
          }
          std::cout << "the num of thread is: " << NTHREAD;
      
          return 0;
      }
      

      通過程序實驗,證明了其相關用法.

      • 共享工作循環構造的附加子句:

        • lastprivate(list)
          如同private與firstprivate子句一樣,其為列表中每個變量創建一個私有副本,在區域結束時,列表中每個變量的原始變量將被賦值為最后一次迭代值.
        • schedule子句中的附加調度類型:
          • 啟發式調度(guided):動態調度的另一種形式,其中chunk_size一開始是一個大值,每次執行新的分塊迭代后chunk_size都會減少,直至chunk_size的最小值.
          • 自動調度(auto):編譯器和運行時根據自己的選擇來安排循環迭代.
          • 運行時調度(runtime):調度和可能的chunk_size來自內部控制變量.
            據文檔所言,其提供了omp_set_schedule與omp_get_schedule兩個函數與omp_sched_t枚舉類來處理.但是,實際編碼時并未成功使用相關函數及枚舉類.
        • collapse(n)
          規定共享工作循環構造之后的n個循環將被合并為一個隱式循環.任何額外的子句,包括數據環境子句或歸約都會應用到這個隱式循環中.
      • 任務構造的附加值局:

        • untied
          該子句用于限制任務隊列增長,在任務隊列增長時避免任務隊列增長速度過大.
        • priority(priority-value)
          該子句可以顯式提示任務的執行優先級,優先級值的范圍為[0,max-task-prioriity-var].
          最大值可以通過環境變量OMP_MAX_TASK_PRIORITY設置.
          也可以通過omp_get_max_task_priority(void)函數查詢.
        • depend(dependence-type:list)
          用于處理依賴情況下的任務,分析模式類似于DAG(有向無環圖).
          其中dependence-type包括out,in和inout三種,帶有in依賴類型的變量會導致任務等待另一個任務完成,該任務在帶有out依賴類型的子句中具有相同的變量.
        • if(scalar-expression)
          如果if子句中表達式為false,那么任務將不會被延遲執行.
        • final(scalar expression)
          當final子句中的表達式為true,那么任務將會被立即執行.
        • mergeable
          用于指示編譯器是否可以將兩個或多個連續的任務合并為一個任務.
      • 創建一個顯式任務調度點:

      #pragma omp taskyield
      
      • 創建一個任務循環構造:
      #pragma omp taskloop [clause[, clause] ...]
          //for-loop
      
      • 創建一個同步任務組:
      #pragma omp taskgroup [clause[, clause] ...]
      {
          //body of taskgroup
      }
      

      為了理解depend子句的使用,下面通過一個實例來幫助理解:

      #include <omp.h>
      
      int main()
      {
          int A,B,C,G,F;
      
          #pragma omp parallel shared(A,B,C,G,F)
          {
              #pragma omp task depend(out:A)
                  TaskA(&A);
              #pragma omp task depend(in:A,G)
                  TaskB(&B);
              #pragma omp task depend(in:A) depend(out:C)
                  TaskC(&C);
              #pragma omp task depend(in:A) depend(out:G)
                  TaskG(&G);
              #pragma omp task depend(in:C,G)
                  TaskF(&F);
          }
      
          return 0;
      }
      

      通用核心中缺失的多線程功能

      • threadprivate

      OpenMP的基本內存模型將內存視為一組給內存中的地址命名的變量.除了shared和private兩類,OpenMP還定義了第三種內存類型: threadprivate.

      threadprivate內存是一個線程的私有內存,它不能被其他線程訪問.然而,其內存中的變量在各個例程中具有可見性. 在非正式情況下,可以認為threadprivate內存是線程的私有內存.它不能被其他線程訪問.

      threadprivate是一個聲明性指令,這意味著它出現在程序中聲明變量的地方,并影響其聲明的語義.

      • 聲明threadprivate內存:
      #pragma omp threadprivate(list)
      

      為了理解threadprivate的使用,我們回到第七章所述的鏈表程序:

      #include <iostream>
      #include <cstdlib>
      #include <omp.h>
      import <format>;
      
      #define NODE_NUM 20
      #define CHUNK 2
      #define NTHREADS 3
      
      typedef struct node {
      	int data;
      	int procResult;
      	struct node* next;
      	node() :data(0), procResult(0), next(nullptr) {}
      }Node, * List;
      
      int count = 0;
      #pragma omp threadprivate(count)
      
      void incCount()
      {
      	count++;
      	return;
      }
      
      void initList(List p)
      {
      	Node* root{ p };
      	Node* temp_node;
      
      	p->data = 0;
      	for (int i = 1; i < NODE_NUM; i++) {
      		temp_node = new Node;
      		temp_node->data = i;
      		root->next = temp_node;
      		root = temp_node;
      	}
      	return;
      }
      
      void processWork(Node* n)
      {
      	n->procResult = (n->data * n->data);
      	return;
      }
      
      void deleteList(List p)
      {
      	Node* temp_node = p->next;
      	for (; p != temp_node;) {
      		temp_node = p;
      		while (temp_node->next != nullptr && temp_node->next->next != nullptr)
      			temp_node = temp_node->next;
      		delete temp_node->next;
      		temp_node->next = nullptr;
      	}
      	delete p;
      	return;
      }
      
      int main()
      {
      	List list = new Node;
      	Node** parr = new Node * [NODE_NUM];
      	initList(list);
      
      	#ifdef NTHREADS
      	omp_set_num_threads(NTHREADS);
      	#endif // NTHREADS
      
      	Node* p;
      	#pragma omp parallel
      	{
      		#pragma omp single
      		{
      			p = list;
      			while (p != nullptr)
      			{
      				#pragma omp task firstprivate(p)
      				{
      					incCount();
      					processWork(p);
      					std::cout << std::format("in the {} thrd, the count is {}",
      						omp_get_thread_num(),
      						count
      					) << std::endl;
      				}//end of task creation
      				p = p->next;
      			}
      		}//end of single region
      	}//end of parallel region
      
      	deleteList(list);
      	return 0;
      }
      

      我們在鏈表程序的基礎上添加了一個threadprivate內存的count,用于統計在線程中執行的task數量.threadprivate數據與特定線程相綁定,因此會在程序中引入錯誤源.

      • master

      master 構造定義了一個由線程組的主線程執行的工作塊.與single構造不同,它的構造末尾沒有隱式的柵欄.

      • 聲明一個master構造:
      #pragma omp master
      {
          //body of master
      }
      
      • atomic

      atomic 構造確保了一個變量作為一個獨立的,不間斷的動作被讀取,寫入或更新.其保護了一個變量,避免了并發線程對一個存儲位置進行多次同步更新的可能性.

      atomic構造與critical構造有很大共同點,如果多個線程試圖同時執行一個atomic構造,"第一個線程"將執行原子操作,而其他線程將等待輪到自己

      atomic構造中通過子句定義原子操作的類型,其中最常見的有三種:讀,寫和更新(不包括捕獲).默認情況(不包含子句)是更新.

      clause 原子操作示例
      read v=x;
      write x=expr;
      update
      (default)
      x++;x--;++x;--x;
      x = expr;v = expr(x);

      現在讓我們回到第四章中關于Pi數值積分的部分.

      #include <iostream>
      #include <omp.h>
      #include <fstream>
      import <format>;
      
      #define TURNS 100
      #define PI 3.141592653589793
      long double num_steps = 1e8;
      double step;
      
      int main()
      {
          std::ofstream out;
          out.open("example.csv", std::ios::ate);
          out << "NTHREADS,pi,err,run_time,num_steps" << std::endl;
          
          double sum = 0.0;
          for (int NTHREADS = 1; NTHREADS < TURNS; NTHREADS++) {
              double start_time, run_time;
              double pi, err;
      
              pi = sum = 0.0;
              int actual_nthreads;
      
              step = 1.0 / (double)num_steps;
              omp_set_num_threads(NTHREADS);
              start_time = omp_get_wtime();
      
              #pragma omp parallel
              {
                  int id = omp_get_thread_num();
                  int numthreads = omp_get_num_threads();
                  double x;
                  double partial = 0.0;
      
                  if (id == 0)
                      actual_nthreads = numthreads;
                  int istart = id * num_steps / numthreads;
                  int iend = (id + 1) * num_steps / numthreads;
                  if (id == (numthreads - 1))
                      iend = num_steps;
      
                  for (int i = istart; i < iend; i++) {
                      x = (i + 0.5) * step;
                      partial += 4.0 / (1.0 + x * x);
                  }
      
                  #pragma omp atomic
                      sum += partial;
              }//end of parallel
      
              pi = step * sum;
              err = pi - PI;
              run_time = omp_get_wtime() - start_time;
      
              std::cout << std::format("pi is {} in {} seconds {} thrds.step is {},err is {}",
                  pi,
                  run_time,
                  actual_nthreads,
                  step,
                  err
              ) << std::endl;
              out << std::format("{},{:.15f},{:.15f},{:.15f},{}",
                  NTHREADS,
                  pi,
                  err,
                  run_time,
                  num_steps
              ) << std::endl;
          }
          out.close();
      
          return 0;
      }
      

      我們在這里將critical構造更改為atomic構造實現了相同的功能.

      然而,雖然類似于critical構造,但是atomic構造只適用于直接涉及內存中存儲位置的操作,也就是說:

      #pragma omp atomic
          full_sum+=foo();
      

      其中函數foo()的執行不受atomic構造的保護,其等價于:

      tmp = foo();
      #pragma omp atomic
          full_sum+=tmp;
      

      這意味著foo()執行過程中很可能發生數據競爭.

      • OMP_STACKSIZE

      OpenMP被設計為支持多種系統,操作系統代表正在執行的程序對進程進行管理.

      進程分叉出與其關聯的線程.

      當操作系統創建線程時,它為每個線程預留了一些本地內存,這個內存以棧的形式進行管理.

      棧的大小是有限的,如果在線程內部運行的代碼創建了大的對象,棧內存可能會溢出,導致潛在的災難性失敗.

      為了解決這個問題,OpenMP定義了一個叫做stacksize-var的內部控制變量,它控制線程組中每個線程相關聯的內存棧的大小.

      設置stacksize-var的命令如下:

      export OMP_STACKSIZE=size
      

      OpenMP定義了一系列單位用于處理size:

      • size設置以1024字節為單位的大小
      • sizeB設置以1字節為單位的大小
      • sizeK設置以1024字節為單位的大小
      • sizeM設置以1024 * 1024字節為單位的大小
      • sizeG設置以1024 * 1024 * 1024字節為單位的大小

      舉例:

      export OMP_STACKSIZE="200K"http://200*1024 bytes
      
      • omp_get_max_threads

      omp_get_num_threads用于詢問OpenMP運行時線程組有多少個線程,但是只能在同一個并行區域內調用.

      但是有時候,需要一個可以從并行區域外調用的函數,以找到后續parallel構造所創建的線程組中可能獲得的最大線程數..

      此時就應當使用 omp_get_max_threads.

      int omp_get_max_threads(void)
      

      為了理解其使用,我們提供下面一個例子:

      #include <iostream>
      #include <omp.h>
      
      int main()
      {
          int nthread_1, nthread_2;
          omp_set_num_threads(2);
          nthread_1 = omp_get_max_threads();
      
          #pragma omp parallel
          {
              if (omp_get_thread_num() == 0)
                  std::cout << nthread_1 << std::endl;
          }
      
          omp_set_num_threads(4);
          nthread_2 = omp_get_max_threads();
      
          #pragma omp parallel
          {
              if (omp_get_thread_num() == 0)
                  std::cout << nthread_2 << std::endl;
          }
      
          return 0;
      }
      

      其最終得到的結果為:

      2
      4
      

      證明了我們調用omp_get_max_threads()所得到的結果的正確.

      • omp_set_dynamic

      一個OpenMP程序通常由多個被并行區域分隔的順序部分組成.OpenMP運行時會嘗試對一個并行區域到下一個并行區域時,優化線程組的大小,這成為動態模式(dynamic mode).

      這意味著OpenMP運行時必須假定與線程相關聯的資源可能在并行區域之間發生變化.如果希望在并行區域之間重用線程資源,則需要告訴運行時系統關閉動態線程調度的功能.

      通過omp_set_dynamic(),我們可以啟用或禁用動態模式.

      • 啟用或禁用動態模式:
      void omp_set_dynamic(int dyn_threads)
      

      其中dyn_threads為一個bool值,其為true時將允許線程組大小再并行區域之間變化.

      • omp_in_parallel

      讓活動線程的數量超過物理核心的數量會影響性能,因為操作系統會因為郭隊線程交換而消耗資源,這就是所謂的認購超額.

      因此,有些時候想知道自己是否在一個活躍的并行區域內,這樣就可以調整后續并行區域中創建的線程數量.

      omp_in_parallel()函數用于查詢代碼是否在并行區域內,如果在活動的并行區域內,那么返回true.

      • 查詢代碼是否在并行區域內:
      void omp_in_parallel();
      
      posted @ 2024-01-25 18:31  Mesonoxian  閱讀(303)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲色一色噜一噜噜噜| 久久香蕉国产线看观看怡红院妓院| 霍城县| 英吉沙县| 日韩中文字幕一区二区不卡| 国产精品亚洲片夜色在线| 干老熟女干老穴干老女人| 无码人妻精品一区二区三区下载| 国产三级a三级三级| 国产爆乳无码视频在线观看3| 精品国际久久久久999波多野| 久久96热在精品国产高清| 四虎影院176| 宜宾县| 无码中文字幕av免费放| 国产精品自偷一区在线观看 | 国产成人av电影在线观看第一页| 国产精品美女黑丝流水| 亚洲无人区码二码三码区| 日韩中av免费在线观看| 九九热精品在线免费视频| 搡bbbb搡bbb搡| 亚洲av首页在线| 国产精品亚洲二区亚瑟| 亚洲天堂精品一区二区| 亚洲精品男男一区二区| 亚洲精品国产suv一区88| 大地资源高清免费观看| 一区二区丝袜美腿视频| 国产99视频精品免费视频36| 美女黄网站人色视频免费国产| 国产在线视频精品视频| 国产一区二区爽爽爽视频| 国产蜜臀av在线一区在线| 久久久久久久久久久免费精品| 激情四射激情五月综合网| 亚洲精品香蕉一区二区| 久久人人爽人人人人爽av| xxxxbbbb欧美残疾人| 黑巨人与欧美精品一区| 乳山市|