OpenMP 使用介紹
-
OpenMP 基本概念
Open Multi-Processing的縮寫,是一個應用程式介面(API),可用于顯式指導多執行緒、共享記憶體的并行性,
在專案程式已經完成好的情況下不需要大幅度的修改源代碼,只需要加上專用的pragma來指明自己的意圖,由此編譯器可以自動將程式進行并行化,并在必要之處加入同步互斥以及通信,當選擇忽略這些pragma,或者編譯器不支持OpenMp時,程式又可退化為通常的程式(一般為串行),代碼仍然可以正常運作,只是不能利用多執行緒來加速程式執行,OpenMP提供的這種對于并行描述的高層抽象降低了并行編程的難度和復雜度,這樣程式員可以把更多的精力投入到并行演算法本身,而非其具體實作細節,對基于資料分集的多執行緒程式設計,OpenMP是一個很好的選擇,
OpenMP支持的語言包括C/C++、Fortran;而支持OpenMP的編譯器VS、gcc、clang等都行,可移植性也很好:Unix/Linux和Windows -
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;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/185018.html
標籤:其他
