2015年2月7日 星期六

在 Linux Mint 17.1 上面安裝 Altera OpenCL 14.1 (二)

這是之一

其實內容跟標題已經不太一樣了
本篇紀錄的是如何從無到有
使用 Altera OpenCL 的模擬模式
最少限度的重現了官方網站中 Altera 向量加法的範例
順便實驗性地嘗試 C++11 跟 glog (Google logger) 的一些功能

檔案

main.cpp

連結

Compile 的指令

使用 aocl,我們可以找出所需的 include/link 資料夾
我們的編譯命令如下
除了檔案比較多了一點
大致上跟一般的編譯流程沒有什麼兩樣
*注意路徑
g++ main.cpp \     
   -g -Wno-unused-result -fPIC --std=c++11\
   -I../common/inc \
   -I/opt/altera/14.1/hld/host/include \
   -L/opt/altera/14.1/hld/board/terasic/de5net/linux64/lib \
   -L/opt/altera/14.1/hld/host/linux64/lib \
   -lalteracl -lalterahalmmd -laltera_apb_14_0_mmd \
   -lelf -lrt -lstdc++ -lglog\
   -Wl,--no-as-need -lacl_emulator_kernel_rt -ldl \
   -o vector_add

其實如果之前打 default 的 make 有加參數的話
也看得到這串命令
make VERBOSE=1 DEBUG=1
但是因為加入了 glog,加上修正了一個小問題(見前篇)
所以最後多出了紅色的部份

程式碼說明

產生隨機資料

範例程式中嘗試把兩個浮點數向量加總
算是平行程式運算中的 Hello world
所以我們學他把 a, b 加總到 c
首先產生隨機,介於 [0, 1) 的 a, b

std::default_random_engine re(1243253);
std::uniform_real_distribution<float> rd(0.0f, 1.0f);
auto r = std::bind(rd, re);
std::generate(a, c, r);

這幾行使用了 C++11 產生隨機的函式庫
因為 a, b, c 實際上是一次宣告的 array 的不同部份
所以可以直接從 a 生成到 c 的位置

找出可用裝置 (device)

一個 OpenCL 環境中會有多個 platform
一個 platform 大概是一個廠商提供的
例如 Intel, NVIDIA... 等等都會各自對應一個 platform
而每個 platform 都會有一個名字,可以用他去找出想找的 platform
而每一個 platform 下面都會有一些有運算能力的 device
下面的程式能出第一個 Altera platform 下的可用 device
其中函數的細節就不說明了

std::vector<cl_platform_id> &&platforms = GetPlatforms();
cl_platform_id first_altera_platform = GetFirstAlteraPlatrofm(platforms);
LOG(INFO) << "First Altera Platform ID: " << first_altera_platform;
std::vector<cl_device_id> devices = GetDevices(first_altera_platform);

LOG(INFO) 是 glog 的功能
使用起來跟 std::cout 非常接近
其他地方出現的 CHECK_XXX 或是 LOG_XXX 之類的 macro 也都是 glog 提供的

create context and program

OpenCL 一個 platform 下的多個 device 可以用一個 context 管理
有了 context 之後,就能替那些 device 創造 program
program 就像函式庫一樣
因為電路編譯要很久的關係,所以只支援 binary program

cl_context context = clCreateContext(
    NULL, num_devices, devices.data(),
    NULL, NULL, &result);
CHECK_EQ(result, CL_SUCCESS) << clewErrorString(result);
cl_program program = GetProgram(context, "vector_add.aocx", devices);

這邊也用到了 glog 的 CHECK_EQ 確保 OpenCL 的呼叫正確回傳

搬資料&呼叫 kernel

每一個 OpenCL device 可以用一個 command queue 來送資料、呼叫
多個 device 就有多個 command queue 塞工作
就跟 CUDA memory copy, kernel launch 行為一樣
每一個 kernel launch 都需要一個對應的 kernel object 來存參數(猜測是)
至於記憶體的宣告跟搬移就不解釋了
方便起見,全部使用同步界面,這跟範例不一樣

for (size_t i = 0; i < num_devices; i++) {
   command_queues[i] = clCreateCommandQueue(context, devices[i], 0, &result);
   kernels[i] = clCreateKernel(program, "vector_add", &result);
   // Then malloc on each devices
}

剩下的部份都跟一般的 OpenCL 一樣

for (int i = 0; i < num_devices; i++) {
   result = clSetKernelArg(kernels[i], 0, sizeof(cl_mem), &input_a_buf[i]);
   // And more args
   result = clEnqueueNDRangeKernel(
      command_queues[i],
      kernels[i],
      1, // work dimension
      NULL, // offset
      &n_element_each[i], // grid dimension
      NULL, // block dimension (auto)
      0, NULL, NULL
   );
}

跟一般 GPU programming 不一樣的是
我們讓 OpenCL library 自己決定 local work size,也就是 CUDA 的 blockDim

沒有留言:

張貼留言