OpenCL 主機提醒事件、命令同步事件

1.在OpenCL中,事件是一個和事件發生(occurrence)相對應的數據結構。一次事件監視的可能是數據傳輸操作的結束,也可能監視的是內核的執行。可以通過三種主要的方式來使用事件:

主機提醒(host notification)--用來提醒主機,設備已經執行完一條命令的事件;
命令同步--用來強迫延遲執行,直到另一個事件發生的事件;
性能分析--用來監視執行一條命令所耗時長的事件。

性能分析是高性能應用程序開發過程中所要用到的十分重要的工具,它可以評估計算硬件和編程方法的性能,有了性能分析,就可以在不同的設備、內核以及數據劃分策略間進行比較。
一般而言,當爲內核生成相對應的工作項之後,他們之間的執行將是無序而且不穩定的。當然,如果工作項訪問的內存空間互不相關,程序的運行也就並無大礙,但如果各個工作項需要對同一塊數據做處理,事情就麻煩了。OpenCL提供了各種函數來對工作項的處理進行排序。
下面這個例子是展示主機提醒事件的工作過程。假設,需要帶調用函數clEnqueueReadBuffer,將大量的數據從設備傳輸到主機上。整個過程可能會非常耗時,所以,你可以通過將函數的第三個參數設置爲CL_FALSE,來將函數定義爲非阻塞型。這樣,函數clEnqueueReadBuffer便會立即返回,而主機則能在數據傳輸的過程中,進行其他任務的處理。
當數據傳輸結束後,可能還會需要主機應用程序來處理這些數據。這時,就需要聲明cl_event結構,並進行兩項相關性配置。其一是配置事件和數據傳輸命令的相關性;其二是配置事件和主機條用函數的相關性,這個函數將在傳輸命令執行完之後,被主機調用,因而這種函數也被稱爲回調函數。

將事件和命令關聯:很多函數都是將命令發送到命令隊列中,他們的名字開頭一般都形如clEnqueue,而且最後一個參數都是指向cl_event結構的指針。
將事件和回調函數做關聯
主機提醒的例子:代碼創建、配置兩個回調事件,Kernel_event和read_event,第一個事件和內核執行命令相關,第二條命令和主機讀取數據的命令相關。這兩個事件同時也和函數kernel_complete,函數read_complete分別相關。

2.命令同步事件
命令隊列處理命令的默認順序就是他們入列的順序。但如果命令被髮送到同一上下文的不同命令隊列中時,就很難區分他們各自之間的執行順序。但在事件的幫助之下,設置命令的執行順序又變成了可能。只需要強迫命令延時,等待相應的事件集執行完成。這些延時事件集也被稱爲等待列表。如果事件和命令執行相關聯,這個事件便被稱爲命令事件。如果事件和主機應用程序中的事件發生相關聯,這個事件就被稱爲用戶事件。
2.1等待命令和命令事件

  cl_uint num_events--命令的等待列表中cl_event對象的數量;
   const cl_event*wait_list--指向等待列表中cl_event對象的指針;

2.2等待列表和用戶事件
命令事件和用戶事件也有個明顯的區別,命令事件對應的是設備上所執行的命令,而用戶事件是由主機應用程序所生成的。有了用戶事件,你可以通過主機,而非設備,來暫緩命令的執行。
函數clCreateUserEvent的目的就是創建用戶事件:

cl_event clCreateUserEvent(cl_context context, cl_int *err)

函數返回的是cl_event對象,可以被放於任何一條命令的等待列表之中。並不特別指定某個命令隊列,用戶事件可以用到多個設備上。
如果想將用戶事件加到命令的等待列表中,命令的執行將會延遲到主機應用程序完成對事件的更新之後。整個過程需要調用函數clSetUserEventStatus,其函數簽名:

cl_int clSetUserEventStatus(cl_event event,cl_int status)

參數status的取值範圍只能是CL_COMPLETE(其值爲0)或一個負值。如果status設爲CL_COMPLETE,所有等待這個用戶事件的命令都將允許執行。如果status設爲一個負值,所有等待這個用戶事件的命令都將被終止執行。
主機應用程序將兩條命令發送到設備上:一個用來執行內核,一個用來讀取內核的輸出數據。相互之間的關係是:讀命令在內核執行命令完成之後開始執行,而內核執行命令又由用戶事件來觸發執行。因此,一旦用戶按鍵,用戶事件被觸發,那麼兩條命令都會相繼被執行。當讀命令完成之後,他會調用回調函數read_complete.
clEnqueueReadBuffer函數被稱爲非阻塞型(CL_FALSE),如果阻塞參數變爲CL_TRUE,函數將會等到讀操作運行完後,纔算完成。但是讀命令暫緩的是內核執行命令,而它要等到用戶事件完成之後才能執行完。如果clEnqueueReadBuffer處於等待狀態,這個函數後面的代碼京不會得到運行,整個應用程序也將處於暫停狀態。
2.3額外的命令同步函數
通過發送三種新的類型的命令,來進行這種同步化操作,這三種新命令就是標記命令、等待命令以及障礙命令。
標記命令:
clEnqueueMarker入列一條名爲標記命令的命令。將事件和之前的所有命令的執行關聯起來。函數的簽名:

cl_int clEnqueueMarker(cl_command_queue command_queue,cl_event *event)

這個函數被用來關聯event和標記命令,event可以被放在等待列表中,來提醒主機。
等待命令:
由clEnqueueWaitForEvents函數所生成的命令也被稱爲等待命令,他會暫停命令隊列的執行,直到它的等待列表中的命令全部執行完成。其函數簽名如下:

cl_int clEnqueueWaitForEvents(cl_command_queue queue,cl_uint num_events,const cl_event *wait_list)

最後兩個參數用來創建一個等待列表,這個列表和之前介紹的入列函數的等待列表很相似。但與後者只暫緩一條命令的執行不同,這個列表中的事件會暫緩列表中每條命令的執行。等待命令告訴命令隊列不要在等待列表中的事件完成之前,處理複製命令和寫命令。
這裏,clEnqueueWaitForEvents函數發送的命令可以暫緩隊列中命令的執行。在調用clSetUserEventStatus函數,設定用戶爲完成狀態,後面的命令就可以被正常的執行。
障礙命令:
clEnqueueBarrier函數並不能接受或配置事件,但函數的目的卻和clEnqueueWaieForEvents函數差不多,因此也可以放到這裏來討論。兩個函數都是暫緩隊列中命令的執行,但clEnqueueBarrier使用的卻不是等待列表,他是通過入列一個名爲障礙命令(barrier command)的命令來實現的。這條命令會在前面的命令執行結束前,一直阻止後面的命令的執行,函數簽名:

cl_int clEnqueueBarrier(cl_command_queue queue)

障礙命令適用於,兩條命令集合之間有執行上的先後關係。有了障礙命令,就可以迴避對事件和回調函數的使用。障礙命令會自動地調整命令之間的先後執行順序。
2.4獲取和事件關聯的數據
回調函數可以訪問函數的data參數,而非函數的event參數。但在調用clGetEventInfo函數之前,返回大量和事件有關的信息,例如事件的上下文,命令隊列以及和事件關聯的命令的類型。函數簽名:

cl_int clGetEventInfo(cl_event event, cl_event_info param_name,
size_t param_value_size,void*param_value, size_t*param_size)

大多數的取值都很簡單,但CL_EVENT_COMMAND_EXECUTION_STATUS和CL_EVENT_COMMAND_TYPE這兩個取值卻值得提一下,到目前爲止,我們所碰到過的表示名利給狀態的常數都是CL_COMPLETE,它表示的是命令執行完之後的狀態。但是一條命令的完整生命週期共包括以下四個階段:

入列--命令被入列到命令隊列中,用CL_QUEUED來指明;
提交--命令被提交給設備,用CL_SUBMITTED來指明;
運行--命令正在設備上執行,用CL_RUNNING來指明;
完成--命令完成執行,用CL_COMPLETE來指明。

只能調用clSetEventCallback和clSetUserEventStatus這兩個函數來訪問CL_COMPLETE狀態。
如果信息參數被設爲CL_EVENT_COMMAND_TYPE,返回值表示的是和事件相關的命令類型。返回結果是一個枚舉型cl_command_type結構。
CL_COMMAND_READ_BUFFER類型對應的是clEnqueueReadBuffer函數入列的命令。CL_COMMAND_USER,沒有相對應的入列命令的函數,它表明cl_event對象是由clCreateUserEvent函數所創建的用戶事件。

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