天天看點

OpenCL概述 (Introduction to OpenCL)

摘要: OpenCL可以實作混合裝置的并行計算,這些裝置包括CPU,GPU,以及其它處理器,比如Cell處理器,DSP等。使用OpenCL程式設計,可以實作可移植的并行加速代碼。[但由于各個OpenCL device不同的硬體性能,可能對于程式的優化還要考慮具體的硬體特性]。

1、OpenCL架構

OpenCL可以實作混合裝置的并行計算,這些裝置包括CPU,GPU,以及其它處理器,比如Cell處理器,DSP等。使用OpenCL程式設計,可以實作可移植的并行加速代碼。[但由于各個OpenCL device不同的硬體性能,可能對于程式的優化還要考慮具體的硬體特性]。

通常OpenCL架構包括四個部分:

  • 平台模型(Platform Model)
  • 執行模型(Execution Model)
  • 記憶體模型(Memory Model)
  • 程式設計模型(Programming Model)

2、OpenCL平台模型

不同廠商的OpenCL實施定義了不同的OpenCL平台,通過OpenCL平台,主機能夠和OpenCL裝置之間進行互動操作。現在主要的OpenCL平台有AMD、Nvida,Intel等。OpenCL使用了一種Installable Client Driver模型,這樣不同廠商的平台就能夠在系統中共存。在我的計算機上就安裝有AMD和Intel兩個OpenCL Platform[現在的OpenCL driver模型不允許不同廠商的GPU同時運作]。

OpenCL概述 (Introduction to OpenCL)

OpenCL平台通常包括一個主機(Host)和多個OpenCL裝置(device),每個OpenCL裝置包括一個或多個CU(compute units),每個CU包括又一個或多個PE(process element)。 每個PE都有自己的程式計數器(PC)。主機就是OpenCL運作庫宿主裝置,在AMD和Nvida的OpenCL平台中,主機一般都指x86 CPU。

對AMD平台來說,所有的CPU是一個裝置,CPU的每一個core就是一個CU,而每個GPU都是獨立的裝置。

OpenCL概述 (Introduction to OpenCL)

3、OpenCL程式設計的一般步驟

下面我們通過一個執行個體來了解OpenCL程式設計的步驟,假設我們用的是AMD OpenCL平台(因為本人的GPU是HD5730),安裝了AMD Stream SDK 2.6,并在VS2008中設定好了include,lib目錄等。

首先我們建立一個控制台程式,最初的代碼如下:

1: #include "stdafx.h"      
2: #include <CL/cl.h>      
3: #include <stdio.h>      
4: #include <stdlib.h>       
6: #pragma comment (lib,"OpenCL.lib")      
int main(int argc, char* argv[])      
{      
return 0;      
}      

第一步,我們要選擇一個OpenCL平台,所用的函數就是

OpenCL概述 (Introduction to OpenCL)

通常,這個函數要調用2次,第一次得到系統中可使用的平台數目,然後為(Platform)平台對象配置設定空間,第二次調用就是查詢所有的平台,選擇自己需要的OpenCL平台。代碼比較長,具體可以看下AMD Stream SDK 2.6中的TemplateC例子,裡面描述如何建構一個robust的最小OpenCL程式。為了簡化代碼,使程式看起來不那麼繁瑣,我直接調用該函數,選取系統中的第一個OpenCL平台,我的系統中安裝AMD和Intel兩家的平台,第一個平台是AMD的。另外,我也沒有增加錯誤檢測之類的代碼,但是增加了一個status的變量,通常如果函數執行正确,傳回的值是0。

1: #include "stdafx.h"      
2: #include <CL/cl.h>      
3: #include <stdio.h>      
4: #include <stdlib.h>      
6: #pragma comment (lib,"OpenCL.lib")      
8: int main(int argc, char* argv[])      
9: {      
10: cl_uint status;      
11: cl_platform_id platform;      
13: status = clGetPlatformIDs( 1, &platform, NULL );      
15: return 0;      
16: }      

第二步是得到OpenCL裝置,

OpenCL概述 (Introduction to OpenCL)

這個函數通常也是調用2次,第一次查詢裝置數量,第二次檢索得到我們想要的裝置。為了簡化代碼,我們直接指定GPU裝置。

1: #include "stdafx.h"      
2: #include <CL/cl.h>      
3: #include <stdio.h>      
4: #include <stdlib.h>      
6: #pragma comment (lib,"OpenCL.lib")      
8: int main(int argc, char* argv[])      
9: {      
10: cl_uint status;      
11: cl_platform_id platform;      
13: status = clGetPlatformIDs( 1, &platform, NULL );      
15: cl_device_id device;      
17: clGetDeviceIDs( platform, CL_DEVICE_TYPE_GPU,      
18: 1,      
19: &device,      
20: NULL);      
22: return 0;      
23: }      

下面我們來看下OpenCL中Context的概念:

通常,Context是指管理OpenCL對象和資源的上下文環境。為了管理OpenCL程式,下面的一些對象都要和Context關聯起來:

—裝置(Devices):執行Kernel程式對象。

—程式對象(Program objects): kernel程式源代碼

—Kernels:運作在OpenCL裝置上的函數。

—記憶體對象(Memory objects): device處理的資料對象。

—指令隊列(Command queues): 裝置之間的互動機制。

注意:建立一個Context的時候,我們必須把一個或多個裝置和它關聯起來。對于其它的OpenCL資源,它們建立時候,也要和Context關聯起來,一般建立這些資源的OpenCL函數的輸入參數中,都會有context。

OpenCL概述 (Introduction to OpenCL)
OpenCL概述 (Introduction to OpenCL)

這個函數中指定了和context關聯的一個或多個裝置對象,properties參數指定了使用的平台,如果為NULL,廠商選擇的預設值被使用,這個函數也提供了一個回調機制給使用者提供錯誤報告。

現在的代碼如下:

1: #include "stdafx.h"      
2: #include <CL/cl.h>      
3: #include <stdio.h>      
4: #include <stdlib.h>      
5:       
6: #pragma comment (lib,"OpenCL.lib")      
7:       
8: int main(int argc, char* argv[])      
9: {      
10: cl_uint status;      
11: cl_platform_id platform;      
12:       
13: status = clGetPlatformIDs( 1, &platform, NULL );      
14:       
15: cl_device_id device;      
16:       
17: clGetDeviceIDs( platform, CL_DEVICE_TYPE_GPU,      
18: 1,      
19: &device,      
20: NULL);      
21: cl_context context = clCreateContext( NULL,      
22: 1,      
23: &device,      
25:       
26: return 0;      
27: }      

接下來,我們要看下指令隊列。在OpenCL中,指令隊列就是主機的請求,在裝置上執行的一種機制。

  • 在Kernel執行前,我們一般要進行一些記憶體拷貝的工作,比如把主機記憶體中的資料傳輸到裝置記憶體中。

另外要注意的幾點就是:對于不同的裝置,它們都有自己的獨立的指令隊列;指令隊列中的指令(kernel函數)可能是同步的,也可能是異步的,它們的執行順序可以是有序的,也可以是亂序的。

OpenCL概述 (Introduction to OpenCL)

指令隊列在device和context之間建立了一個連接配接。

指令隊列properties指定以下内容:

  • 是否亂序執行(在AMD GPU中,好像現在還不支援亂序執行)
  • 是否啟動profiling。Profiling通過事件機制來得到kernel執行時間等有用的資訊,但它本身也會有一些開銷。

如下圖所示,指令隊列把裝置和context聯系起來,盡管它們之間不是實體連接配接。

OpenCL概述 (Introduction to OpenCL)

添加指令隊列後的代碼如下:

1: #include "stdafx.h"      
2: #include <CL/cl.h>      
3: #include <stdio.h>      
4: #include <stdlib.h>      
5:       
6: #pragma comment (lib,"OpenCL.lib")      
7:       
8: int main(int argc, char* argv[])      
9: {      
10: cl_uint status;      
11: cl_platform_id platform;      
12:       
13: status = clGetPlatformIDs( 1, &platform, NULL );      
14:       
15: cl_device_id device;      
16:       
17: clGetDeviceIDs( platform, CL_DEVICE_TYPE_GPU,      
18: 1,      
19: &device,      
20: NULL);      
21: cl_context context = clCreateContext( NULL,      
22: 1,      
23: &device,      
24: NULL, NULL, NULL);      
25:       
26: cl_command_queue queue = clCreateCommandQueue( context,      
27: device,      
28: CL_QUEUE_PROFILING_ENABLE, NULL );      
29:       
30: return 0;      
31: }      

繼續閱讀