MIC C編程(offload模式)
編程特點
簡單---隱藏大量細節,語法與OpenMPI類似(不需要開闢空間)
靈活---OpenMP MPI(但是用的不多)pThread等多種方式
傳統---與CPU編程一脈相承
MIC C擴展語言結構
編譯指導方式(#pragma)
offload
--表示之後的代碼段將使用offload模式運行
運行在其他設備上(MIC)
--標識內存傳遞參數,傳遞過程對用戶透明
不需要手動寫代碼控制何時傳入、何時傳出
不需要手動申請卡上內存空間
不需要講卡上內存與主機端內存空間手動對應
簡單示例
#pragma offload target(mic)
for(i=0;i<LEN;i++)
{
printf(“Index:%d\n”,i);
}
循環和printf語句在MIC上執行,沒有數據傳輸
此時循環是串行的,因爲沒有並行引語
offload語法
#pragma offload specifier[,specifier...]
specifier |
示例 |
含義 |
target |
target(mic:0) |
使用什麼設備運行 |
if |
if(N>1000) |
是否使用該設備 |
in |
in(p:length(LEN) alloc_if(1)) |
數據傳入device |
out |
out(p:length(LEN)) |
數據從device傳出 |
inout |
inout(p:length(LEN) align(8)) |
數據既傳入又傳出 |
nocopy |
nocopy(p) |
只開闢空間不傳遞 |
signal |
signal(tag) |
發送信號 |
wait |
wait(tag1,tag2) |
等待信號 |
mandatory |
mandatory |
必須用MIC運行 |
其中in/out/inout/nocopy可用的屬性有
屬性 |
示例 |
含義 |
length |
length(LEN) |
元素個數 |
alloc_if |
alloc_if(1) |
是否開闢內存 |
free_if |
free_if(N>0) |
offload後是否釋放 |
align |
align(8) |
對齊 |
alloc |
alloc(p[10:100]) |
開闢數組一部分 |
into |
into(p[10:100]) |
傳遞數組一部分 |
語法詳解target
#pragma offload target(mic) mic這裏目前只支持MIC卡
--這是必須使用的屬性
--表示下面的代碼段使用MIC運行
--現階段,target的參數只支持“mic”
--使用哪塊卡,由驅動決定,如果系統沒有MIC卡,則使用CPU運行
target(mic:num)
--num>=-1;當爲-1時,系統自動選擇一塊mic卡,如果1號卡不存在或者有故障,則程序報錯退出
--設備號=num%mic數量(也就是說num可以大於mic卡數,循環的)
語法詳解in
#pragma offload target(mic) in(array) --傳入CPU中的array,不負責傳出
--可選屬性
--可附加length、alloc_if、free_if、align、alloc、into屬性
--表示進入offload區域時,將數組array從CPU內存中傳遞到MIC內存中,只傳入不傳出
--只需要傳遞數組,標量會自動以inout方式傳遞
語法詳解out/inout/nocopy
可選屬性
與in基本相似
out表示離開offload區域時,將數組array從MIC內存傳遞到CPU內存,只傳出不傳入
inout表示進入offload區域時,將數組從CPU內存傳輸到MIC內存,離開時將數組傳回
nocopy表示僅開闢或保留空間,不傳輸數據
注意事項:
array是靜態聲明時,不用加長度參數
array是動態開闢時(堆),需要加長度參數length
array長度相同,或靜態聲明時,可以在一個傳輸區域內傳輸多個。例:in(a,b)
默認進入offload時開闢內存,離開時釋放
in/out/inout/nocopy在一句offload中可以出現多次,但每個數組名只能出現一次
傳輸屬性詳解length
傳輸數組的元素個數,非數組長度!
只能使用在動態開闢的數組中
數組長度相同時可以共用
示例:
float *pArray,*pArray1;
pArray=(float*)malloc(LEN*sizeof(float));
pArray1=(float*)malloc(LEN*sizeof(float));
#pragma offload target \
in(pArray,pArray1:length(LEN)) 共用啦
{……}
傳輸屬性詳解alloc_if,free_if
控制是否在傳輸前開闢卡上內存空間和傳完是否釋放
常結合nocopy屬性應用
例:
#pragma offload target \
in(pArray:length(LEN) free_if(0)) 裏面是boolean型,“0”表示不釋放空間
//進入下一個offload時,pArray的數據仍然存在(計算完後pArray存在於MIC中,不需要再從CPU端傳輸多MIC端了)
#pragma offload target \
nocopy(pArray:length(LEN) alloc_if(0)) 不用再釋放空間了,也不用in再次傳輸了
//此時不需要開闢空間,否則會覆蓋原有數據
(這裏優化點:1:傳輸 2:開闢 3:釋放)
數據傳輸優化Nocopy
p_c=...//p_c在每次迭代中值不變
for(i=0;i<steps;i++) //迭代次數
{
p_in=...;//每次迭代計算時,p_in的值變化
#pragma offload target(mic) \
in(p_in:length(...)) in(p_c:length(...)) out(p_out:lenth(...))
{
kernel(p_in,p_c,p_out);
}
}
...=p_out;//CPU端在所有迭代完成之後纔用到p_out的值
這裏的冗餘是p_c冗餘傳入p_out冗餘輸出
優化方案:
p_c=...;//p_c在每次迭代中值不改變
#pragma offload target(mic) \
in(p_c:length(...) alloc_if(1) free_if(0)) \ //開闢不釋放
nocopy(p_in:length(...) alloc_if(1) free_if(0)) \
nocopy(p_out:length(...) alloc_if(1) free_if(0))
{
}//申請空間,並且不釋放,傳遞p_c的值
for(i=0;i<steps;i++){//迭代多次
p_in=...;
#pragma offload target(mic) \
in(p_in:length(...) alloc_if(0) free_if(0)) \ //p_in省去了steps次的開闢釋放空間
nocopy(p_c) nocopy(p_out) //這裏p_c省去了輸入,p_out省去了輸出
{
kernel(p_in,p_c,p_out);
}}
//以下是把最後的p_out輸出來
#pragma offload target(mic) \
nocopy(p_c:length(...) alloc_if(0) free_if(1)) \
nocopy(p_in:length(...) alloc_if(0) free_if(1)) \
out(p_out:length(...) alloc_if(0) free_if(1))
{...}
...=p_out
傳輸屬性詳解align
控制卡上內存開闢的對齊大小
單位爲字節
內存不對齊時會影響性能
一般用於傳輸不規則的結構體(定義順序)
傳輸屬性詳解alloc/into
alloc開闢卡上內存,大小爲數組的一部分
into傳輸數據,大小爲數組的一部分
不能與inout/nocopy同用
傳輸屬性特殊用法
#pragma offload target \
in(p[10:100]:alloc(p[5:1000]))
在設備開闢了1000個元素的數組p,數組下表的可用範圍是從5開始,即5-10004.然後將主機端從p[10]開始的100個元素傳到設備端的p[10]-p[109]的位置(mic卡上)
需要程序員保證不會越界
#pragma offload ... \
in(p[0:500]:into(p1[500:500]))
將主機端p[0]開始的500個元素的值,複製到設備端p1[500]-p1[999]的響應位置
如果同一offload語句有多個into,執行順序未知,因此需要注意目的數組範圍不能重疊
into不是簡單拷貝,維度不同不能傳輸
語法詳解signal/wait
#pragma offload target(mic) signal(tag)
可選屬性,異步傳輸
等同於offload_transfer/offload_wait,但後者爲單一語句,後面不跟代碼段
在傳輸語句時加signal,CPU不等待MIC運行結束即繼續運行。知道遇到wait語句
wait語句等待signal語句傳輸完成再繼續運行
參數爲一個變量或者數組名(即使同事傳遞多個)
示例:
float *in1;
#pragma offload target(mic:0) signal(in1)
{
mic_compute();
}
cpu_compute(); //本函數與上面的MIC函數並行執行
#pragma offload_wait target(mic:0) wait(in1) //這裏與MIC合流
offload+OpenMP
在offload語句作用範圍內使用OpenMp,即可在MIC上並行計算
例:
#pragma offload target(mic) out(arr:length(LEN))
#pragma omp parallel for
for(i=0;i<LEN;++i)
{
arr[i]=i*3.0/x;
}
變量和函數聲明
_declspec(target(mic))
_attribute_((target(mic)))
修飾後,變量和函數可同時用於CPU和MIC,二者等價,attribute時注意有兩層括號
例:
_attribute_((target(mic))) int a;
-attribute_((target(mic))) void fun();
變量和函數聲明(批量)
同時聲明多個變量和函數
兩種形式
#pragma offload_attribute(push,target(mic))
//函數或變量聲明
#pragma offload_attribute(pop)
或者:
#pragma offload_attribute(target(mic))
//函數或變量聲明
#pragma offload_attribute(target(none))
判斷代碼段是否運行在MIC上
檢查是否定義了宏_MIC_
#ifdef_MIC_
...
#else
...
#endif
需要封裝在函數中,而不能直接用於offload代碼段
_attribute_((target(mic))) void offload_check(void)
{
#ifdef _MIC_
printf(“check func:Run on the mic!\n”);
#else
printf(“check func:Run on the cpu!\n”);
#endif
}
語法詳解if
#pragma offload target(mic) if(flag) //flag 是boolean型的
可選屬性
如果flag爲真,則使用MIC執行代碼段,否則使用CPU執行
例:#pragma offload target(mic) if(N>100)
表示如果N大於100就在MIC卡上執行
可用於不同規模數據,或者CPU、MIC協同計算的情況
語法詳解mandatory
可選屬性
沒有參數
強制下面的代碼段在MIC上運行,如果MIC設備不可用,則報錯退出--不去CPU上跑
多用於必須在MIC上運行的代碼,例如使用SIMD寫的代碼段
程序編譯
使用Intel編譯器
編譯選項與原CPU程序完全一樣
如果只想使用CPU,添加選項”-no-offload”
如果使用了OpenMP,添加選項”-openmp” (如果不加此選項,就在mic的單核上運行)