MIC C编程(offload模式)

编程特点

简单---隐藏大量细节,语法与OpenMPI类似(不需要开辟空间)

灵活---OpenMP MPI(但是用的不多)pThread等多种方式

传统---CPU编程一脉相承

 

MIC C扩展语言结构

编译指导方式(#pragma

offload

--表示之后的代码段将使用offload模式运行

  运行在其他设备上(MIC

--标识内存传递参数,传递过程对用户透明

  不需要手动写代码控制何时传入、何时传出

  不需要手动申请卡上内存空间

  不需要讲卡上内存与主机端内存空间手动对应

 

简单示例

#pragma offload target(mic)

for(i=0;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,不负责传出

--可选属性

--可附加lengthalloc_iffree_ifalignallocinto属性

--表示进入offload区域时,将数组arrayCPU内存中传递到MIC内存中,只传入不传出

--只需要传递数组,标量会自动以inout方式传递

 

 

语法详解out/inout/nocopy

可选属性

in基本相似

out表示离开offload区域时,将数组arrayMIC内存传递到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迭代次数

{

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迭代多次

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,但后者为单一语句,后面不跟代码段

在传输语句时加signalCPU不等待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

{

arr[i]=i*3.0/x;

}

 

变量和函数声明

_declspec(target(mic))

_attribute_((target(mic)))

修饰后,变量和函数可同时用于CPUMIC,二者等价,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卡上执行

可用于不同规模数据,或者CPUMIC协同计算的情况

 

语法详解mandatory

可选属性

没有参数

强制下面的代码段在MIC上运行,如果MIC设备不可用,则报错退出--不去CPU上跑

多用于必须在MIC上运行的代码,例如使用SIMD写的代码段

 

程序编译

使用Intel编译器

编译选项与原CPU程序完全一样

如果只想使用CPU,添加选项”-no-offload”

如果使用了OpenMP,添加选项”-openmp” (如果不加此选项,就在mic的单核上运行)

 

 

 

 

12-20 12:51