NCA-AIIO 2: CUDA và lập trình song song trên GPU
Cập nhật: 2026-07-09
Mục lục
- 1. CUDA là gì?
- 2. Kiến trúc CUDA: thread, block, warp, SM, grid
- 3. Các tầng bộ nhớ trong CUDA
- 4. Quản lý nhiều phiên bản CUDA
- 5. Thực hành: viết chương trình CUDA đầu tiên
- 6. ResNet-50 — model tham chiếu hay gặp trong đề thi
- Nguồn tham khảo
1. CUDA là gì?
CUDA là viết tắt của Compute Unified Device Architecture — một nền tảng tính toán song song và API do NVIDIA phát triển, cho phép lập trình viên tận dụng các GPU hỗ trợ CUDA để thực hiện tính toán tổng quát trên GPU (general-purpose computing on GPUs, hay GPGPU). Hầu hết card đồ họa NVIDIA hiện nay đều hỗ trợ CUDA. Một số GPU có thể truy cập qua cloud để dùng CUDA gồm Tesla V, M60, T4, A100,...
Toàn bộ các framework deep learning lớn đều tích hợp với NVIDIA Deep Learning SDK — một tập hợp thư viện của NVIDIA cho deep learning. Một trong các thư viện đó là cuDNN (CUDA Deep Neural Network library), cung cấp các cài đặt được tối ưu cao cho các phép toán tiêu chuẩn như convolution (tiến/lùi), pooling, normalization, các lớp activation. Đây chính là lý do NVIDIA thành công trong mảng AI: CUDA đi kèm những thư viện như thế này giúp việc bắt đầu làm việc với phần cứng của họ, đặc biệt là tính toán song song và tính toán tổng quát, trở nên rất dễ dàng. Ai muốn tìm hiểu sâu hơn về ý tưởng đằng sau CUDA có thể đọc white paper năm 2008 của Lindholm.
Cách cài đặt CUDA là thông qua CUDA Toolkit — tải về sẽ có driver cần thiết và trình biên dịch (compiler) tên là NVCC. CUDA thường được viết bằng C++, nhưng có các wrapper cho ngôn ngữ cấp cao hơn như Python (và .NET). Có một thư viện Python cho phép làm việc trực tiếp với CUDA mang phong cách tương tự NumPy/SciPy nhưng chạy trên nền CUDA bên dưới.
2. Kiến trúc CUDA: thread, block, warp, SM, grid
Kiến trúc CUDA khá khó hình dung nếu chỉ đọc tài liệu suông, nên cách dễ nhớ nhất là đi từ dưới lên:
- CUDA thread: đơn vị thực thi song song thấp nhất cho một kernel function.
- CUDA block (hay CUDA thread block): một tập hợp các thread, thường gồm 32 thread — một block gồm 32 thread như vậy được gọi là một warp.
- CUDA kernel: hàm được viết (thường bằng C++) thực hiện một phép toán cụ thể, chạy trên toàn bộ các thread trong block.
- CUDA core: đơn vị thực thi số học đơn giản bên trong streaming multiprocessor, thực hiện các phép toán số nguyên và dấu phẩy động cho từng CUDA thread. CUDA core nhỏ và đơn giản hơn nhiều so với một core CPU.
- Streaming Multiprocessor (SM): đơn vị thực thi lõi bên trong GPU NVIDIA, chạy các warp của CUDA thread. Một SM chứa CUDA core, tensor core, load/store unit, thanh ghi.
- CUDA grid: chạy trên nhiều SM, và một CUDA grid ánh xạ tới đúng một GPU.
💡 Hình dung như thế này: GPU giống một nhà máy lớn. Nhà máy có nhiều phân xưởng (Streaming Multiprocessor). Mỗi phân xưởng có nhiều tổ đội làm việc (CUDA block), và mỗi tổ đội gồm đúng 32 công nhân luôn thao tác đồng bộ với nhau — đó chính là một warp. Mỗi công nhân (CUDA thread) đứng tại một trạm làm việc riêng (CUDA core) để thực hiện phép tính được giao. Toàn bộ nhà máy — tất cả phân xưởng cùng hoạt động song song — chính là CUDA grid, và một nhà máy như vậy chỉ vận hành trên đúng một GPU.
Chương trình CUDA đã biên dịch có nhiều block (ví dụ 8 block), và CUDA runtime sẽ quyết định cách phân bổ các block này cho các SM (streaming multiprocessor), phân bổ đồng đều tùy theo số lượng SM có trên GPU. Các thread block riêng lẻ được lên lịch (scheduled) vào từng SM cụ thể.
Hai thuật ngữ hay gây nhầm lẫn: khi CUDA nói tới host, đó là CPU của hệ thống (và host memory là bộ nhớ của CPU); khi nói tới device, đó là GPU (và device memory là bộ nhớ của GPU).
Một số công cụ debug đáng biết khi làm việc với CUDA: Nsight, GDB, và memcheck — các công cụ này không xuất hiện trong đề thi, nhưng nên biết để đưa vào bộ công cụ khi cần gỡ lỗi.
3. Các tầng bộ nhớ trong CUDA
Phía GPU trong CUDA có các tầng bộ nhớ sau:
| Tầng bộ nhớ | Đặc điểm |
|---|---|
| Register (thanh ghi) | Riêng cho từng thread, không thể nhìn thấy giữa các thread khác nhau, tốc độ rất nhanh cho từng thread |
| L1 / shared memory | Bộ nhớ nhanh trên chip, giống bộ nhớ CPU, dùng chung trong phạm vi một CUDA block — tài nguyên vật lý chia sẻ giữa các block trên cùng một SM |
| Read-only memory | Bộ nhớ chỉ đọc trên mỗi SM: instruction cache, constant memory, texture memory, RO cache — chỉ đọc trong kernel |
| L2 | Chia sẻ giữa nhiều SM |
| Global memory | Tầng lớn nhất nhưng chậm nhất |
| Local memory | Trông giống như đang dùng register, nhưng khi giá trị quá lớn, dữ liệu sẽ được đưa vào đây thay vì register |
Về đặc tính tốc độ/dung lượng: global memory rất chậm nhưng dung lượng lớn nhất; shared memory rất nhanh nhưng dung lượng nhỏ; constant memory chỉ đọc, siêu nhanh nhưng cũng siêu nhỏ; register memory siêu nhanh cho từng thread.
Quy trình xử lý CUDA (CUDA processing flow) điển hình gồm các bước:
- Sao chép dữ liệu từ main memory (CPU) sang bộ nhớ GPU.
- CPU khởi động (initiate) compute kernel trên GPU.
- Các CUDA core của GPU thực thi kernel song song.
- Sao chép kết quả từ bộ nhớ GPU trở lại main memory.
Chứng chỉ này không yêu cầu biết viết CUDA ở mức chi tiết, nhưng vì các khái niệm này xuất hiện rất nhiều trong hệ sinh thái NVIDIA, nên vẫn cần nắm được.
4. Quản lý nhiều phiên bản CUDA
CUDA có nhiều phiên bản khác nhau, và mỗi phiên bản có tập tính năng riêng — vì vậy khả năng chạy được của một mô hình có thể bị giới hạn bởi CUDA capability của card đang dùng. Việc phải làm việc với nhiều phiên bản CUDA cùng lúc là chuyện bình thường, nhưng quản lý nhiều phiên bản khá rắc rối vì CUDA không có version manager riêng — thay vào đó, cách làm phổ biến là dựa vào cô lập môi trường (environment isolation) bằng Mamba (một phiên bản gọn nhẹ hơn của Docker), tạo các environment riêng biệt, mỗi environment chỉ định một phiên bản CUDA toolkit cụ thể (ví dụ 11.8) và chuyển đổi qua lại giữa chúng khi cần.
CUDA SDK cung cấp một API cấp thấp (CUDA driver API) và một API cấp cao (CUDA runtime). Đôi khi các tính năng cũ bị deprecated (loại bỏ) và đổi tên ở phiên bản sau, dẫn tới trường hợp một số chương trình chỉ chạy đúng với một phiên bản CUDA rất cụ thể.
5. Thực hành: viết chương trình CUDA đầu tiên
Để thử nghiệm, có thể tải CUDA Toolkit phiên bản mới nhất và cài đặt (trên Windows, cũng có thể dùng qua WSL — Windows Subsystem for Linux). Sau khi cài xong, kiểm tra trình biên dịch NVCC đã sẵn sàng chưa bằng:
nvcc --version
Chương trình CUDA có phần mở rộng file là .cu. Một chương trình CUDA cơ bản (ví dụ cộng hai mảng số — vector add) sẽ định nghĩa một kernel function chạy trên các thread, cấp phát bộ nhớ bằng cudaMalloc, sao chép dữ liệu qua lại giữa host và device, gọi kernel, rồi lấy kết quả về. Biên dịch và chạy chương trình bằng NVCC:
nvcc vector_add.cu -o vector_add
./vector_add
Nếu chương trình in ra kết quả cộng đúng của các vector, nghĩa là môi trường CUDA đã hoạt động — đây là bài "hello world" tiêu chuẩn cho CUDA, dù chưa đề cập sâu tới quản lý bộ nhớ thủ công hay tối ưu hiệu năng.
6. ResNet-50 — model tham chiếu hay gặp trong đề thi
ResNet-50 là một backbone vision model cơ bản, dùng được cho nhiều mục đích khác nhau. Model này xuất hiện rất nhiều trong đề thi và cũng là một model rất phổ biến, tổng quát nên hay được dùng làm ví dụ.
ResNet-50 là một convolutional neural network rất mạnh trong phân loại ảnh (image classification), thuộc họ ResNet (residual network) — cùng họ với các biến thể khác như ResNet-18, ResNet-32. ResNet-50 được phát hành năm 2015, kích thước model chỉ khoảng 100 megabyte. Vì nhỏ, ResNet-50 có thể chạy trên phần cứng đời khá cũ — ví dụ CPU Intel Haswell thế hệ 4, iPhone 8 (2017), hay GPU GTX 700 series (2013, chạy chậm nhưng vẫn hoạt động được). Đây chính là lý do ResNet-50 vẫn phổ biến dù đã ra đời từ lâu: một model cũ không có nghĩa là một model tệ.
Một ví dụ dùng ResNet-50 điển hình: huấn luyện để chạy trên điện thoại và nhận diện một con chó trong ảnh. Có công cụ trực quan hóa tên TensorFace cho phép xem cách ResNet-50 xử lý qua từng lớp (layer có trọng số — weighted layer) để dần đưa ra kết luận cuối cùng, ví dụ nhận diện một tấm ảnh là "chó" hay "kem ốc quế" (ice cream sandwich). ResNet-50 cũng có phiên bản dạng ONNX trong ONNX model zoo — ONNX là định dạng chuẩn hóa để biểu diễn model mà khóa học này sẽ tìm hiểu ở phần sau.
Vì model chỉ nặng khoảng 100 megabyte, nó có thể nằm gọn trong bộ nhớ mà không gặp vấn đề gì khi inference — một điểm đáng lưu ý khi cân nhắc yêu cầu bộ nhớ cho việc triển khai model.
Nguồn tham khảo
Nguồn gốc: NVIDIA-Certified Associate AI Infrastructure and Operations (NCA AIIO) Free Study Course — kênh freeCodeCamp.org