GPU là gì? GPU viết tắt của Graphic Processing Unit (còn gọi là visual processing unit) là một con chip được thiết kế chuyên môn hóa trong việc tính toán nhanh và thay thế bộ nhớ nhằm mục đích tăng tốc việc
Trang 1GPU là gì?
GPU viết tắt của Graphic Processing Unit (còn gọi là visual processing unit) là một con chip được thiết kế chuyên môn hóa trong việc tính toán nhanh và thay thế bộ nhớ nhằm mục đích tăng tốc việc xây dựng hình ảnh trong bộ nhớ màn hình để thể hiện lên màn hình GPU có thể được tích hợp trên card màn hình, mainboard, hoặc CPU
CUDA là gì?
CUDA viết tắt của Compute Unified Device Architecture là một kiến trúc tính toán song song được phát triển bởi NVIDIA CUDA là động cơ tính toán trong các GPU mà lập trình viên có thể truy xuất thông qua các ngôn ngữ lập trình phổ biến Không giống như CPU, GPU tập trung tính toán đồng thời nhiều luồng một cách chậm rãi hơn là tính toán nhanh chóng một luồng
Ưu điểm của CUDA trên GPU sử dụng graphic API
Đọc rải rác: code có thể được đọc từ địa chỉ bất kỳ trong bộ nhớ
Chia sẻ bộ nhớ: CUDA sử dụng một bộ nhớ chia sẻ nhanh chung cho các luồng xử lý
Đọc và ghi dữ liệu trên GPU nhanh chóng
Hỗ trợ tính toán số nguyên và các phép toán trên bit
Hạn chế của CUDA
Không hỗ trợ render texture
Bandwidth và độ trễ giữa CPU và GPU dẫn đến hiện tượng thắt nút cổ chai
Chỉ hỗ trợ trên các card đồ họa NVIDIA từ series Geforce8 trở lên Đề xem danh sách các card đồ họa có hổ trợ CUDA có thể vào trang sau đây để xem
(http://www.nvidia.com/object/cuda_gpus.html)
Trang 2CUDA kết hợp với OpenGL
CUDA OpenGL Pipeline
Vấn đề bộ nhớ khi lập trình với CUDA
Bởi vì nhân CUDA chỉ có thể truy xuất vùng nhớ dành riêng cho GPU, nên khi sử dụng, cần phải cấp phát bộ nhớ cho chương trình và trên GPU
Việc cấp phát bộ nhớ cho chương trình khi lập trình với CUDA tương tự như trong một chương trình bình thường Còn việc cấp phát bộ nhớ trên GPU được thực hiện thông qua hàm
cudaMalloc(void **ppData, int numBytes)
Ví dụ:
//Khai báo biến
float *h_dataA, *h_dataB, *h_resultC;
float *d_dataA, *d_dataB, *d_resultC;
//Cấp phát cho bộ nhớ chương trình
h_dataA = (float *)malloc(sizeof(float) * MAX_DATA_SIZE);
h_dataB = (float *)malloc(sizeof(float) * MAX_DATA_SIZE);
h_resultC = (float *)malloc(sizeof(float) * MAX_DATA_SIZE);
//Cấp phát cho GPU
CUDA_SAFE_CALL( cudaMalloc( (void **)&d_dataA, sizeof(float) *
MAX_DATA_SIZE) );
CUDA_SAFE_CALL( cudaMalloc( (void **)&d_dataB, sizeof(float) *
MAX_DATA_SIZE) );
Trang 3CUDA_SAFE_CALL( cudaMalloc( (void **)&d_resultC , sizeof(float) * MAX_DATA_SIZE) );
Copy dữ liệu sang và từ bộ nhớ trên GPU
//copy dữ liệu từ bộ nhớ chương trình chính sang bộ nhớ trên GPU
CUDA_SAFE_CALL( cudaMemcpy(d_dataA, h_dataA, sizeof(float) *
dataAmount, cudaMemcpyHostToDevice) );
//copy dữ liệu từ bộ nhớ trên GPU sang bộ nhớ chương trình chính
CUDA_SAFE_CALL( cudaMemcpy(h_resultC, d_dataA, sizeof(float) *
dataAmount, cudaMemcpyDeviceToHost) );
Gọi thực hiện hàm tính toán chung cho tất cả các thread
multiplyNumbersGPU<<<blockGridRows, threadBlockRows>>>(d_dataA,
d_dataB, d_resultC);
Gọi hàm này để chờ cho đến khi các thread thực hiện xong công việc
CUDA_SAFE_CALL( cudaThreadSynchronize() );
Because CUDA kernels can only access memory dedicated to the GPU, we will need to seperately allocate memory space both on the host machine, and on the GPU
Viết một ứng dụng demo CUDA
Chương trình tạo particle , ban đầu tất cả các hạt sẽ đều nằm ở vị trí (0,0,0) trong không gian 3 chiều, sau 1 giây hoặc sau khi người dùng nhấn phím c thì các hạt này sẽ lan ra theo mặt phẳng xOz, khi lan ra đến 1 mức nào đó thì tất cả các hạt lại quay về vị trí cũ (0,0,0).
Tạo một CUDA WinAPP
Download file CUDA_VS_Wizard_W32.2.0.zip để dùng wizard tạo 1 chương trình CUDA Hello world
Việc sử dụng Wizard sẽ giúp chúng ta cấu hình sẵn cho project để chạy được chương trình, như cấu hình compiler, cấu hình các additional Directory (thư mục chứa file include, file lib)
Khi dùng wizard để tạo, ban đầu sẽ có sẵn 2 file readme.txt và sample.cu Đổi tên file sample này thành firework.cu
Trang 4Tạo thêm 1 file main.cpp để chạy chương trình chính, trong hàm keyprocess hoặc hàm
timercallback sẽ gọi đến hàm ComputeCoor trong file firework.cu
Hàm ComputeCoor sẽ chép dữ liệu từ bộ nhớ chương trình sang bộ nhớ trên GPU để chuẩn bị cho việc tính toán, sau đó gọi hàm kernel Hàm kernel là hàm chạy trên GPU, hàm này chỉ thực hiện việc đơn giản là cộng giá trị x cho sin(i*2*PI/numofThreads)*0.1 và cộng z cho
cos(i*2*PI/numofThreads)*0.1 để các hạt lan đều ra thành vòng tròn Hàm ComputeCoor sau khi thực hiện xong (sau khi hàm CUDA_SAFE_CALL( cudaThreadSynchronize() ); kết thúc) thì sẽ chép dữ liệu từ bộ nhớ trên GPU sang bộ nhớ chương trình để chương trình chính thực hiện việc
vẽ các hạt
Số lượng hạt được define trong file main.cpp
Source code chương trình nằm trong file Demo.rar