簡介
OpenCL C 編程語言內置了類似 memcpy
的函數,內核代碼通過調用該函數,可以在局部內存和全局內存區域之間拷貝數據。當設備內存獨立於主機內存時,無需主機內存參與數據週轉,就能在不同內存區域之間拷貝數據,這提高了數據拷貝效率。
函數描述
異步拷貝
下面的 OpenCL C 內置函數在全局內存和局部內存區域之間執行異步拷貝,同時還從全局內存區域預取數據到全局高速緩存。
event_t async_work_group_copy(__local gentype *dst,
const __global gentype *src,
size_t num_gentypes,
event_t event)
event_t async_work_group_copy(__global gentype *dst,
const __local gentype *src,
size_t num_gentypes,
event_t event)
以異步方式,從內存區域 src
拷貝 num_gentypes
個元素到內存區域 dst
。工作組中所有工作項都會執行異步拷貝操作,也就是說該內置函數必須被工作組中的每個工作項執行,而且傳遞的參數需要保持相同,否則會出現不可預知的錯誤。例如,下面代碼中:
local_index = get_local_id(0);
event_t event = async_work_group_copy(&buffer[local_index],
&src1[global_index], 1, 0);
對於不同的工作項,傳遞的參數並不一致,這時就會出現不可預知的錯誤。
這些異步拷貝函數返回一個 event_t
類型的事件對象,wait_group_events
可以通過它們來等待異步拷貝操作執行完成。如果有多個異步拷貝操作,可以將它們關聯爲同一個事件。將前面返回的事件對象 event
作爲後面 async_work_group_copy
調用的參數即可,這就允許一個事件被多個異步拷貝共享。如果不需要共享,在調用 async_work_group_copy 函數時將 event
參數設置爲 0 即可。如果 event
參數爲非 0 值,將在調用 async_work_group_copy
後返回相同值。
注意:該函數並沒有執行任何隱式的數據源同步,這就需要在執行拷貝之前使用一個 barrier 來做同步。
事件等待
等待事件,用來表示 async_work_group_copy 操作已經完成。在該等待事件函數執行返回後,event_list 中的事件對象將被釋放。
void wait_group_events(int num_events, event_t *event_list)
該函數必須被工作組中所有工作項執行,並且它們在執行內核時使用的 num_events
和 event_list
中指定的事件對象必須相同,否則會出現不可預知的結果。
注意:在退出之前,內核必須使用
wait_group_events
內置函數來等待所有的異步拷貝操作完成,否則會出現不可預知的結果。
預取操作
從全局內存區域預取數據到全局高速緩存。
void prefetch(const __global gentype *p, size_t num_gentypes)
從全局內存區域預取 num_gentypes
* sizeof(gentype) 字節到全局高速緩存。該預取指令用於工作組中的某個工作項,而且並不會影響內核函數的功能。
示例程序
示例程序 OpenCLAsyncCopy 在 Ubuntu 平臺上執行,內核代碼在局部內存區域聲明瞭一個緩衝區 buffer
,其長度爲工作組大小。然後將全局內存中的數據拷貝到局部內存區域,待拷貝完成後每個工作項根據其局部 ID 標識更新緩衝區。最後以工作組大小爲單位再將其拷貝回全局內存區域,內核代碼如下:
__kernel void kernel_dot(__global int *dst, __global int *src1, __global int *src2)
{
// 定義局部緩衝區,在同一工作組的工作項之間共享
__local int buffer[WORKGROUP_SIZE];
// 每個工作組中第一個工作項的偏移
const size_t offset = get_group_id(0) * WORKGROUP_SIZE;
// 執行異步拷貝操作
event_t event = async_work_group_copy(buffer, &src1[offset], WORKGROUP_SIZE, 0);
// 獲取工作組中的每個工作項
const int index = get_local_id(0);
// 等待異步拷貝完成
wait_group_events(1, &event);
buffer[index] *= 2;
// 工作組中所有的工作項執行到這裏。等待對局部緩衝區的訪問完成
barrier(CLK_LOCAL_MEM_FENCE);
event = async_work_group_copy(&dst[offset], buffer, WORKGROUP_SIZE, 0);
// 等待異步拷貝完成
wait_group_events(1, &event);
}
參考
- OpenCL異構並行計算:原理、機制與優化實踐
- https://www.khronos.org/registry/OpenCL/sdk/1.1/docs/man/xhtml/