MỞ ĐẦU Tính toán song song hay xử lý song song là quá trình xử lý thông tin trong đó nhấn mạnh việc nhiều đơn vị dữ liệu được xử lý đồng thời bởi một hay nhiều bộ xử lý để giải quyết một
Trang 1TRƯỜNG ĐẠI HỌC CÔNG NGHỆ
Trang 2TRƯỜNG ĐẠI HỌC CÔNG NGHỆ
HOÀNG ĐÌNH THẮNG
LẬP TRÌNH SONG SONG
TRÊN NỀN ĐƠN VỊ XỬ LÝ ĐỒ HỌA
VÀ ỨNG DỤNG
Ngành: Công nghệ Thông tin
Chuyên ngành: Hệ thống Thông tin
Mã số: 60 48 05
LUẬN VĂN THẠC SĨ
NGƯỜI HƯỚNG DẪN KHOA HỌC: TS Nguyễn Trí Thành
Hà Nội – 2010
Trang 3MỤC LỤC
LỜI CAM ĐOAN 1
MỤC LỤC 2
DANH MỤC HÌNH VẼ 4
KÝ TỰ VIẾT TẮT 5
LỜI CẢM ƠN 6
MỞ ĐẦU 7
CHƯƠNG 1: GIỚI THIỆU 9
1.1 Mục đích của lập trình song song 9
1.2 Mục tiêu của luận văn 12
1.3 Tổ chức của luận văn 12
CHƯƠNG 2: TỔNG QUAN VỀ ĐƠN VỊ XỬ LÝ ĐỒ HỌA (GRAPHIC PROCESSING UNIT-GPU) 14
2.1 Tóm tắt lịch sử phát triển của đơn vị xử lý đồ họa (GPU) 14
2.2 Sự khác nhau giữa GPU và CPU 15
2.3 Mô hình xử lý song song dữ liệu 17
2.4 Lợi ích sự thực thi tính toán song song 21
2.5 Phân cấp bộ nhớ 22
2.5.1 Bộ nhớ toàn cục (global memory) và bộ nhớ cục bộ (local memory) 23 2.5.2 Bộ nhớ chia sẻ (shared memory) 24
2.5.3 Bộ nhớ texture và constant 24
2.6 Các chiến lược tối ưu hóa trên GPU 24
2.6.1 Tối đa hóa thực thi song song 25
2.6.2 Tối ưu hóa sử dụng bộ nhớ 25
2.6.3 Tối ưu hóa thực thi các thread 26
2.6.4 Tối ưu hóa sử dụng chỉ lệnh 27
2.7 nVidia CUDA 27
2.8 Thrust 28
Trang 4CHƯƠNG 3: MÔ HÌNH TRƯỜNG NGẪU NHIÊN CÓ ĐIỀU KIỆN
(CONDITIONAL RANDOM FIELDS -CRFS) 29
3.1 Mô hình lý thuyết CRFs 29
3.2 Ước lượng tham số mô hình CRFs 32
3.3 Ứng dụng CRFs trong trích chọn thông tin 35
CHƯƠNG 4: ỨNG DỤNG GPU SONG SONG TỪNG PHẦN CÔNG CỤ CRF++ 37
4.2 Công cụ CRF++ 37
4.2.1 Training 40
4.2.2 Testing 43
4.3 Song song CRF++ 43
4.3.1 Song song phần Training 43
4.4 Kết quả thực nghiệm 48
4.4.1 Môi trường thực nghiệm 48
4.4.2 Đánh giá kết quả 49
KẾT LUẬN 53
TÀI LIỆU THAM KHẢO 54
Trang 5DANH MỤC HÌNH VẼ
Hình 1: Sự khác nhau giữa CPU và GPU – GPU có nhiều bộ xử lý để định
hướng xử lý song song dữ liệu [2] 15
Hình 2: So sánh tốc độ tính toán giữa CPU và GPU [2] 16
Hình 3: Nguyên lý lập trình trên GPU [2] 18
Hình 4: Ví dụ về lưới 5 chiều của thread 19
Hình 5: Luật Amdahl's – tăng tốc phụ thuộc vào phần mã song song và số lượng thread [3] 21
Hình 6: Thu thập và phân tán các hoạt động bộ nhớ [3] 22
Hình 7: Các loại bộ nhớ trong GPU [2] 23
Hình 8: Phân cấp bộ nhớ theo khía cạnh phần mềm (trái) và phần cứng (phải) [2] 23
Hình 9: Phân rã tại nhánh phụ thuộc dữ liệu 27
Hình 10: Điện toán GPU được tăng cường bởi kiến trúc CUDA 28
Hình 11: So sánh mô hình HMMs, MEMMs và CRFs 29
Hình 12: Ví dụ tệp tin huấn luyện 38
Hình 13: Tệp tin mẫu (template) 39
Hình 14: Kết quả thực hiện crf_learn 40
Hình 15: Kết quả thực hiện crf_test 43
Hình 16: So sánh thời gian thực hiên tính toán trên GPU-CPU 49
Hình 17: Biểu đồ thời gian thực hiên tính toán trên GPU-CPU 50
Hình 18: Biểu đồ hình cột so sánh thời gian thực hiên tính toán trên GPU-CPU 50
Hình 19: Tập dữ liệu CoNLL 2000-shared task 51
Hình 20: So sánh thời gian thực hiện phần training 52
Trang 6KÝ TỰ VIẾT TẮT
ALU Arithmetic Logic Unit
API Application Programming Interface
CPU Central Processing Unit
CRFs Conditional Random Fields
CUBLAS CUDA Basic Linear Algebra Subprograms
CUDA Compute Unified Device Architecture
CUDPP CUDA Data Parallel Primitives
CUFFT CUDA Fast Fourier Transform
GPGPU General-Purpose computing on Graphics Processing Units GPU Graphics Processing Unit
MIRA Margin Infused Relaxed Algorithm
MPI Message Passing Interface
OpenCL Open Computation Language
OpenMP Open Multi-Processing
PVM Parallel Virtual Machine
SIMD Single Instruction Multiple Data
SIMT Single Instruction Multiple Thread
VLSI Very Large Scale Integration
Trang 7Tôi xin bày tỏ lòng biết ơn sâu sắc đến TS Nguyễn Trí Thành - người thầy
đã trực tiếp hướng dẫn tôi trong suốt quá trình xây dựng và hoàn thành luận văn này
Đồng thời, xin chân thành cảm ơn lãnh đạo Viện Công nghệ Thông tin thuộc Viện Khoa học và Công nghệ Quân sự đã tạo điều kiện để tôi thực hiện thành công luận văn tốt nghiệp này
Trang 8MỞ ĐẦU
Tính toán song song hay xử lý song song là quá trình xử lý thông tin trong
đó nhấn mạnh việc nhiều đơn vị dữ liệu được xử lý đồng thời bởi một hay nhiều
bộ xử lý để giải quyết một bài toán Trái ngược với xử lý nối tiếp, đòi hỏi phải
xử lý các công việc theo thứ tự tuần tự
Có hai kiểu song song đó là:
Song song về dữ liệu (data parallelism): là cơ chế sử dụng nhiều đơn
vị xử lý thực hiện cùng một thao tác trên nhiều đơn vị dữ liệu
Song song lệnh (control parallelism): là cơ chế trong đó nhiều thao tác khác nhau tác động lên nhiều đơn vị dữ liệu khác nhau một cách đồng thời
Chíp đồ họa, với hàng chục lõi được sử dụng để tạo ra hình ảnh thực trên màn hình máy tính, rất phù hợp cho các nhiệm vụ xử lý song song Trong khi đó thì bộ xử lý, với những lõi mạnh hơn nhưng số lượng ít hơn (giống như Core i7) lại phù hợp hơn cho các ứng dụng xử lý nối tiếp
Trong một báo cáo các nhà nghiên cứu của Intel cho biết, trung bình, NVIDIA GeForce GTX 280 (tung ra hồi tháng 6/2008) nhanh hơn gấp 2,5 lần so với bộ xử lý (BXL) Intel Core i7 960 3,2GHz, và nhanh hơn trên 14 lần trong những hoàn cảnh nhất định
Xử lý song song là hướng nghiên cứu quan trọng, nó giúp cho việc thực hiện các bài toán được nhanh hơn rất nhiều so với xử lý tuần tự Luận văn sẽ hướng tới tìm hiểu bài toán xử lý song song trên nền đơn vị xử lý đồ họa (Graphic Processing Unit-GPU), một trong những hướng xử lý song song đang được phát triển mạnh trong thời gian gần đây
Đồng thời luận văn này cũng tìm hiểu về mô hình trường ngẫu nhiên có điều kiện (Conditional Random Fields-CRFs), một mô hình được ứng dụng thành công trong rất nhiều lĩnh vực như tin-sinh học, xử lý ngôn ngữ tự nhiên và khai phá text/web
CRF++ là một công cụ được xây dựng cho mô hình CRF, áp dụng cho các tác vụ xử lý ngôn ngữ tự nhiên như: nhận dạng thực thể tên (Named Entity Recognition), trích chọn thông tin (Information Extraction) và Text Chunking CRF++ là một phần mềm nguồn mở, được xây dựng bằng ngôn ngữ C++
Trang 9Luận văn này sẽ nghiên cứu cách mà tác giả của CRF++ thực hiện mô hình CRF lý thuyết Từ đó đề xuất cách song song một số phần của CRF++ sử dụng
mô hình song song trên đơn vị xử lý đồ họa
Trang 10CHƯƠNG 1: GIỚI THIỆU
1.1 Mục đích của lập trình song song
Yêu cầu thực tế cần thực hiện khối lượng tính toán lớn: bài toán xử lý ngôn ngữ tự nhiên, bài toán xử lý ảnh 3D, thăm dò dầu khí, dự báo thời tiết, (máy tính xử lý tuần tự kiểu von Neumann là không đáp ứng yêu cầu)
Xử lý song song là quá trình xử lý gồm nhiều tiến trình được kích hoạt đồng thời và cùng tham gia giải quyết một bài toán, nói chung xử lý song song được thực hiện trên những hệ thống đa bộ xử lý
Phân biệt xử lý song song với tuần tự:
Trong xử lý tuần tự với một bộ xử lý thì tại mỗi thời điểm chỉ thực hiện được một phép toán
Trong xử lý song song thì nhiều bộ xử lý cùng kết hợp với nhau để giải quyết cùng một bài toán cho nên giảm được thời gian xử lý vì mỗi thời điểm có thể thực hiện đồng thời nhiều phép toán
Mục đích của xử lý song song: là thực hiện tính toán nhanh trên cơ sở sử dụng nhiều bộ xử lý đồng thời Cùng với tốc độ xử lý nhanh hơn, việc xử lý song song cũng sẽ giải được những bài toán phức tạp yêu cầu khối lượng tính toán lớn
Ba yếu tố chính dẫn đến việc xây dựng các hệ thống xử lý song song:
Tốc độ xử lý của các bộ xử lý theo kiểu von Neumann đã dần tiến tới giới hạn, không thể cải tiến thêm được do vậy dẫn tới đòi hỏi phải thực hiện xử lý song song
Hiện nay giá thành của phần cứng (CPU) giảm mạnh, tạo điều kiện
để xây dựng những hệ thống có nhiều bộ xử lý với giá thành hợp lý
Sự phát triển của công nghệ mạch tích hợp (VLSI) cho phép tạo ra những hệ thống có hàng triệu bóng bán dẫn (transistor) trên một chíp
Vấn đề xử lý song song liên quan trực tiếp đến: kiến trúc máy tính, phần mềm hệ thống (hệ điều hành), thuật toán và ngôn ngữ lập trình,v.v…
Hệ thống tính song song: là một tập các bộ xử lý (thường là cùng một loại) kết nối với nhau theo một kiến trúc nào đó để có thể hợp tác với nhau trong hoạt động và trao đổi dữ liệu được với nhau
Trang 11Chúng ta dễ nhận thấy là độ phức tạp của xử lý song song sẽ lớn hơn xử lý tuần tự rất nhiều, và tập trung chủ yếu ở phương diện trao đổi dữ liệu và đồng bộ các tiến trình
Để cài đặt các thuật toán song song trên các máy tính song song chúng ta phải sử dụng những ngôn ngữ lập trình song song Nhiều ngôn ngữ lập trình song song đang được sử dụng như: Fortran 90, Pthread [24] với Fortran/C++, MPI [21] với C/C++, PVM [22] với C/C++, OpenMP [23] với C/C++, v.v…
Một số khó khăn của lập trình song song truyền thống:
Khó khăn thứ nhất – Suy nghĩ tuần tự
Chúng ta có thể thấy rằng tư duy tuần tự là do môi trường lập trình quyết định Từ thập niên 80 đến khoảng gần đây, chúng ta chủ yếu làm việc với máy tính cá nhân, và mô hình thực thi trên đó là từng bước một Cũng có máy tính song song, nhưng hiếm người được dùng nó trong công việc hàng ngày, vì chuyện xây dựng một hệ máy tính như vậy ở mức quốc gia đã cực kỳ tốn kém chứ đừng nói là công ty hay cá nhân
Cũng chính vì thói quen suy nghĩ tuần tự đã ăn sâu như vậy nên giờ chuyển sang suy nghĩ kiểu song song rất là mệt mỏi, vì sẽ phải chú ý tới các lỗi đặc thù
của nó, như bế tắc (deadlock), khóa sống (livelock), điều kiện cạnh tranh,v.v…
Khó khăn thứ hai – Mở rộng hiệu năng
Qui tắc Amdahl [3] trong lập trình song song cho chúng ta biết là lượng
tăng tốc tối đa có thể đạt được bằng việc song song hóa thuật toán thì tỷ lệ nghịch với phần mã chương trình không thể chuyển sang dạng song song Tức là
cho dù chỉ còn 10% mã chương trình không thể song song hóa, thì sự tăng tốc chương trình đó cũng không vượt quá 10 lần Tuy nhiên để ước lượng mức độ cải tiến theo kiểu này rất khó, vì không thể xác định chính xác bao nhiêu phần trăm mã chương trình thật sự chạy song song, do trong lúc thực thi song song,
mọi thứ lại rất có thể bị chuyển ngược lại thành tuần tự, như khi có tranh chấp
tài nguyên dùng chung, hay có truy cập đến nhiều vị trí khá cách xa nhau trong
bộ nhớ Đây chính là một trong những hạn chế chính của mô hình lập trình song truyền thống (vốn thường cài đặt cách điều khiển tiểu trình qua cơ chế khóa, hay
qua giao diện truyền thông điệp, hay nhiều cách khác nữa đòi hỏi sự đồngbộ hóa
– tức là là tiến trình này đợi tiến trình kia hoàn tất công việc rồi mới làm tiếp phần việc tiếp theo của mình) khi áp dụng cho nhiều nhân/lõi Nói một cách dễ hiểu, nếu chúng ta chỉ phải đợi từng người một hoàn tất công việc của họ một cách độc lập, thì thời gian chờ đợi có thể coi là tăng tuyến tính với số người cần
Trang 12đợi/đồng bộ Nhưng nếu chúng ta phải đợi cùng lúc nhiều người, và kết quả công việc của họ lại phụ thuộc lẫn nhau, thì thời gian chờ đợi sẽ tăng tổ hợp và rất khó xác định, chứ không đơn giản chỉ là tăng tuyến tính
Còn có một khó khăn cơ bản hơn nữa trong mở rộng hiệu năng với số nhân/lõi Mô hình lập trình song song truyền thống tiếp cận bài toán cần giải quyết theo kiểu top-down, bằng cách chia một bài toán lớn thành nhiều tác vụ con, và mỗi tác vụ này sẽ được giao cho một bộ xử lý/nhân/lõi Vấn đề rắc rối ở đây là khi số lượng bộ xử lý/nhân/lõi tăng lên đến hàng chục, hay hàng trăm như hiện nay, thì số tác vụ con đã được thiết kế để giao nhằm tận dụng sức mạnh tính toán của chúng lại không có đủ Cách tiếp cận phổ biến nhất cho tình huống này là lại cố chia nhỏ các tác vụ con đó thành nhiều đơn vị thực thi nhỏ hơn Như vậy, cách làm này bị phụ thuộc vào từng thế hệ phần cứng bộ xử lý, dẫn đến chuyện không thể mở rộng việc thực thi chương trình một cách dễ dàng theo kiểu không cần phải thay đổi, sửa chữa gì về mặt thuật toán hay mã nguồn cài đặt Và suy cho cùng thì nếu một bài toán mà được chia thành vài chục tác vụ
con chạy song song sẽ vô cùng khó quản lý và gỡ rối
Khó khăn thứ ba – Tuần tự sang song song
Việc cố gắng chuyển mã từ tuần tự sang song song dựa trên tác vụ là quá trình này cần rất nhiều thời gian, có khi cả vài năm trời Do tốc độ gia tăng số nhân/lõi hiện tại quá nhanh, nên nhiều khi thuật toán song song mà chúng ta đã
bỏ ra vài năm kể từ năm nay để thiết kế lại không mở rộng tốt với số nhân/lõi của vài năm nữa, ở thời điểm chúng ta hoàn tất việc cài đặt nó một cách song song
Ý tưởng gán một công việc nhỏ cho từng bộ xử lý/nhân/lõi trong máy tính nhằm tăng hiệu năng phần mềm chạy trên đó là hoàn toàn tự nhiên và được đúc kết từ kinh nghiệm trong đời sống hàng ngày Tuy nhiên để tận dụng được hết sức mạnh của hàng trăm, rồi hàng ngàn nhân/lõi trong tương lai, chúng ta có lẻ cần phải “đi trước đón đầu” bằng cách tạo ra hàng chục ngàn, hàng trăm ngàn công việc nhỏ li ti cung cấp cho đội quân nhân/lõi đông đảo và ngày một gia tăng đó
Với những khó khăn đó, yêu cầu về một mô hình lập trình song kiểu mới
linh hoạt hơn và theo sát với sự thay đổi về số lượng nhân /lõi của một hệ thống
máy tính Đó là lập trình song song đa dụng trên đơn vị xử lý đồ họa-GPU
Trang 131.2 Mục tiêu của luận văn
Vì giới hạn của phần cứng, xu hướng tăng số lõi (cores) thay vì tăng xung
nhịp (clock rate) đang trở nên phổ biến CPU thì có multicores, còn GPU thì có
many-cores Ngày nay, GPU có thể hoạt động nhanh hơn cả CPU trong việc xử
lý tính toán Cụ thể nhờ số lượng cores lớn (vài trăm so với 2,4, hoặc 8 của CPU), tốc độ truy xuất bộ nhớ nhanh (85GB/s với G80, 150GB/s với GTX200) Nhờ thế, vai trò của GPU đã vượt ra ngoài phạm vi truyền thống, đó là chạy các ứng dụng tính toán Tuy nhiên, GPU vẫn chưa thay thế hoàn toàn được CPU, do
có nhiều tác vụ thực hiện hiệu quả hơn trên CPU Do đó, GPU chỉ đóng vai trò
là một co-processor
Mục đích của luận văn này là giới công nghệ song song được dùng trên nền đơn vị xử lý đồ họa (GPU) và cung cấp cái nhìn tổng quan về xu hướng hiện nay trong tính toán song song đa dụng trong GPU Trình bày về các lợi thế của tính toán song song quy mô lớn trên GPU
Mô hình trường ngẫu nhiên có điều kiện (Conditional Random CRFs), một mô hình được ứng dụng thành công trong rất nhiều lĩnh vực như tin-sinh học, xử lý ngôn ngữ tự nhiên và khai phá text
Fields-CRF++ là một công cụ xây dựng cho mô hình CRF, được viết bằng ngôn ngữ C++ Luận văn này sẽ ứng dụng GPU để song song hóa từng phần của CRF++, nhằm mục đích cải thiện tốc độ tính toán của CRF++, nhất là trong trường hợp xử lý số liệu lớn (hàng chục nghìn câu, hàng triệu thuộc tính)
1.3 Tổ chức của luận văn
Luận văn này gồm 4 chương, với nội dung sơ bộ như sau:
Chương 1 - Giới thiệu nêu lên mục đích của lập trình song song, những khó
khăn của lập trình song song truyền thống, dẫn đến yêu cầu một mô hình lập trình song song kiểu mới linh hoạt hơn Chương này cũng trình bày mục tiêu của luận văn, tổ chức của luận văn
Chương 2 - Tổng quan về đơn vị xử lý đồ họa (GPU), cung cấp cái nhìn
tổng quan về tính toán song song đa dụng trên đơn vị xử lý đồ họa Mô tả sự khác nhau cơ bản giữa đơn vị xử lý trung tâm (CPU) và đơn vị xử lý đồ họa (GPU) Phần lớn của chương này dành cho việc giải thích các nguyên tắc cơ bản của tính toán song song đa dụng trên đơn vị xử lý đồ họa và giao diện lập trình ứng dụng (API) trên GPU
Trang 14Chương 3 - Mô hình trường ngẫu nhiên có điều kiện (CRF), trình bày mô
hình lý thuyết CRF, phương pháp ước lượng tham số của mô hình CRF và một
số ứng dụng của CRF
Chương 4 - Ứng dụng GPU song song hóa từng phần công cụ CRF++,
giới thiệu công cụ CRF++, cách thức mà tác giả của CRF++ thực hiện, từ đó đề xuất chiến lược song song CRF++ bằng GPU thông qua các thư viện lập trình cho GPU như CUDA, Thrust Phần cuối của chương sẽ đưa ra kết quả thực nghiệm cũng như một số đánh giá, nhận xét
Trang 15CHƯƠNG 2: TỔNG QUAN VỀ ĐƠN VỊ XỬ LÝ ĐỒ HỌA (GRAPHIC PROCESSING UNIT-GPU)
2.1 Tóm tắt lịch sử phát triển của đơn vị xử lý đồ họa (GPU)
Trong suốt 20 năm qua GPUs đã trải qua những phát triển lớn Bắt đầu phát triển từ đơn vị xử lý đồ họa 2D đơn giản, đến đa mục đích, có thể lập trình được, song song mức độ cao, có nhiều lõi đơn vị xử lý với khả năng tính toán phi thường và băng thông bộ nhớ rất cao
Lịch sử của GPUs những năm 1970 với các máy tính có Atari 8-bit Những chíp đồ họa này được sử dụng để hòa trộn đồ họa và dữ liệu văn bản rồi hiển thị chúng lên màn hình Những năm 1980, kiến trúc của chíp này trở thành nền tảng của sản xuất chíp hàng loạt tiếp theo, được tích hợp trong Commodore Amiga Chíp này là chip đầu tiên có đầy đủ các đặc tính tăng tốc đồ họa – nó chứa các đặc tính như: vẽ hình ảnh gốc, tô màu hình ảnh, chuyển khối hình ảnh, v.v….Đặc tính cách mạng là tất cả video được đẩy vào phần cứng Năm 1984, gần 10 năm sau khi phần cứng tăng tốc đồ họa trở thành chuẩn, IBM giới thiệu tăng tốc 2D/3D đầu tiên dưới cái tên IBM Professional Graphics Controller, nhưng thành công thì không được như mong đợi Việc thiếu tính tương thích, xử
lý chậm và giá cao ($4.500) làm cho dự án này thất bại [7]
Trong suốt những năm 1990, nhu cầu tăng tốc đồ họa trở nên cấp thiết, bởi
vì số lượng máy trạm ngày càng tăng với nền tảng công nghệ của Microsoft Windows Năm 1993, S3 Graphics giới thiệu chíp đơn 2D tăng tốc đầu tiên - S3 86C911, dưới tên mã là Porsche 911 Chíp này thiết lập chuẩn tăng tốc 2D trong vòng vài năm Năm 1995 gần như tất cả các nhà sản xuất chíp đã bổ sung hỗ trợ tăng tốc 2D vào sản phẩm của họ
Giữa những năm 1990 đồ họa 3D thời gian thực trở nên phổ biến hơn trong máy tính và trò chơi điều khiển Nhu cầu đồ họa 3D thời gian thực dẫn đến phát triển tăng tốc 3D thời gian thực đầu tiên Các chíp giá rẻ như S3 ViRGE, Matrox Mystique hay Ati Rage được dựa trên 2D tăng tốc và thêm các đặc tính 3D Bước đột phá đầu tiên đến năm 1996, khi 3Dfx Interactive giới thiệu tăng tốc 3D đầu tiên đầu đủ tính năng có tên là Voodoo [8] Gia tăng tốc độ này xác định tiêu chuẩn mới cho chip đồ họa 3D cho ba năm tiếp theo Bước đột phá lớn thứ hai đến năm 1999 khi tổng công ty nVidia giới thiệu GPU đầu tiên – NV10,
Trang 16dưới tên mã GeForce 256 Tất cả các con chíp hiện nay về cơ bản đều xuất phát
từ kiến trúc này
Vào cuối năm 2006 nVidia giới thiệu kiến trúc thiết bị hợp nhất cho tính toán đầu tiên - Compute Unified Device Architecture (CUDA) Các công ty đối thủ AMD/ATI giới thiệu kiến trúc của họ (ATI FireStream) vài tháng sau đó Thời điểm này GPU trở nên dễ tiếp cận để phát triển phần mềm bằng các ngôn ngữ lập trình chuẩn công nghiệp và thiết lập cách thức mới cho tính toán hiệu năng cao [2]
2.2 Sự khác nhau giữa GPU và CPU
Ý tưởng chính của tính toán song song đa dụng trên GPU (GPGPU) là sử dụng nhiều bóng bán dẫn (transistor) để xử lý dữ liệu và xử lý dữ liệu song song Đây là sự khác biệt chính giữa CPU và GPU GPU chủ yếu định hướng các thao tác song song dữ liệu hơn là trữ dữ liệu (caching data) và điều khiển luồng [2] Nhu cầu này chủ yếu xuất phát từ dựng hình đồ họa, bởi vì thiết bị đồ họa bắt buộc phải xử lý một lượng lớn các phần tử dữ liệu đồ họa (như các điểm ảnh, đỉnh và mảnh) và thao tác trên các phân tử này (lightning, blending, clipping, texturing, …) Song song là cách để giảm lượng thời gian cần thiết để xử lý dữ liệu và giảm chi phí tổng thể bằng cách phân phối nó trong xử lý các phần tử Nguyên tắc cơ bản là quá trình dựng hình đồ họa là cố định Dữ liệu được xử lý bởi phần cứng đồ họa đi qua nhiều giai đoạn trước khi chúng được hiển thị và chuỗi sự kiện này là không thay đổi
Hình 1: Sự khác nhau giữa CPU và GPU – GPU có nhiều bộ xử lý để định
hướng xử lý song song dữ liệu [2]
Trang 17Hình 2: So sánh tốc độ tính toán giữa CPU và GPU [2]
CPU phải xử lý nhiều tác vụ khác nhau vào cùng một thời gian Vì vậy CPU phải được tối ưu hóa cho mục đích này, chủ yếu lưu trữ dữ liệu quan trọng được dùng bởi tiến trình đang chạy và nhanh chóng chuyển ngữ cảnh giữa các tiến trình đang chạy Tất cả các tác vụ được cung cấp một thời gian xử lý xác định trước để thực thi mã của chúng Khi đến thời gian này, hệ điều hành chuyển CPU sang tiến trình tiếp theo Chuyển đổi ngữ cảnh giữa các tiến trình đang chạy có thể được tính toán với cường độ lớn
Trang 18Ngược lại, GPU xử lý nhiều nhất một nhiệm vụ tại một thời điểm nhưng thực hiện nhiều lần song song GPU sử dụng mô hình song song dữ liệu, để có thể giải quyết các vấn đề số học cường độ cao hơn CPU Khi cùng một đoạn mã GPU được thực hiện song song nhiều lần thì không cần kiểm soát luồng và quản
lý bộ nhớ đệm (cache) Thực thi tác vụ song song không được hỗ trợ trong GPU
Có nghĩa là, không thể thực thi hai hoặc nhiều chương trình khác nhau trên cùng GPU vào cùng một thời điểm
Mô hình xử lý dữ liệu song song ánh xạ các phần tử dữ liệu vào các đơn vị làm việc cơ sở được gọi là các luồng (thread) Tính song song thread cùng với phân cấp bộ nhớ chia sẻ và hàng rào đồng bộ hóa giữa các luồng làm nên khái niệm trừu tượng về mô hình lập trình song song đa dụng của GPU [2]
2.3 Mô hình xử lý song song dữ liệu
Mục này sẽ trình bày các khái niệm cơ bản trong đơn vị xử lý đồ họa
Kernel là một đoạn mã chương trình, được thể hiện bởi một hàm hay thủ
tục viết bằng một ngôn ngữ lập trình cụ thể Thread là một thực thể chạy mã nhị
phân, được thực thi song song nhiều lần [2] Có thể nói kernel là một “mẫu
hàm” cho một nhóm thread Hai thuật ngữ quan trọng tiếp theo là host và device
Host là bao gồm bộ xử lý trung tâm và DRAM của CPU, còn device gồm các bộ
xử lý và DRAM của GPU Host và device được phân tách bởi không gian địa chỉ của hai DRAM Host thực thi chương trình chính (mã tuần tự) còn device thực thi kernel (mã song song) Device trong mô hình này làm việc giống như một bộ
đồng xử lý (co-processor) đối với host Hình 3 thể hiện cách thức thực thi mã tuần tự và song song
GPU thread dùng xử lý dữ liệu song song và mô hình lập trình Trong mô hình này, các phần tử dữ liệu cơ sở được lưu trong bộ nhớ GPU được ánh xạ vào thread đang chạy Bộ nhớ GPU (không gian địa chỉ) là một mảng tuyến tính của các phần tử cơ bản (bytes) Để ánh xạ thread vào bộ nhớ đối tượng chúng ta cần biết cách tổ chức thread Cơ cấu tổ chức này phải có khả năng xác định rõ đối tượng bộ nhớ từ các giá trị định danh thread được sinh ra một cách tự động Để làm được điều này, cách dễ dàng nhất là tổ chức thread thành lưới (grid) ảo đa chiều của các thread Với công nghệ CUDA, lưới này có 5 chiều và để định hướng tốt hơn, lại được chia thành 2 loại: khối 3 chiều của thread và lưới 2 chiều của khối của thread
Trang 19Hình 3: Nguyên lý lập trình trên GPU [2]
Số định danh duy nhất của thread có thể ước lượng từ một số biến liên quan, được khởi tạo khi lưới của khối thread được liệt kê Các biến này có thể truy cập từ bên trong mỗi kernel Mỗi thread có biến riêng của nó, chứa tọa độ
Trang 20ba chiều x, y, z trong khối Khối thread chứa các biến chiều x, y, z ứng với các trục , , và tọa độ 2 chiều x, y trong lưới khối thread Các chiều của lưới khối thread cũng có thể truy cập từ trong các biến x, y Các chiều của khối thread và của lưới phải được thiết lập trước khi chạy các tính toán Số định danh duy nhất của thread trong khối thread và số định danh duy nhất của block trong grid được tính như sau [2]:
threadId = x + y * x + z * x * y (2.1)
blockId = x + y * x (2.2)
Hình 4: Ví dụ về lưới 5 chiều của thread
Cơ cấu phân cấp cung cấp phương tiện để tính toán trên các cấu trúc dữ liệu như véctơ, ma trận hay mảng ba chiều Tuy nhiên do hạn chế của phần cứng, kích thước của khối thread hay số thread trong mỗi block là giới hạn1
1
Đối với nVidia CUDA tương thích với 1.x, kích thước lớn nhất của khối thread là 512x512x64, nhưng
có nhiều nhất 512 thread trong mỗi block
Trang 21Nghĩa là chúng ta không thể gọi khối thread với kích thước bất kỳ Đây là một hạn chế, nhưng chúng ta có thể “lợi dụng” tính toán theo một số khối của các thread Nếu làm điều này, chúng ta luôn phải xác định kích thước của grid để
thực hiện các tính toán Tối đa kích thước của khối thread luôn luôn có nghĩa là
giảm thiểu kích thước của grid và ngược lại Vậy làm thế nào để chúng ta chọn
kích thước của khối thread, mà vẫn tuân theo giới hạn của phần cứng Ví dụ, nếu chúng ta muốn ánh xạ véctơ kích thước với khối kích thước , chúng ta cần xác định kích thước của grid của khối thread Có thể tính toán như sau:
ϵ and 1 ≤ ≤ maxBlockDimensionX 1≤ ≤ maxThreadsPerBlock
Để xác định kích thước của và trong trường hợp ánh xạ ma trận kích thước * vào block kích thước , :
ϵ and 1 ≤ ≤ maxBlockDimensionX
ϵ N and 1 ≤ ≤ maxBlockDimensionY
1 ≤ * ≤ maxThreadsPerBlock
Trường hợp ánh xạ mảng 3 chiều * * chúng ta dùng biểu thức như trên, với thay đổi điều kiện ban đầu
and 1 ≤ ≤ maxBlockDimensionX and 1 ≤ ≤ maxBlockDimensionY and 1 ≤ ≤ maxBlockDimensionZ
1 ≤ * * ≤ maxThreadsPerBlock
Trang 222.4 Lợi ích sự thực thi tính toán song song
Để đạt được lợi ích tối đa từ tính toán song song chúng ta cần tìm giải pháp làm thế nào để song song mã tuần tự Trong GPGPU điều này được thực hiện bởi song song thread và mô hình thực thi song song dữ liệu Lợi ích tối đa (tăng tốc lý thuyết) có thể được diễn tả bởi luật Amdahl's [3]
Trong đố là một phần của chương trình có thể được thực hiện song song, tượng trưng cho phần mã được thực hiện tuần tự và là số bộ xử lý hoặc
thread thực hiện phần song song Hình 5 minh họa tăng tốc lý thuyết cho các phần khác nhau của mã song song phụ thuộc vào số lượng thread thực hiện song song
Hình 5: Luật Amdahl's – tăng tốc phụ thuộc vào phần mã song song
và số lượng thread [3]
Trang 232.5 Phân cấp bộ nhớ
Ngày này, GPUs hỗ trợ đánh địa chỉ bộ nhớ byte-wise và hỗ trợ thu thập
(gather) và phân tán (scatter) các hoạt động bộ nhớ Truy cập bộ nhớ song song
là giải pháp chính cho thông lượng bộ nhớ lớn và xử lý dữ liệu song song Điều quan trọng cần đề cập đến là tất cả các hoạt động bộ nhớ phải được hợp nhất Hợp nhất truy cập bộ nhớ nghĩa là đọc từ một tập địa chỉ liên tục phải tương ứng với tổ chức thread (số định danh thread) Cách tốt nhất để hợp nhất bộ nhớ truy cập là ánh xạ thread và block vào bộ nhớ đối tượng Không hợp nhất hoặc truy cập bộ nhớ ngẫu nhiên dẫn đến đọc và ghi tuần tự dữ liệu và làm mất hiệu suất [2]
Hình 6: Thu thập và phân tán các hoạt động bộ nhớ [3]
Có thể xem xét bộ nhớ GPU từ hai khía cạnh: phần cứng và phần mềm Với khía cạnh phần mềm, cần phân biệt giữa các bộ nhớ global, local, shared, texture và constant Mỗi loại bộ nhớ có chức năng đặc thù và mục đích sử dụng riêng Với cái nhìn phần cứng, chỉ cần phân biệt giữa bộ nhớ thiết bị (DRAM),
bộ nhớ chia sẻ (shared memory) với constant và texture cache
Trang 24Hình 7: Các loại bộ nhớ trong GPU [2]
Hình 8: Phân cấp bộ nhớ theo khía cạnh phần mềm (trái)
và phần cứng (phải) [2]
2.5.1 Bộ nhớ toàn cục (global memory) và bộ nhớ cục bộ (local memory)
Bộ nhớ toàn cục là phần bộ nhớ lớn nhất trong thiết bị GPU Bộ nhớ này nằm trong DRAM của thiết bị và có thể truy cập từ tất cả các thread trong grid
và từ host-device thông qua thƣ viện runtime đặc thù Các đối tƣợng bộ nhớ khai báo trong không gian bộ nhớ toàn cục có thời gian tồn tại là thời gian của ứng dụng Bộ nhớ toàn cục có thông lƣợng bộ nhớ rất lớn (hàng trăm gigabyte/giây) Hạn chế chính của loại bộ nhớ này là độ trễ rất lớn khi đọc và ghi dữ liệu (hàng
Trang 25trăm chu kỳ đồng hồ-hundreds of clock cycles) Nguyên nhân là do bộ nhớ toàn cục không có bộ nhớ đệm (cache) Vì thế, nên hạn chế làm việc trực tiếp với bộ nhớ toàn cục, thay vào đó sử dụng bộ nhớ chia sẻ Ưu điểm của loại bộ nhớ toàn cục là tải dữ liệu, lưu trữ chỉ lệnh và sử dụng con trỏ thông thường và con trỏ số học
Bộ nhớ cục bộ cũng tương tự như bộ nhớ toàn cục Nó thuộc về DRAM của thiết bị nhưng chỉ truy cập bên trong mỗi thread và có kích thước giới hạn (hàng chục kilobytes) Đối tượng khai báo trong bộ nhớ này có thời gian tồn tại
là thời gian của khối thread Điểm mạnh và điểm yếu của bộ nhớ này giống với
bộ nhớ toàn cục
2.5.2 Bộ nhớ chia sẻ (shared memory)
Bộ nhớ chia sẻ là bộ nhớ nhỏ (chục kilobytes) có thể truy cập từ tất cả các thread trong một khối thread, vì thế đối tượng khai báo trong bộ nhớ này có thời gian tồn tại cùng với khối thread Bộ nhớ chia sẻ làm việc giống như L1 cache trên CPU chuẩn Nó cung cấp độ trễ truy cập rất thấp và thông lượng bộ nhớ rất cao (gần như tương đương với thông lượng của thanh ghi nếu không có truy cập xung đột giữa các thread) Cách sử dụng shared memory khó hơn so với global memory Để đạt được hiệu suất tối đa người lập trình phải giải quyết vấn đề xung đột truy cập shared memory và tổ chức cấu trúc dữ liệu
2.5.3 Bộ nhớ texture và constant
Đây là hai bộ nhớ chuyên biệt Cả hai thuộc về DRAM và có thể truy cập
từ tất cả các thread trong grid và từ host thông qua thư viện thời gian chạy đặc thù Không giống như global và local memory, cả hai bộ nhớ này đều là cached,
vì thế truy cập đến hai bộ nhớ này nhanh hơn nhiều so với truy cập global memory Hơn nữa, một số phần cứng đồ họa có thể truy cập dữ liệu trong bộ nhớ texture và thực hiện các thao tác chuyên biệt Đối tượng khai báo trong texture và constant memory có vòng đời của ứng dụng
2.6 Các chiến lược tối ưu hóa trên GPU
Không cân nhắc trước khi sử dụng GPU có thể dẫn đến kết qủa không như
ý muốn Trong trường hợp xấu nhất có thể cho kết quả sai hoặc tiêu tốn quá nhiều thời gian Ví dụ, sẽ không hiệu quả nếu chạy thuật toán sử dụng một thread Trong trường hợp này phải thực hiện sao chép dữ liệu vào device, thực thi thuật toán trên device và sao chép dữ liệu trở lại host Dữ liệu di chuyển giữa host và device là tốn nhất trong quá trình này, mặc dù với công nghệ ngày nay
có thể cung cấp băng thông dữ liệu rất cao Chúng ta cần nhận thấy rằng băng
Trang 26thông trên device cũng như trên host lớn hơn nhiều so với băng trong kết nối giữa host và device
Trước khi viết một chương trình GPU, cần phải xem xét các khía cạnh kết quả từ kiến trúc GPU Kiểu lập trình GPU yêu cầu cách tiếp cận khác với lập trình CPU Đó là do kiến trúc phần cứng và mô hình thực thi chương trình GPU hoạt động như một bộ xử lý song song với CPU CPU rất mạnh trong thực thi
mã tuần tự, trữ dữ liệu và xử lý luồng Trong chương trình GPU, CPU đóng vai trò như là đối tượng giám sát mã GPU Nghĩa là CPU chỉ đảm nhận các hoạt động bộ nhớ cơ bản (cấp phát, giải phóng bộ nhớ GPU, chuyển dữ liệu giữa host
và device) và gọi kernel Các tính toán khác được thực hiện trong bộ nhớ GPU
Có 4 chiến lược thực thi chính để đạt được lợi ích thời gian lớn nhất từ GPGPU [3]:
- Tối đa hóa thực thi song song,
- Tối ưu hóa sử dụng bộ nhớ,
- Tối ưu hóa thực thi thread,
- Tối ưu hóa sử dụng chỉ lệnh
2.6.1 Tối đa hóa thực thi song song
Tối đa hóa thực thi song song là chiến lược cơ bản nhất, và chiến lược trừu tượng nhất trong nhiều trường hợp trong tính toán song song Có hai cách để song song hiệu quả bài toán Đầu tiên bao gồm phân rã vấn đề thành các vấn đề con và ánh xạ chúng vào các thực thi nguyên thủy (các khối thread, thread) Tiếp theo ánh xạ các thực thi nguyên thủy vào cấu trúc dữ liệu trong bộ nhớ Chiến lược thứ hai là ngược lại của chiến lược thứ nhất Cần phải tìm cách ánh xạ dữ liệu vào các thực thi nguyên thủy để giải quyết bài toàn Tùy thuộc vào bài toán đưa ra để chọn chiến lược cụ thể
Nói chung, thuật toán bằng cách nào đó phản ánh được thực hiện phần cứng của thiết bị để đạt được hiệu quả lớn nhất, hơn nữa phải chắc chắn rằng thuật toán được ánh xạ vào hiệu quả phần cứng
2.6.2 Tối ưu hóa sử dụng bộ nhớ
Chiến lược đầu tiên của việc tối ưu hóa sử dụng bộ nhớ là giảm thiểu việc chuyển dữ liệu giữa host và device Băng thông chuyển giữa host và device nhỏ hơn rất nhiều so với chuyển dữ liệu trong device và host Tiếp đến là việc tối ưu truy cập bộ nhớ toàn cục Nên dùng bộ nhớ chia sẽ, kết hợp với bộ nhớ toàn cục
Trang 27Bộ nhớ chia sẻ làm việc giống L1cache trong một bộ đa xử lý và hầu như
có chi phí truy cập bằng không, vì thế rất nhanh Mỗi khối thread truy cập bộ nhớ chia sẻ của nó Tối ưu gồm chuyển dữ liệu từ bộ nhớ toàn cục sang bộ nhớ chia sẽ, thực hiện các tính toán trên dữ liệu của bộ nhớ chia sẽ, sau đó chuyển kết quả ngược lại bộ nhớ toàn cục Một điều quan trọng là, truy cập đến bộ nhớ chia sẻ phải được tổ chức hợp lý để tránh xung đột, vì bộ nhớ chia sẻ có thể truy cập song song bởi nhiều thread Xung đột truy cập giữa các thread có thể dẫn đến thực thi tuần tự các yêu cầu bộ nhớ và làm mất hiệu suất tính toán
Truy cập đến bộ nhớ toàn cục độ trễ rất lớn (hàng trăm chu kỳ đồng hồ), vì thế phải cẩn thận khi làm việc với dữ liệu trên bộ nhớ này Hợp nhất truy cập bộ nhớ nghĩa là đọc từ một tập địa chỉ liên tục phải tương ứng với tổ chức thread (số định danh thread) Hợp nhất truy cập bộ nhớ là vấn đề phụ thuộc thiết bị và không có chiến lược chung
Kỹ thuật tối ưu bộ nhớ tiếp theo là sử dụng bộ nhớ constant và texture Bộ nhớ constant và texture là bộ nhớ chỉ đọc (từ thiết bị-device), có thể bộ nhớ đệm, nên truy cập loại bộ nhớ này tương đối nhanh Ví dụ, để tiết kiệm bộ nhớ chia
sẽ, chúng ta lưu trữ một số đối số của kernel vào trong bộ nhớ constant Bộ nhớ texture dùng cho các thao tác đồ họa
2.6.3 Tối ưu hóa thực thi các thread
Thread được tổ chức trong khối (block), được thực thi trên bộ đa xử lý Mỗi khối của các thread được phân chia thành từng phần gồm 32 thread bởi đơn
vị chỉ dẫn trong bộ đa xử lý và được thực thi trên bộ xử lý vô hướng Việc đầu tiên để cải thiện hiệu suất là tạo khối thread (chứa số lượng thread là bội của 32) Điều này sẽ cung cấp hiệu quả tính toán tối ưu và sẽ tạo điều kiện hợp nhất truy cập bộ nhớ
Chiến lược tối ưu quan trọng nhất là tránh các đường thực thi khác nhau trong cùng một warp (warp là một nhóm gồm 32 thread) Nếu một đường thực thi của thread phân rã ở nhánh phụ thuộc dữ liệu, tất cả các thread không ở trên đường này được dừng lại cho đến khi tất cả các đường hội tụ lại cùng đường thực hiện
Trang 28Hình 9: Phân rã tại nhánh phụ thuộc dữ liệu
Tất cả các lệnh điểu khiển (nhánh, lặp) có thể làm cho warp đang hoạt động phân rã
2.6.4 Tối ưu hóa sử dụng chỉ lệnh
Tối ưu hóa các chỉ lệnh sử dụng trong hầu hết các trường hợp là kỹ thuật tối ưu có độ ưu tiên thấp nhất và được thực hiện sau khi xong các tối ưu trên GPU được tối ưu hóa cao cho các phép toán dấu phẩy động Vì thế cần cân nhắc việc ưu tiên cho tốc độ hay cho độ chính xác Hiện nay GPU hỗ trợ số dấu phẩy động độ chính xác kép, nên có thể cung cấp chính tính toán chính xác hơn với chi phí thực hiện
Nên cẩn thận khi chuyển đổi số chính xác đôi sang số chính xác đơn Tránh dùng phép chia, phép chia lấy dư, thay vì thế nên dùng phép dịch chuyển
2.7 nVidia CUDA
CUDA là từ viết tắt trong tiếng Anh của thuật ngữ Compute Unified Device Architecture, tạm dịch là kiến trúc thiết bị hợp nhất cho tính toán CUDA bắt đầu xuất hiện từ tháng 11 năm 2006 với vai trò ban đầu là một bộ công cụ phát triển phần mềm dựa trên ngôn ngữ lập trình C, và giờ đây cuối năm
2010, sau bốn năm phát triển, CUDA đang tiến hóa thành kiến trúc điện toán GPU, hay còn gọi là GPGPU, của NVIDIA CUDA có mặt trên hầu hết các GPU đời mới của NVIDIA, từ dòng GeForce giành cho giải trí, đến Quadro giành cho điện toán hình ảnh chuyên nghiệp, ví dụ như trong thiết kế và sáng tạo, và mới nhất là dòng Tesla cho tính toán hiệu năng cao
Hình dưới đây cho chúng ta thấy bức tranh toàn cảnh về điện toán GPU xét trên khía cạnh phần mềm từ quan điểm và công nghệ của NVIDIA Trong hình này, ở lớp thấp nhất của kiến trúc phần mềm giành cho điện toán GPU, chúng ta
có các GPU của NVIDIA được trang bị kiến trúc tính toán song song CUDA, bao gồm cả trình điều khiển thiết bị CUDA vốn được nhúng bên trong trình điều khiển thiết bị đồ họa do NVIDIA cung cấp