Tải bản đầy đủ (.pdf) (35 trang)

Lập trình song song dữ liệu trên GPU OpenCL

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.01 MB, 35 trang )

ĐẠI HỌC QUỐC GIA TP. HỒ CHÍ MINH
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN
KHOA CNTT - BỘ MÔN KHMT

BÁO CÁO MÔN HỌC
LẬP TRÌNH SONG SONG DỮ LIỆU TRÊN GPU

OpenCL

Thông tin về nhóm thực hiện:

Cào Cào
Họ và tên

Thành viên

OpenCL
 

MSSV

TL. đóng góp

Nguyễn Hùng Sơn

1/3

Nguyễn Trung Tín

1/3


Trần Quốc Tự

1/3

1
 


Mục lục
I. Giới thiệu về OpenCL...................................................................................................... 4
 
1. Tổng quan.................................................................................................................... 4
 
2. Lịch sử hình thành....................................................................................................... 5
 
3. Đặc điểm ..................................................................................................................... 5
 
3.1. OpenCL, một chuẩn lập trình mở......................................................................... 5
 
3.2. Tận dụng tối đa các tài nguyên có thể có của máy tính ....................................... 5
 
4. Ngôn ngữ..................................................................................................................... 6
 
5. Hạ tầng (Platform)....................................................................................................... 6
 
6. Tầm vực của OpenCL ................................................................................................. 7
 
II. Kiến trúc OpenCL trên nền hệ điều hành Mac OS X ..................................................... 8
 
1. Sơ lược về Mac OS X ................................................................................................. 8

 
2. Framework & Runtime................................................................................................ 8
 
3. Compiler...................................................................................................................... 9
 
4. Operation Model ......................................................................................................... 9
 
4.1. Platform Model .................................................................................................... 9
 
4.2. Execution Model ................................................................................................ 10
 
4.3. Memory Model................................................................................................... 11
 
4.4. Programming Model .......................................................................................... 12
 
III. Workflow phát triển chương trình OpenCL................................................................ 13
 
1. Các bước viết một chương trình OpenCL ................................................................. 13
 
1.1. Xác định những nhiệm vụ nào có thể thực hiện song song................................ 13
 
1.2. Viết các kernel và các hàm bổ trợ ...................................................................... 13
 
1.3. Setup context ...................................................................................................... 13
 
1.4. Viết mã lệnh để biên dịch và build chương trình OpenCL ................................ 13
 
1.5. Khởi tạo các đối tượng memory object .............................................................. 14
 
1.6. Lập hàng đợi lệnh có thứ tự (enqueue command) để điều khiển việc thực thi liên

tục và đồng bộ các kernel, đọc và ghi dữ liệu, và thao tác trên các memory object . 14
 
OpenCL
 

2
 


1.7. Đọc giá trị trả về................................................................................................. 14
 
2. Viết Kernel ................................................................................................................ 14
 
3. Truy vấn thiết bị ........................................................................................................ 16
 
4. Khởi tạo OpenCL Context ........................................................................................ 16
 
5. Khởi tạo Program Object .......................................................................................... 17
 
6. Build Program Executable ........................................................................................ 19
 
7. Khởi tạo Kernel Object ............................................................................................. 22
 
8. Khởi tạo Memory Object .......................................................................................... 22
 
9. Thực thi các kernel .................................................................................................... 22
 
9.1. Xác định số chiều của dữ liệu: ........................................................................... 23
 
9.2. Xác định số lượng work-item............................................................................. 23

 
9.3. Chọn kích thước cho work-group ...................................................................... 23
 
9.4. Enqueue Kernel Execution................................................................................. 25
 
10. Nhận kết quả trả về.................................................................................................. 28
 
10.1. Chờ cho đến khi các kernel hoàn tất thực thi ................................................... 28
 
10.2. Đọc kết quả....................................................................................................... 29
 
11. Giải phóng bộ nhớ................................................................................................... 29
 
12. Debug chương trình OpenCL.................................................................................. 29
 
IV. Performance ................................................................................................................ 31
 
1. GPGPU Performance ................................................................................................ 31
 
1.1. Số thực................................................................................................................ 31
 
1.2. Bandwidth .......................................................................................................... 32
 
1.3. Nhận xét ............................................................................................................. 32
 
2. CPU Performance...................................................................................................... 33
 
2.1. Số học................................................................................................................. 33
 
2.2. Bandwidth .......................................................................................................... 34

 
2.3. Nhận xét ............................................................................................................. 34
 
IV. Tài liệu tham khảo....................................................................................................... 35
 

OpenCL
 

3
 


I. Giới thiệu về OpenCL

1. Tổng quan
OpenCL (Open Computing Language) là chuẩn mở (công bố vào 12/2008) hỗ trợ lập
trình song song trên các thiết bị (bao gồm cả GPU), được đề xuất bởi Apple và nhượng
lại quyền phát triển cho Khronos Group. Dù chỉ mới ra đời nhưng OpenCL lại nhận được
rất nhiều sự hỗ trợ từ các nhà sản xuất phần cứng.

Danh sách các nhà sản xuất phần cứng ủng hộ OpenCL

OpenCL
 

4
 



2. Lịch sử hình thành
OpenCL ban đầu được đề xuất và phát triển bởi Apple sau này được phát triển thêm với
sự hợp tác của AMD, IBM, Intel,và nVidia. Sau đó Apple nhượng lại quyền phát triển
cho Khronos Group (tổ chức đang nắm giữ các chuẩn mở khác như OpenGL,
OpenAL…).
• 16/06/2008: Nhóm Khronos Compute Working được thành lập với các đại diện
đến từ các công ty CPU, GPU, thiết bị nhúng và các vi xử lý khác.
• 18/11/2008: đưa ra đặc tả kĩ thuật OpenCL 1.0.
• 08/12/2008: bản OpenCL 1.0 chính thức được phát hành.
• 20/04/2009: nVidia ra mắt OpenCL driver và SDK để phát triển trong chương
trình OpenCL Early Access.
• 05/08/2009: AMD giới thiệu công cụ phát triển đầu tiên cho nền tảng OpenCL
như là một phần của chương trình ATI Stream SDK v2.0 Beta.
3. Đặc điểm
3.1.
 OpenCL,
 một
 chuẩn
 lập
 trình
 mở
 
Đây là điều trước tiên phải nói đến, OpenCL là một chuẩn lập trình mở hỗ trợ
miễn phí cho tất cả những hãng phần cứng nào có nhu cầu tìm hiểu và ứng dụng.
Do đó, mọi hỗ trợ kĩ thuật của OpenCL đều đăng tải miễn phí trên website của
Khronos Group.
OpenCL được phát triển theo xu hướng cross-platform, độc lập với hạ tầng phần
cứng của các thiết bị tính toán cũng như giữa các hệ điều hành nên đã nhận được
sự ủng hộ của rất nhiều các nhà sản xuất.


 
3.2.
 Tận
 dụng
 tối
 đa
 các
 tài
 nguyên
 có
 thể
 có
 của
 máy
 tính
 
OpenCL được phát triển theo xu hướng tận dụng được tất cả các thiết bị tính toán
có thể thực thi song song. Điều đó có nghĩa là nếu ta có một CPU đa nhân với
OpenCL thì có thể lập trình để thực thi các tác vụ song song trên CPU đó. Hơn
nữa, OpenCL hỗ trợ lập trình song song tác vụ (task-parallel programming) và cả
lập trình song song dữ liệu (data-parallel programming).
OpenCL
 

5
 


4. Ngôn ngữ
OpenCL sử dụng ngôn ngữ OpenCL-C dựa trên chuẩn C99 cho lập trình kernel và IEEE754 (chuẩn dấu chấm động cho số học) vì thế nên cú pháp hoàn toàn giống với C/C++.

5. Hạ tầng (Platform)
Do được phát triển theo hướng độc lập với hạ tầng phần cứng nên OpenCL tự xây dựng
một lớp phần cứng trừu tượng cho bản thân mình và độc lập hoàn toàn với hạ tầng phần
cứng của thiết bị.

Mô hình hạ tầng:
• Một host bao gồm nhiều Compute Device (Core – CPU / SM – GPU / …).
• Một Compute Device (CPU / GPU / …) bao gồm nhiều Compute Unit.
• Một Compute Unit có thể được phân chia thành một hoặc nhiều Processing
Element (vd: 1 SP trong SM GPU).

OpenCL
 

6
 


6. Tầm vực của OpenCL
Phần cứng: OpenCL hỗ trợ lập trình song song trên CPU, GPU hay thậm chí cả các thiết
bị nhúng và di động.
Danh sách các GPU được OpenCL chính thức hỗ trợ bao gồm:
Nvidia:












GeForce 9400M
GeForce 9600M GT
GeForce 8600M GT
GeForce GT 120
GeForce GT 130
GeForce GTX 285
GeForce 8800 GT
GeForce 8800 GS
Quadro FX 4800
Quadro FX 5600

Thực tế các chip dòng G80, G90 của nVidia hoặc cao hơn, thậm chí là các GPU
GTX200 series vẫn có thể hỗ trợ OpenCL nếu các GPU này có công nghệ CUDA
của nVidia.
ATI:
• Radeon 4850
• Radeon 4870

Đối với CPU, OpenCL hỗ trợ các chip thuộc cả hai hãng lớn hiện nay là Intel và AMD.
Do OpenCL vẫn còn khá mới cho nên chưa nhiều các hãng thiết kế phần cứng hỗ trợ
chuẩn này. Nhưng tương lai rất hứa hẹn khi OpenCL là chuẩn được nhiều “ông lớn”
trong ngành công nghiệp phần cứng hỗ trợ nhất. Và dù mới chỉ ra đời không lâu nhưng
các đại gia này đã chính thức hỗ trợ OpenCL trong loạt sản phẩm hiệu năng cao của
mình.
Về nền tảng hệ điều hành: OpenCL có thể chạy được trên cả Mac OS X, Windows và
Linux.

OpenCL
 

7
 


II. Kiến trúc OpenCL trên nền hệ điều hành Mac OS X
1. Sơ lược về Mac OS X
Hệ máy Macintosh của Apple nổi tiếng vì thiết kế đẹp và tinh tế, hệ thống phần cứng cao
cấp, sự tối đồng bộ tối ưu giữa phần cứng với phần mềm. Hệ điều hành Mac OS X góp
phần không nhỏ vào thành công đó nhờ vào tính đơn giản ổn định, hệ màu chuẩn, ít bị
tổn hại cùng với các công nghệ tiên tiến (Grand Central Dispatch, 64-bit,…) trong đó có
OpenCL, và có thể nói Mac OS X Snow Leopard 10.6 là hệ điều hành đầu tiên trực tiếp
đưa OpenCL vào “Core” của mình.

2. Framework & Runtime
OpenCL framework trong Mac OS X cung cấp
đầy đủ các headers cần thiết để dễ dàng thực
hiện biên dịch mã nguồn OpenCL cũng như
giao tiếp với OpenCL Runtime. Chỉ đơn giản
với
một
dòng
lệnh
#include
<opencl.h>, ta có thể sử dụng các API của
OpenCL mà không cần phải khai báo gì thêm
trong chương trình.
Theo hình vẽ minh họa bên, mô hình làm việc

của OpenCL cũng tương tự như CAL của ATI
hay CUDA của nVidia.
Một điều dễ thấy là OpenCL runtime làm việc
trực tiếp với driver của phần cứng, vì thế một
số ý kiến cho rằng OpenCL chỉ là một chuẩn về
ngôn ngữ lập trình song song nên không thể chạy nhanh hơn CAL/CUDA là hoàn toàn
sai lệch. Vì làm việc trực tiếp với driver của phần cứng nên OpenCL runtime có thể coi
như tương đương với CAL/CUDA và việc hiệu năng có thể cao hơn CAL/CUDA là bình
thường.
Một số hãng phần cứng như ATI đưa OpenCL vào bộ công cụ lập trình song song của
mình, nhưng ở mức nào đó chỉ về mặt cú pháp hình thức chứ không sử dụng OpenCL
OpenCL
 

8
 


runtime và tầng giao tiếp với driver vẫn giữ CAL mà ko sử dụng OpenCL runtime. Đó là
lý do STREAM chạy chậm hơn OpenCL vì một số nguyên nhân nào đó.

3. Compiler
OpenCL compiler trong Mac OS X sử dụng LLVM. Khi biên dịch một chương trình
OpenCL, trước hết các chỉ thị trong chương trình đó sẽ được dịch sang một dạng biểu
diễn trung gian (Intermediate Representation – IR). Sau đó, LLVM sẽ dịch và tối ưu hóa
IR sang mã phù hợp với thiết bị mà chương trình sẽ thực thi. Điểm nhấn nằm ở chỗ:
chương trình chỉ cần viết một lần nhưng vẫn có thể chạy trên nhiều kiến trúc phần cứng
khác nhau. Và ứng với mỗi hệ thống phần cứng, lần đầu tiên biên dịch của chương trình
sẽ được “cache” lại để tránh việc biên dịch trùng lặp không cần thiết.


4. Operation Model
Sự vận hành của OpenCL được mô tả bởi cụm các model có mối liên hệ với nhau, bao
gồm Platform Model (mô hình hạ tầng), Execution Model (mô hình thực thi), Memory
Model (mô hình bộ nhớ), và Programming Model (mô hình lập trình).
4.1.
 Platform
 Model
 
Như đã nói ở trên, OpenCL
device sẽ làm việc với host
device – là device điều khiển
chương trình hoạt động. Khi
chương trình thực thi, host sẽ
tạo ra một môi trường trừu
tượng hay còn gọi là môi
trường ảo (context) và cung
cấp các thiết bị tính toán (compute device) cùng với khoảng vùng nhớ nhất định
mà chương trình sẽ sử dụng. Bên cạnh đó một “hàng đợi lệnh có thứ tự”
(command queue) cũng sẽ được tạo ra để chương trình có thể điều phối các lệnh
trong kernel và thực hiện các thao tác truy xuất tới bộ nhớ.

OpenCL
 

9
 


Lưu ý: tốc độ truy xuất giữa các device sẽ chậm hơn rất nhiều so với tốc độ giao
tiếp nội bộ của các thành phần trong device đó và bản thân host device (vd: CPU)

cũng có thể là một OpenCL device.

 
4.2.
 Execution
 Model
 

 
Kernel: là một tập các lệnh được viết ra để thực thi trên một thiết bị hỗ trợ
OpenCL (OpenCL device). Tập các kernel và các hàm bổ trợ (helper function)
được gọi là Program.

Khi biên dịch chương trình, các kernel được biên dịch thành kernel object và
tương tự với program ta có program object.
Việc thực thi một chương trình OpenCL bao gồm nhiều thực thi một cách đồng
thời các instance của một kernel trên một hoặc nhiều OpenCL device trên
command queue được điều phối bởi ứng dụng host (host application). Mỗi
instance của một kernel gọi là một work-item. Mỗi work-item thực thi cùng một
đoạn mã lệnh nhưng trên các vùng dữ liệu khác nhau và mỗi một work-item chạy
trên một single-core của multiprocessor. Khi ấn định thực thi chương trình trên
một device nào đó, ta xác định số lượng work-item cần thiết để hoàn tất việc xử lý
dữ liệu mà ta sẽ gọi là index space (không gian chỉ mục). OpenCL hỗ trợ index
space tối đa là 3 chiều.
Các work-item có thể nhóm lại thành những work-group. OpenCL cũng có cơ chế
đồng bộ hóa tính toán giữa các work-item trong một work-group nhưng không hỗ
trợ tương tự giữa các work-group với nhau.
Mỗi work-item trong chương trình có một định danh duy nhất - global ID để hỗ
trợ truy xuất trong index space. Ví dụ, một work-item trong không gian chỉ mục 2
chiều có giá trị X là 23 và Y là 6 sẽ mang global ID (23, 6). Tương tự, mỗi workOpenCL

 

10
 


group cũng sẽ có một định danh duy nhất work-group ID, để xác định vị trí của
work-group trong index space.
OpenCL cũng cho phép định vị trí của một work-item trong work-group thông qua
local ID.
Ta có thể hình dung sự tương tự giữa OpenCL với CUDA, mỗi work-item tương
đương một thread, và mỗi work-group tương đương với một thread block.
Memory object: là một handle tới vùng nhớ global (xem 4.3) được sử dụng để
lưu dữ liệu từ ứng dụng vào vùng nhớ của thiết bị để thao tác. Có 2 loại chính:
buffer object và image object, với buffer object có thể chứa bất cứ loại dữ liệu
nào và image object được sử dụng đặc thù cho các dữ liệu ảnh. Host application
dùng command-queue để thực hiện thao tác đọc và ghi lên memory object.
4.3.
 Memory
 Model
 
OpenCL phân chia tầm vực bộ nhớ vào bốn loại sau:
- Global memory: có thể đọc và ghi
bởi tất cả các work-item trong các
work-group. Đây chính là vùng nhớ
được cấp phát đã mô tả trong Platform
Model.
- Constant memory: là một vùng trên
global memory chỉ hỗ trợ việc đọc bởi
các work-item và giữ giá trị không đổi

suốt quá trình thực thi của một kernel.
Giá trị trên constant memory được
cung cấp bởi host application.
- Local memory: có thể được đọc ghi bởi một work-group đặc thù và giữ giá trị
chia sẻ bởi các work-item trong work-group đó.
- Private memory: chỉ có thể truy xuất bởi một work-item duy nhất.
Việc sử dụng bộ nhớ hiệu quả và tốc độ phụ thuộc rất nhiều vào cách dùng bốn
loại bộ nhớ trên. Trong đó private memory và local memory cho tốc độ cao nhất,
vùng nhớ cho tốc độ truy xuất chậm chính là global memory.
OpenCL
 

11
 


Các khái niệm về tầm vực của vùng nhớ trong OpenCL cũng tương tự với CUDA:

4.4.
 Programming
 Model
 
OpenCL hỗ trợ hai mô hình lập trình song song chính: song song dữ liệu (dataparallel) và song song tác vụ (task-parallel).
Các tiến trình song song dữ liệu thực thi nhiều instance có cùng kernel một cách
đồng thời, mỗi instance xử lý một tập dữ liệu riêng biệt. Mỗi tập dữ liệu liên kết
với một điểm trong không gian chỉ mục một, hai hay ba chiều.
Song song tác vụ lại tương tự như những tiến trình thực thi đa luồng có tính chất
độc lập nhau, mỗi process thực hiện những nhiệm vụ khác nhau. Trong OpenCL,
lập trình song song tác vụ bao gồm việc lập hàng đợi nhiều kernel, và để OpenCL
thực hiện chúng một cách song song sử dụng các thiết bị tính toán có thể có.


OpenCL
 

12
 


III. Workflow phát triển chương trình OpenCL
1. Các bước viết một chương trình OpenCL
Tiến trình phát triển một chương trình OpenCL bao gồm các bước dưới đây.
1.1.
 Xác
 định
 những
 nhiệm
 vụ
 nào
 có
 thể
 thực
 hiện
 song
 song
 
Để chương trình đạt được hiệu quả cao nhất, trước tiên ta phải xác định những gì
có thể thực hiện đồng thời từ đó dễ dàng suy ra cách tổ chức bộ nhớ cũng như chi
phí phù hợp cho chương trình.

 

1.2.
 Viết
 các
 kernel
 và
 các
 hàm
 bổ
 trợ
 
Để thực hiện tính toán song song trên OpenCL device, bắt buộc phải viết các
kernel. Các kernel được đóng gói và biên dịch khi chương trình thực thi.

 
1.3.
 Setup
 context
 
Sử dụng các hàm có trong OpenCL framework để tìm và quyết định thiết bị nào sẽ
dùng trong chương trình. Sau đó khởi tạo môi trường ảo bao gồm memory object
và command queue.

 
1.4.
 Viết
 mã
 lệnh
 để
 biên
 dịch

 và
 build
 chương
 trình
 OpenCL
 
Sau khi đã xác định được OpenCL device và setup context, chúng ta sẽ viết mã lệnh
cho host application để biên dịch mã nguồn chương trình và sử dụng các kernel object
từ mã nguồn đã biên dịch. Các lệnh sau được thực hiện liên tục theo thứ tự:
a. Hàm clCreateProgramWithSource để khởi tạo chương trình từ mã nguồn
OpenCL-C cho trước, hoặc nếu có sẵn đoạn mã đã được biên dịch trước đó (được
“cache” từ lần thực thi trước, ví dụ chương trình ngoài như các thư viện), gọi hàm
clCreateProgramWithBinary. Các hàm này sẽ liên kết các kernels và các
hàm bổ trợ vào một chương trình và trả về một program object.
OpenCL
 

13
 


b. Gọi hàm clBuildProgram để biên dịch program object phù hợp với các thiết
bị đặc thù đang có của hệ thống.
c. Gọi
clCreateKernel
cho
mỗi
kernel,
hoặc
gọi

clCreateKernelsInProgram để tạo các kernel object trong một chương
trình OpenCL, hay nói khác đi, ta extract các đối tượng kernel đã được biên dịch
từ một program object cho trước.

 
1.5.
 Khởi
 tạo
 các
 đối
 tượng
 memory
 object
 
 
Để giữ các dữ liệu nhập xuất và trả về giá trị cho các đối số đầu vào (input object),
memory object tham gia vào nhiệm vụ thao tác vùng nhớ giữa host device và
OpenCL device.

 
1.6.
 Lập
 hàng
 đợi
 lệnh
 có
 thứ
 tự
 (enqueue
 command)

 để
 điều
 khiển
 việc
 thực
 thi
 liên
 
tục
 và
 đồng
 bộ
 các
 kernel,
 đọc
 và
 ghi
 dữ
 liệu,
 và
 thao
 tác
 trên
 các
 memory
 object
 
Để thực thi một kernel, ta phải tuân theo các bước sau:
a. Gọi hàm clSetKernelArg để truyền các tham số đầu vào (parameter value)
vào kernel.

b. Xác định kích thước work-group và lập index space để thực thi kernel.
c. Đưa lệnh thực thi kernel vào command queue.

 
1.7.
 Đọc
 giá
 trị
 trả
 về
 
Enqueue command để đọc giá trị xuất từ work-item và đưa nó vào host memory.

2. Viết Kernel
Kernel được viết bằng ngôn ngữ OpenCL-C có cú pháp giống với C với một số điểm
riêng biệt. Một kernel có dạng như sau:

OpenCL
 

14
 


Lưu ý:
1. Một kernel luôn được khai báo với tiết đầu tố __kernel.
2. Khi thực thi một kernel, ta dùng hàm clSetKernelArg để truyền giá trị vào
các tham số được định nghĩa ở trên.
3. Các hàm get_global_id và get_local_size để lấy thông tin về workitem khi thực thi kernel.
4. mul24 là hàm toán học có sẵn trong OpenCL-C, và có rất nhiều hàm có khả năng

tính toán hiệu suất cao được hỗ trợ sẵn cho cả dữ liệu có hướng lẫn vector.
5. Kernel có thể được gọi từ một kernel khác trong cùng một chương trình OpenCL.

OpenCL
 

15
 


3. Truy vấn thiết bị
Mỗi chương trình OpenCL đòi hỏi phải có một context, bao gồm danh sách các OpenCL
device tồn tại trên hệ thống. Sử dụng hàm clGetDeviceIDs để truy vấn danh sách
thiết bị trên máy hỗ trợ OpenCL. Ta có thể giới hạn việc truy vấn dựa vào đặc thù của
loại thiết bị hoặc kết hợp các thiết bị (vd: chỉ dùng GPU, CPU hay kết hợp cả 2), bên
cạnh đó ta cũng có thể giới hạn số lượng thiết bị muốn sử dụng.
Ví dụ: giả sử chúng ta muốn thực thi code trên GPU và không quan tâm có bao nhiêu
GPU sử dụng được vì ta chỉ cần một. Ta gán CL_DEVICE_TYPE_GPU vào tham số
device_type trong hàm clGetDeviceIDs và gán num_entires = 1, OpenCL sẽ
trả về ID của GPU đầu tiên mà nó tìm thấy.

4. Khởi tạo OpenCL Context
Một khi đã xác định được sẽ sử dụng OpenCL device nào để tính toán và có ít nhất một
thiết bị sử dụng được, chúng ta bắt tay vào khởi tạo OpenCL context nhằm phục vụ cho
việc nhónm các thiết bị lại với nhau để có thể chia sẻ vùng nhớ giữa các compute device,
hoặc chúng ta cũng có thể khởi tạo context từ một OpenGL context đã tồn tại trước đó
OpenCL
 

16

 


nếu có nhu cầu kết hợp OpenGL và OpenCL với nhau. Việc chia sẻ bộ nhớ giữa 2
context hoàn toàn có thể thực hiện được.
Để khởi tạo một context, trước tiên ta phải xác định thiết bị nào sẽ dùng (kết quả trả về từ
hàm clGetDeviceIDs), và truyền nó vào hàm clCreateContext.

5. Khởi tạo Program Object
Một chương trình OpenCL bao gồm một tập các kernel, các hàm bổ trợ có thể gọi từ
kernel (các kernel luôn phải bắt đầu bằng từ khóa __kernel). Tuy nhiên, những hàm
bổ trợ này có thể không thực thi đúng vai trò như một entry point từ OpenCL API. Có
nghĩa là, ta chỉ có thể enqueue các kernel đã thông báo như trên. Một program object
đóng gói chương trình nguồn OpenCL, đi kèm với phiên bản thực thi được build lần
trước của chương trình, cũng như build options, build log, và danh sách các thiết bị mà
chương trình đã biên dịch để dùng trước đó.
Ta có thể khởi tạo một program object trực tiếp từ mã nguồn của chương trình OpenCL
và biên dịch nó trực tiếp vào thời điểm thực thi ứng dụng (application runtime). Thêm
vào đó, ta cũng có thể build program object sử dụng mã nhị phân của lần build thành
công trước đó để tránh phải build khi thực thi ứng dụng.

OpenCL
 

17
 


Lưu ý:
1. Mã nguồn của kernel được đưa vào ứng dụng nhưng một con trỏ kiểu char, ta có

thể khai báo trực tiếp hoặc lưu vào file, từ đó đọc chuỗi ra con trỏ này và sử dụng
trong hàm clCreateProgramWithSource.
2. Nếu không kết thúc chuỗi bởi NULL, ta phải quy định số lượng ký tự tối đa của
một chuỗi cho mỗi mã nguồn kernel.
Hàm clCreateProgramWithSource tạo ra một program object chứa mã nguồn,
nhưng nó vẫn không có khả năng thực thi đến khi nào được biên dịch và liên kết.
Một khi đã tạo bản nhị phân của chương trình, ta có thể sử dụng hàm
clGetProgramInfo để chứa phiên bản nhị phân này. Nếu ta cache lại, lần chạy tới
của chương trình ta có thể sử dụng phiên bản nhị phân thay cho mã nguồn để tạo program
OpenCL
 

18
 


object. Thao tác này giảm đáng kể thời gian khởi tạo và thực thi chương trình sau lần đầu
tiên ứng dụng chạy trên một thiết bị nhất định.
Việc khởi tạo program object từ mã nhị phân cũng tương tự với từ mã nguồn, ngoại trừ
việc ta phải cung cấp mỗi phiên bản nhị phân khác nhau cho mỗi thiết bị khác nhau mà
kernel sẽ chạy. Ta có hàm clCreateProgramWithBinary:

Lưu ý:
1. Xem mục “Truy vấn thiết bị”.
2. Giá trị trả về từ hàm clGetDeviceIDs.
3. Khi đã có program binary, ta có thể lấy thông tin về program object từ hàm
clGetProgramInfo.
4. Vì mỗi compute device đều có tập lệnh riêng biệt, ta phải cung cấp những mã nhị
phân riêng biệt cho mỗi thiết bị muốn sử dụng. Khi gọi hàm
clCreateProgramWithBinary, OpenCL kiểm tra mỗi mỗi bản nhị phân đó

với từng thiết bị có trên hệ thống mà nó tìm được để đảm bảo bản nhị phân này
phù hợp với thiết bị. Tham số binaryStatus trả về một mảng thông tin chứa
kết quả kiểm tra cho mỗi bản nhị phân.

6. Build Program Executable
Sau
khi
đã
khởi
tạo
thành
công
program
object
sử
dụng
clCreateProgramWithSource hay clCreateProgramWithBinary, ta phải
xây dựng phiên bản thực thi của chương trình (build program executable) từ program
object đó. Việc build một chương trình biên dịch bất cứ mã nguồn nào có trong program
OpenCL
 

19
 


object và liên kết mã máy trả về với một chương trình có thể thực thi được. Hàm
clBuildProgram được sử dụng để thực hiện việc này.

Hàm clBuildProgram tác động chỉnh sửa lên chính program object mà ta đã truyền

để thêm vào phiên bản thực thi của chương trình. Do đó, một số program object chứa bản
thực thi, một số không.
Khi biên dịch mã nguồn chương trình, có thể chúng ta sẽ gặp lỗi. OpenCL framework
cung cấp hàm clGetProgramBuildInfo để hỗ trợ truy vấn trình biên dịch của
OpenCL nhằm lấy thông tin chi tiết về lần build cuối cùng. Ta có thể sử dụng hàm này
kết hợp với clBuildProgram như sau:

OpenCL
 

20
 


Trong ví dụ trên, ứng dụng dùng hằng số CL_PROGRAM_BUILD_LOG nhằm lấy thông
tin chi tiết lỗi. Ta có thể sử dụng clGetProgramBuildInfo để lấy những thông tin
khác như build options mà ta đã sử dụng khi gọi hàm clBuildProgram, hay tình trạng
biên dịch hiện tại.

OpenCL
 

21
 


7. Khởi tạo Kernel Object
Một kernel object chứa những thông tin đặc biệt về kernel function được khai báo trong
chương trình cũng như mã nguồn, tham số sử dụng khi thực thi kernel. Mặt khác, bản
thân kernel là một hàm, nhưng một kernel là một cấu trúc dữ liệu phức tạp bao gồm

kernel function và cả dữ liệu mà kernel đó thao tác. Khi muốn thực thi một kernel, ta sử
dụng kernel object chứa kernel đó đưa vào command queue.
Sử dụng hàm clCreateKernel để khởi tạo một kernel object hoặc gọi hàm
clCreateKernelsInProgram để tạo các kernel object cho tất cả kernel trong
chương trình OpenCL.
Các phần tiếp theo cung cấp cái nhìn tổng quan về cách khởi tạo memory object để chứa
dữ liệu, kết hợp dữ liệu với kernel object và thực thi kernel.

8. Khởi tạo Memory Object
Memory object thực chất là “một vùng bảo quản” từ global memory của thiết bị có thể
được xem như nơi chứa dữ liệu chương trình. Sau khi đã khởi tạo và đăng ký kernel với
OpenCL runtime, chúng ta có thể gửi dữ liệu của ứng dụng tới các kernel đang chạy trên
những thiết bị khác nhau bằng cách đóng gói dữ liệu vào memory object trước tiên, sau
đó liên kết memory object này với kernel đặc biệt nào đó. Như đã mô tả ở trên, có 2 loại
memory object: buffer object là một khối bộ nhớ, trong khi image object lại là một cấu
trúc phức tạp, đặc thù dành để biểu diễn các đối tượng ảnh 2D hay 3D.
Để khởi tạo buffer object, ta dùng hàm clCreateBuffer. Tương tự ta có thể sử dụng
các hàm clCreateImage2D hay clCreateImage3D cho các dữ liệu ảnh phù hợp.
Các hàm này trả về đối tượng có kiểu dữ liệu là cl_mem.

9. Thực thi các kernel
OpenCL luôn thực thi các kernel theo cơ chế song song dữ liệu, có nghĩa là, các instance
của cùng một kernel (hay còn gọi là các work-item) thực thi trên các phần khác nhau của
tập dữ liệu. (Nếu muốn thực thi song song tác vụ, ta phải enqueue nhiều kernel trên các
thiết bị khác nhau) Mỗi work-item chịu trách nhiệm thực thi kernel đúng một lần và thao
tác trên phần dữ liệu được giao. Chúng ta có nhiệm vụ xác định số lượng work-item cần
OpenCL
 

22

 


thiết để xử lý tất cả dữ liệu. Bởi vì tập dữ liệu thường được tổ chức dưới dạng một, hai,
hoặc ba chiều (dữ liệu âm thanh, ảnh hai hay ba chiều, các đối tượng ba chiều).
9.1.
 Xác
 định
 số
 chiều
 của
 dữ
 liệu:
 
Bước đầu tiên khi chuẩn bị thực thi một kernel là xác định số chiều mà ta muốn sử dụng
để biểu diễn dữ liệu. Ví dụ, nếu dữ liệu biểu diễn một ảnh hai chiều có kích thước m x n,
khi đó ta có tập dữ liệu hai chiều với mỗi điểm dữ liệu biểu diễn bởi tọa độ của nó trên
hai trục m và n.
OpenCL chưa hỗ trợ số chiều lớn hơn 3.

 
9.2.
 Xác
 định
 số
 lượng
 work-­item
 
Bước kế tiếp khi muốn thực thi kernel là xác định có bao nhiêu work-item cần
thiết để xử lý hết dữ liệu (global work size), và nó định nghĩa tổng số work-item

cả ba chiều. Với dữ liệu một chiều, global work size bằng với với số lượng data
item. Với dữ liệu hai chiều, global work size là m*n. Tương tự là x*y*z với dữ
liệu 3 chiều có x, y, và z work-item trong mỗi chiều. Thực tế không có giới hạn về
số lượng work-item, và số lượng work-item lớn sẽ tận dụng được khả năng tính
toán của GPU (hơn 1000).

 
9.3.
 Chọn
 kích
 thước
 cho
 work-­group
 
Khi enqueue một kernel để thực thi nó trên một thiết bị, ta có thể chỉ định kích
thước của work-group mà OpenCL sử dụng trong quá trình thực thi. Các workitem trong cùng work-group có thể chia sẻ bộ nhớ và thực thi một cách đồng bộ.
Để tận dụng những đặc điểm này, tuy nhiên, cần phải biết kích thước cực đại của
work-group mà OpenCL device muốn thực thi cho phép. Ta sử dụng hàm
clGetKernelWorkGroupInfo

thuộc
tính
CL_KERNEL_WORK_GROUP_SIZE để lấy thông tin này. Nếu không cần chia sẻ
dữ liệu giữa các work-item trong một work-group, truyền giá trị NULL vào tham
số local_work_size khi enque kernel khi thực.
Lưu ý là cũng cần dùng hàm clGetDeviceInfo với tham số
CL_DEVICE_MAX_WORK_ITEM_SIZE để lấy kích thước cực đại trong mỗi
OpenCL
 


23
 


chiều của work-group, và gọi hàm clGetKernelWorkGroupInfo với tham
số CL_KERNEL_WORK_GROUP_SIZE để lấy kích thước tổng của work-group.
Có 3 điều kiện cần được đáp ứng để kích thước địa phương được đảm bảo:
1. Số lượng work-item đối với từng chiều (local_x, local_y, và local_z) trong
một work-group phải nhỏ hơn giá trị trả về từ hàm
clGetDeviceInfo(CL_DEVICE_MAX_WORK_ITEM_SIZES).
2. Tổng số work-item trong mỗi work-group (local_x*local_y*local_z) phải
nhỏ
hơn
hoặc
bằng
với
giá
trị
trả
về
từ
hàm
clGetKernelWorkGroupInfo(CL_KERNEL_WORK_GROUP_SIZ).
3. Số lượng work-item ứng với từng chiều trong mỗi work-group phải được
chia đều cho tổng số các work-item trong chiều đó (global_n mod
local_n = 0).
Đoạn mã sau minh họa việc sử dụng hàm clGetKernelWorkGroupInfo:

OpenCL
 


24
 



 
9.4.
 Enqueue
 Kernel
 Execution
 
Sau khi đã xác định số chiều cần thiết để biểu diễn dữ liệu, số work-item cho mỗi
chiều, và kích thước work-group phù hợp. ta có thể enqueue kernel để thực thi nó.

OpenCL
 

25
 


×