No.7_6 OpenCL 同步——異步拷貝

簡介

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_eventsevent_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);
}

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章