OpenCL‎ > ‎

在 Xcode 中使用 OpenCL

OpenCL 是 Mac OS X 10.6 "Snow Leopard" 的新功能之一,也是第一個正式支援 OpenCL 的作業系統。這裡就簡單介紹一下,如何在 Snow Leopard 下使用 Xcode 建立一個 OpenCL 的程式。

首先,要特別注意的是,必須安裝針對 Snow Leopard 的最新版 Xcode Version 3.2 (1060)。在 Snow Leopard 的安裝光碟中,有一個 Optional Install 的目錄中,可以找到新版 Xcode 的安裝程式。如果你沒有安裝過 Xcode,那麼,在安裝完後,可以在硬碟根目錄的 Developer/Applications 目錄下找到 Xcode 和 Interface Builder 等程式。如果經常要使用的話,可以把它們拉到 dock 上面。

現在執行 Xcode。它可能會出現一個 "Welcome to Xcode" 的畫面,可以讓你選擇建立新的 project,或是從最近打開過的 project 中選擇一個。這裡我們選擇 "Create a new Xcode project" 來建立一個新的 project。如果你的 Xcode 沒有顯示這個畫面,也可以直接從選單的 File/New Project... 來建立一個新的 project。這時,它會顯示一個 "New Project" 視窗,有很多各種不同的 project 種類可以選擇。在這裡,為了簡單起見,我們只做一個 command line 模式的程式。因此,在 Mac OS X 的 Application 中,選擇 "Command Line Tool" 這個選項。在 Type 中選擇 "C" 語言,然後按下 "Choose..." 按鈕。

現在你要為你的 project 選一個儲存的位置以及一個適當的名字。找一個你喜歡的位置,並輸入 hello_cl (或是任何你喜歡的名字)為 project 的名字,然後按下 "Save"。

現在 Xcode 會幫你建好一個完整的 project,包括一些程式檔,例如 main.c。不過,在開始寫 OpenCL 程式之前,還需要做一些小調整。首先,在選單中選擇 Project/Edit Project Settings... 來修改一些 project 的設定。它會顯示出 Project "hello_cl" Info 的對話框。預設的設定中,只有一個需要修改,也就是我們希望它只編譯出 Intel 架構的執行檔(這是因為 Snow Leopard 並不支援 PowerPC 架構)。選擇 "Build" tab,並在 Configuration 中選擇 "All Configurations",然後在 "Valid Architectures" 上按兩下,它預設選擇的各種 Architecture 中,把所有 ppc 開頭的都移除,也就是只留下 "i386" 和 "x86_64" 這兩個選項。現在可以把這個對話框關閉。

接下來,我們要把 OpenCL Framework 加到 project 中。在左邊的 "Groups & Files" 中,最上面有 "hello_cl",在上面按下右鍵(如果你的滑鼠因為某些神秘的原因沒有右鍵,可以按 control+左鍵),然後選擇 Add->Existing Frameworks...。它會列出一大堆系統中的 framework 讓你選擇。往下拉找到 OpenCL.framework 並選擇它,按下 Add 按鈕,就會把 OpenCL.framework 加到 project 中。

在 Source 中選 main.c,會顯示出 Xcode 預先產生的簡單 "Hello, world!" 程式碼。把它的內容改成如下:


#include <stdio.h>
#include <stdlib.h>
#include <OpenCL/opencl.h>

int main (int argc, const char * argv[])
{
    cl_context context;
    cl_device_id *devices;
    char *devname;
    size_t cb;

    // create a GPU context
    context = clCreateContextFromType(NULL, CL_DEVICE_TYPE_GPU, NULL, NULL, NULL);
    if(context == 0) {
        printf("Can't create GPU context\n");
        return 0;
    }

    // get a list of devices
    clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &cb);
    devices = (cl_device_id*) malloc(cb);
    clGetContextInfo(context, CL_CONTEXT_DEVICES, cb, devices, 0);

    // show the name of the first device
    clGetDeviceInfo(devices[0], CL_DEVICE_NAME, 0, NULL, &cb);
    devname = (char*) malloc(cb);
    clGetDeviceInfo(devices[0], CL_DEVICE_NAME, cb, devname, 0);
    printf("Device: %s\n", devname);

    // release everything
    free(devname);
    free(devices);
    clReleaseContext(context);

    return 0;
}

按下 "Build and Run" 圖示,Xcode 就會編譯並執行程式。一切順利的話,它應該會執行,並在左下角顯示出 "Debugging of "hello_cl" ended normally." 的訊息。要看到執行結果,可以選擇選單的 Run/Console 顯示 Debugger Console 的結果。正常的話,應該可以在 "Running..." 後面看到程式執行所印出的 "Device: XXX" 字串。

如果程式印出的是 "Can't create GPU context" 字串,那麼可能表示你的系統並沒有支援 OpenCL 的 GPU。可以把程式中,呼叫 clCreateContextFromType 函式的參數中,CL_DEVICE_TYPE_GPU 改成 CL_DEVICE_TYPE_CPU,表示建立 CPU device,或是改成 CL_DEVICE_TYPE_DEFAULT 表示選擇預設的 device。

接下來簡單介紹一下程式的內容。

首先,所有的 OpenCL 函式都是在 OpenCL/opencl.h 中宣告的。所以,所有的 OpenCL 程式都要 #include <OpenCL/opencl.h>

接下來,是建立一個 OpenCL 的 context。OpenCL 的 context 是用來管理包括 command queue、memory object、program 等等各種物件。使用 clCreateContextFromType 函式,可以很簡便的指定一個 device 的類型,就能建立一個 context。為了簡單起見,這裡就不再說明其它的參數是什麼意思了,有興趣的人可以自行參考 OpenCL Specification,或是相關的文件。這裡我們只指定第二個參數,也就是 device 的類型。它可以是下列各種的其中一種:

CL_DEVICE_TYPE_CPU電腦上的主處理器,可能是單核心或多核心的處理器。
CL_DEVICE_TYPE_GPU具有 3D 加速能力的顯示晶片,支援 OpenGL 或 DirectX。 
CL_DEVICE_TYPE_ACCELERATOR某種特定的 OpenCL 加速器,例如 IBM CELL Blade。
CL_DEVICE_TYPE_DEFAULT系統中預設的 OpenCL 裝置。 
CL_DEVICE_TYPE_ALL所有系統中的 OpenCL 裝置。 

clCreateContextFromType 會傳回建好的 OpenCL context。如果 clCreateContextFromType 失敗,則會傳回 0。

一個 OpenCL context 裡面可能包括多個 device(例如一部電腦中可能有多個顯示晶片),所以我們要先取得它的 device 的列表。這可以透過呼叫 clGetContextInfo 函式來取得。不過,因為我們一開始並不知道它有幾個 device,所以先呼叫第一次,並在最後一個參數傳入一個指標,以取得需要的記憶體大小。也就是:

    clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &cb);

配置好需要的記憶體之後,再呼叫第二次,才真正取得所要的資料內容,即:

    clGetContextInfo(context, CL_CONTEXT_DEVICES, cb, devices, 0);

在 OpenCL 中,很多取得資料的函式,都會使用到類似的方法。事實上,接下來取得 device 名稱的地方,就馬上又使用到了。

要取得 device 名稱,我們需要前面取得的 device 列表,把第一個 device(即 devices[0])傳入 clGetDeviceInfo 函式。clGetDeviceInfo 可以取得各種不同的 device 資訊,這裡我們要的是 CL_DEVICE_NAME,即 device 的名稱。和前面一樣,要先呼叫一次取得需要的記憶體大小,配置好記憶體後,再呼叫第二次以取得資料內容。

最後,把配置的記憶體釋放,並呼叫 clReleaseContext 函式,把之前建立的 OpenCL context 釋放,程式就結束了。

Comments