本文将主要介绍Offload模式场景下异步计算编程时需要考虑的相关的编程方式比如异步数据传输、指定异步计算以及指定不需数据传输的内存分配等。
Intel® C++ Compiler提供了两个不同的C++ 编译指示来标示数据传输及等待其完成,一是用于指示异步数据传输:
#pragma offload_transfer <clauses> [ signal(<tag>) ]
第二个是用于标示等待异步传输完成:
#pragma offload_wait <clauses> wait(<tag>)
offload编译指示也可以伴随可选的signal或wait从句,如:
#pragma offload <clauses> [ signal(<tag>) ] [ wait(<tag>) ] <statement>
值得注意的是,offload_transfer以及offload_wait的pragma(编译指示)是单独的,其不会应用于之后的代码块。
- Offload异步数据传输
offload_transfer编译指示包括一个target标示及或者全部是in的标示或者全部是out的标示;在没有signal标示时,offload_transfer会发起及完成一次同步的数据传输;有signal标示时,offload_transfer仅初始化一次数据传输;offload_transfer编译指示也可以伴随一个wait标示用于等待数据传输完成;signal以及wait标示可用于作为异步操作的tag:
// Example 1: // Synchronous data transfer CPU -> MIC // Next statement executed after data transfer is completed #pragma offload_transfer target(mic:0) in(a,b,c) // Example 2: // Initiate asynchronous data transfer CPU -> MIC #pragma offload_transfer target(mic:0) in(a,b,c) signal(&a)
offload_wait编译指示包含一个target及wait标示,后者用于标明仅在tag相关的异步操作完成后才开始执行pragma:
// Example 3: // Wait for activity signaled by &p to be completed. Variable p is the tag. #pragma offload_wait target(mic:0) wait(&p)
- Offload异步内存管理
offload_transfer编译指示能用nocopy标示内存分配及回收,从而避免了负载开销较大的数据传输,通常nocopy方式用于循环外以便更好地降低分配开销:
// Example 4: #define ALLOC alloc_if(1) free_if(0) #define FREE alloc_if(0) free_if(1) #define REUSE alloc_if(0) free_if(0) // Allocate memory on the coprocessor (without also transferring data) #pragma offload_transfer target(mic:0) nocopy(p,q : length(l) ALLOC) … for (…) { // Use of allocated memory on the coprocessor for offloads #pragma offload target(mic:0) in(p:length(l) REUSE) out(q:length(l) REUSE) { // computation using p and q ... } } … // Free memory on the coprocessor (without also transferring data) #pragma offload_transfer target(mic:0) nocopy(p,q : length(l) FREE)
- Offload模式下的异步发送输入数据
对于此方式下,典型的使用场景是发起数据传输、执行一些CPU任务再开始offload计算,进而会使用到传输好的数据,被传送的变量数据必须在offload代码块开始执行时就可存取到:
// Example 5: // Initiate asynchronous data transfer MIC -> CPU #pragma offload_transfer target(mic:0) in(p,q,r) signal(&p) … … // Do the offload only after data has arrived #pragma offload target(mic:0) wait(&p) { // offload computation … = p; }
- Offload模式下的异步接收输出结果
在异步offload模式下,异步计算的结果将会之后传送回host主机端,这以通过offload_transfer的方式发起拷贝,之后当需要结果的时候,通过使用offload_wait来存取到数据:
// Example 6a: // Perform the offload computation but don’t copy back results immediately #pragma offload target(mic:0) nocopy(p) { p = …; } // Initiate asynchronous data transfer MIC -> CPU #pragma offload_transfer target(mic:0) out(p) signal(&p) … … // Wait for data to arrive #pragma offload_wait target(mic:0) wait(&p)
- Offload异步计算
当host主机发起一个异步offload计算时,当前的计算任务可以接着进行,只需要在之后的代码中加入offload_wait来用于等待offload计算完成即可:
// Example 6b: char signal_var; int *p; do { // Initiate asynchronous computation #pragma offload … in( p:length(1000) ) signal(&signal_var) { mic_compute(); } concurrent_cpu_activity(); #pragma offload_wait (&signal_var); } while (1);
总结:以上概括了典型的offload异步模式需要注意的编程方式,另外开发者在一些场景需要确定指定的某个运算是否完成时,可以使_Offload_signaled函数来检查此状态;开发者也可以通过使用offload_wait及offload_transfer编译指示来实现双缓冲算法等。通过使用异步offload模式,程序可以允许并行的数据传输及计算,且这种方法不需要host主机上的额外线程来管理,充分体现了流水线方式管理计算的思想。开发者可以在Intel® C++ Compiler的缺省安装目录下找到更多相关的编程细节:
- Linux*: /opt/intel/composer_xe_2015/Samples/en_US/C++/mic_samples/intro_sampleC
- Windows*: C:\Program Files (x86)\Intel\Composer XE 2015\Samples\en_US\C++