Sắp xếp nhanh (Quicksort), còn được gọi là sắp xếp kiểu phân chia là một thuật toán sắp xếp dựa trên phép phân chia danh sách được sắp thành hai danh sách con. Khác với sắp xếp trộn, chia danh sách cần sắp xếp a[1..n] thành hai danh sách con có kích thước tương đối bằng nhau nhờ chỉ số đứng giữa danh sách, sắp xếp nhanh chia nó thành hai danh sách bằng cách so sánh từng phần tử của danh sách với một phần tử được chọn được gọi là thành phần chốt. Những phần tử nhỏ hơn hoặc bằng phần tử chốt được đưa về phía trước và nằm trong danh sách con thứ nhất, các thành phần tử lớn hơn chốt
được đưa về phía sau và thuộc danh sách đứng sau. Cứ tiếp tục chia như vậy tới khi các danh sách con đều có độ dài bằng 1. Độ phức tạp tính toán
O(N.Log(N))
Để thực hiện song song, chuyển danh sách A[1..n] thành cây nhị phân Qtree thỏa mãn các nút gốc thuộc cây con trái có giá trị nhỏ hơn hoặc bằng gốc, các nút thuộc cây con phải có giá trị lớn hơn gốc. Sau khi xây dựng được cây Qtree, tiến hành duyệt theo thứ tự giữa thu được dãy sắp xếp theo chiều tăng dần. Giải thuật vẫn giữ ý tưởng của giải thuật tuần tự nhưng dùng thêm 3 mảng phụ.
54
• Mảng Left: phần tử Left[i] lưu trữ chỉ số con trái của A[i].
• Mảng Right: phần tử Right[i] lưu trữ chỉ số con phải của A[i]
• Mảng Local_pivot: phần tử Local_Pivot[i] lưu trữ chỉ số nút gốc chứa A[i]
Các bước thực hiện gồm:
• Tại mỗi bước thực hiện, mỗi BXL đảm nhận việc so sánh của 1 phần tử trong mảng (ứng với chỉ số BXL) với giá trị của phần tử chốt
• Các nút có giá trị nhỏ hơn Local_Pivot sẽ nằm trong cây con trái của Local_Pivot
• Các nút có giá trị lớn hơn Local_Pivot sẽ nằm trong cây con phải của Local_Pivot
• Giá trị Left[i] được chọn ngẫu nhiên từ các phần tử có giá trị <A[i] item, Giá trị Right[i] được chọn ngẫu nhiên từ các phần tử có giá trị > A[i]
• Lặp lại quá trình cho đến khi các phần tử được đưa hết vào cây nhị
phân
Cài đặt thuật toán với Cuda C cần thực hiện các bước:
• Mảng cần sắp xếp được khởi tạo trên Host Memory
• Sau đó copy sang Device Memory
• Đăng ký số threads và blocks: Do dữ liệu cần xử lý là mảng 1 chiều, do vậy số threads và block khởi tạo theo một chiều. Số luồng thực hiện bằng số luồng lớn nhất mà một block cho phép (XTHREADS=512/1024). Số Block sẽ bằng [N/XTHREADS]. Công việc so sánh với phần tử chốt ở mỗi bước được thực hiện đồng thời.
55 Hình 2.8: Hiệu năng sắp xếp nhanh nhờ sử dụng GPU so với sử dụng CPU
Cùng với hệ thống đánh giá như với trường hợp nhân ma trận, hình 2.8 thể hiện kết quả thực hiện với các kích cỡ dữ liệu khác nhau, được so sánh với CPU. Rõ ràng khi kích thước dãy số càng lớn, việc thực hiện song song trên GPU càng có hiệu quả.
2.3.2 Bài toán nhân ma trận
Bài toán nhân ma trận là bài toán cơ bản trong đại số tuyến tính và
được ứng dụng rất nhiều trong khoa học kĩ thuật. Đặc biệt trong lĩnh vực đồ
hòa máy tính với việc hiển thị hình ảnh trên màn hình 2D. Đầu vào là 2 ma trận vuông A, B kích thước N x N, đầu ra là ma trận vuông C kích thước N thỏa mãn điều kiện C[i,j]=∑N= ⋅
k 1A[x,k] B[k,y]. Độ phức tạp tính toán cho giải thuật tuần tự là O(N3). Đối với giải thuật song song sử dụng nhiều bộ vi xử lý,
độ phức tạp tính toán có thể giảm đi nhưng phí tổn truyền thông lại tăng lên. Thực hiện nhân ma trân với giải thuật song song bằng Cuda C cần thực hiện các bước [19]
56
• Khởi tạo ma trận A, B trên bộ nhớ Host Memory
• Copy dữ liệu của ma trận A, B được sang Device Memory
• GPU tạo ra số Block cần thiết để tính toán. Do số luồng theo các phương là giới hạn theo các trục, nên cần phải tính số block theo các trục x, y tương ứng.
• Các luồng xác định chỉ số các phần tử, chỉ số dòng cột tương ứng và thực hiện tính toán.
• Copy dữ liệu trên bộ nhớ thiết bị sang bộ nhớ chính
Bài toán nhân ma trận được đánh giá trên hệ thống CPU Intel Core 2 Duo P8600@ 2.4GHz, 4GB Ram và GPU Geforce 9600M GS, 32 cores, cores speed 430MHz, Memory, 1024M. Kết quả so sánh với việc thực hiện CPU được đưa ra trong 2.9
Hình 2.9. Thời gian thực hiện nhân ma trận sử dụng CPU so với sử dụng GPU Nhận xét rằng – khi kích thước ma trận nhỏ tốc độ tính toán của GPU cao hơn CPU. Điều này hoàn toàn hợp lý do việc tính toán trên GPU cần phải có thời gian phụ cho việc copy dữ liệu từ Host memory sang Deivce memory,
57 và thời gian khởi tạo, đồng bộ các tiến trình. Khoảng thời gian không dành cho tính toán là nhỏ khi kích thước của ma trận tăng lên. Do vậy với N lớn hiệu năng tính toán song song được thể hiện quá rõ rệt (nhanh hơn hàng trăm lần so với CPU, các kết quả thực hiện trên GPU đều mất <1s với kích thước N<=2048).
2.3.3 Bài toán chuyển đổi Fourier
Chuyển đổi Fourier rời rạc (DFTL Discrete Fourier transform) là một biến đổi trong giải tích Fourier cho các tín hiệu thời gian rời rạc. Đầu vào của biến đổi này là một chuỗi hữu hạn các số thực hoặc số phức. Biến đổi Fourier này được sử dụng rộng rãi trong xử lý tín hiệu. Biến đổi này có thể được tính nhanh bởi thuật toán biển đổi Fourier nhanh (FFT: Fast Fourier transform)
Chuyển đổi Fourier của dãy N số x0,x1,…,xN-1 thành dãy N số
XO,X1,…,XN-1 theo công thức (i là sốảo, e là cơ số của logrit tự nhiên) Xk = ∑− = Π − 1 0 2 N n kn N i ne x Đặt WN = N i e Π −2
, giải thuật chuyển đổi Fourier rời rạc được tính như
sau Begin For k=0 to N-1 do Begin X[k]=0; For n=0 to N-1 do Begin X[k] = X[k] + x[n]* kn N W ; End end end
58
Để thực hiện song song với N Threads trong CUDA, chương trình cần cài đặt thêm các hàm xử lý số phức. Bởi vì đây là hàm tính toán trên mảng một chiều nên số threads là số luồng lớn nhất của block (XTHREADS) và số
block sẽ bằng [N/XTHREADS]. Kết quả đạt được, khi so sánh với bài toán
được giải quyết sử dụng CPU thông thường được đưa ra trong hình 2.10.
Hình 2.10: Thời gian thực hiện chuyển đổi Fourier sử dụng GPU so với CPU Cũng giống như các ví dụ trước, với các trường hợp dữ liệu nhỏ, khi thời gian khởi tạo, chuyển đổi dữ liệu qua lại giữa bộ nhớ của máy tính (host
memory) và của thiết bị (device memory) chiếm tỷ trọng lớn thời gian tính toán trên GPU sẽ lâu hơn. Sử dụng GPU sẽ mang lại hiệu quả khi kích thước của dữ liệu cần tính toán lớn.
2.3.4 Bài toán phân cụm K-Means
Thuật toán K-means clustering do MacQueen giới thiệu trong tài liệu “J. Some Methods for Classification and Analysis of Multivariate Observations” năm 1967. K-means Clustering là một thuật toán dùng trong
59 các bài toán phân loại/nhóm n đối tượng thành k nhóm dựa trên đặc tính/thuộc tính của đối tượng (k ≤n nguyên, dương).
Về nguyên lý, có n đối tượng, mỗi đối tượng có m thuộc tính, ta phân chia được các đối tượng thành k nhóm dựa trên các thuộc tính của đối tượng bằng việc áp dụng thuật toán này.
Coi mỗi thuộc tính của đối tượng (đối tượng có m thuộc tính) như một toạ độ của không gian m chiều và biểu diễn đối tượng như một điểm của không gian m chiều.
ai =( xi1, xi2, ... xim)
ai (i=1..n) - đối tượng thứ i
xij (i=1..n, j=1..m) - thuộc tính thứ j của đối tượng i
Phương thức phân loại/nhóm dữ liệu thực hiện dựa trên khoảng cách Euclidean nhỏ nhất giữa đối tượng đến phần tử trung tâm của các nhóm. Phần tử trung tâm của nhóm được xác định bằng giá trị trung bình các phần tử
trong nhóm.
Khoảng cách Euclidean.
ai=(xi1, xi2,... xim) i=1..n - đối tượng thứ i cần phân phân loại cj=(xj1, xj2,... xjm) j=1..k - phần tử trung tâm nhóm j
Khoảng cách Euclidean từđối tượng ai đến phần tử trung tâm nhóm j cj được tính toán dựa trên công thức:
dji - khoảng cách Euclidean từ ai đến cj xis - thuộc tính thứ s của đối tượng ai
60 Phần tử trung tâm.
k phần tử trung tâm (k nhóm) ban đầu được chọn ngẫu nhiên, sau mỗi lần nhóm các đối tượng vào các nhóm, phần tử trung tâm được tính toán lại.
clusteri = {a1, a2 .... at} – Nhóm thứ i i=1..k, k số cluster
j= 1..m, m số thuộc tính
t - số phần tử hiện có của nhóm thứ i xsj - thuộc tính thứ j của phần tử s, s=1..t
cij - toạđộ thứ j của phần tử trung tâm nhóm i;
Việc phân bổ đối tượng vào các nhóm (bằng việc tính khoảng cách tới k phần tử trung tâm và chọn phần tử trung tâm gần nó nhất) và tính toán lại k phần tử trung tâm là những nhiệm vụ chiếm nhiều thời gian nhất của thuật toán k-means. Ta có thể sử dụng các luồng CUDA của GPGPU để thực hiện các công việc tính toán này.
Thuật toán song song k-means [29] thực thi trên CUDA C gồm các bước sau:
1) Khởi tạo k vị trí trung tâm trên bộ nhớ host (CPU). 2) Copy dữ liệu k vị trí này vào bộ nhớ thiết bị (GPU).
3) Tính toán khoảng cách giữa các điểm với k vị trí trọng tâm (GPU).
4) Sắp xếp lại các phần tử vào các nhóm (khoảng cách ngắn nhất tới một vị trí trung tâm) (thực hiện trên CPU).
5) Copy dữ liệu phân nhóm các đối tượng này vào bộ nhớ thiết bị
(GPGPU).
61 7) Sao chép k vị trí trung tâm mới này tới bộ nhớ host (CPU) và kiểm tra điều kiện hội tụ. Nếu đúng thì dừng thuật toán, nếu sai thì chuyển tới bước 3.
Thuật toán được cài đặt trên GPU GeForce 9600M GS và CPU Intel Core 2 Duo 2.4GHz, Ram 4GB và được thử nghiệm trên bộ test gồm 17695
đối tượng với các giá trị k tăng dần từ 2 tới 128. Kết quả thử nghiệm được chỉ
ra ở hình 2.11 dưới đây.
Hình 2.11. So sánh thời gian tính toán k-means giữa GPGPU và CPU. Rõ ràng khi k càng lớn thì hiệu năng của GPU càng tăng hơn gấp nhiều lần so với hiệu năng của CPU.
62
CHƯƠNG 3
MÔ PHỎNG SONG SONG SỬ DỤNG KHỐI ĐỒ HỌA GPGPU
3.1. Mô phỏng N-Body
3.1.1 Giới thiệu bài toán
Mô phỏng n-body [12] là mô phỏng số lượng rất lớn các hạt dưới ảnh hưởng của các lực vật lý, thường là lực hấp dẫn. Mô phỏng này thường được sử dụng trong vũ trụ học để nghiên cứu các quá trình dữ liệu cấu trúc phi tuyến tính như cơ cấu hình thành các dải thiên hà và các ngôi sao từ hố đen trong thiên văn học. Mô phỏng n-body trực tiếp được dùng trong nghiên cứu vụ nổ của các cụm sao.
Trong mô phỏng thiên văn học N-body, tương tác quan trọng nhất là lực hấp dẫn. Sử dụng máy tính, chúng ta tính được lực hấp dẫn của phần tử
thứ i từ j phần tử khác. Thiên văn học không phải là ứng dụng duy nhất của mô phỏng N-body. Mô phỏng chuyển động phân tử (MD - Molecular dynamics) và phương pháp khoanh vùng thành phần (BEM - Boundary element method) là những ví dụ của phương pháp số học trong đó từng thành phần trong hệ thống nằm trong những tương tác cơ bản với các thành phần còn lại của hệ thống. Trong cả hai trường hợp, cách tiếp cận giống với thuật toán cây Barnes-Hut hoặc FMM [15] giúp giảm được độ chi phí tính toán, tuy nhiên tính toán sự tương tác vẫn ảnh hưởng lớn tới tổng chi phí tính toán.
63 Hình 3.1. Hình ảnh mô phỏng N-body [12].
Theo định luật vạn vật hấp dẫn của Newton, hệ thống N thực thể, mỗi thực thể i có khối lượng mi, vận tốc vi và vị trí xi (xi là một vector trong không gian 3 chiều) thì lực hấp dẫn Fij giữa 2 thực thể i và j được tính theo công thức:
(3.1)
Trong phương trình 3.1, Gọi G là hằng số hấp dẫn, rij = xi - xj và thành phần
r rij
là một vector đơn vị có chiều hướng từ thực thể i tới thực thể j. Tổng lực lên mỗi thực thểđược tính toán từ tổng các lực của tất cả thực thể khác tác
động lên nó. Vậy với thực thể i, tổng lực Fi là
(3.2)
Từ phương trình 3.2 có thể tính được gia tốc ai = Fi/mi và từđó tính toán lại tốc độ, vị trí mới của thực thể i trong hệ thống.
Phương pháp thực hiện này là dùng kỹ thuật ghép cặp toàn bộ (all-
64 nhưng thường không được sử dụng trong thực tế để mô phỏng các hệ thống có số lượng thực thể lớn do nó có độ phức tạp tính toán O(N2). Một số thuật toán khác đã được đề nghị thể để cải thiện tốc độ tính toán. Chúng dựa chủ
yếu vào kỹ thuật ghép cặp toàn bộ kết hợp với sự xấp xỉ hóa các lực tương tác
ở xa. Ví dụ thuật toán được đề nghị trong [15] có độ phức tạp giảm xuống còn
O(N Log (N)). Tuy nhiên, với mục đích so sánh hiệu năng của mô phỏng giữa CPU và GPU nên tác giả không đề cập đến các thuật toán cải tiến như vậy trong luận văn này.
3.1.2 Cài đặt và thử nghiệm
Để thực hiện đánh giá, tác giả cài đặt thuật toán theo phương pháp được giới thiệu trong tài liệu Fast N – Body Simulation with CUDA [12], trong đó các tính toán sử dụng bộ nhớ dùng chung GPU để tăng cường hiệu năng mô phỏng. Các luồng được gắn với một block. Mỗi luồng sẽ tính toán lực trên các thực thể của nó bởi thực hiện thuật toán
• Nạp dữ liệu của các thực thể vào bộ nhó phân chia
• Đồng bộ hóa với các luồng khác trong block
• Tính toán các lực của mỗi thực thể nằm trong bộ nhớ phân chia lên thực thể của luồng
• Đồng bộ hóa với các luồng khác trong block
• Lặp lại các bước trên cho tất cả các thực thểđã xử lý
• Từ các lực tổng hợp tính toán được, tính toán lại vị trí và tốc độ • Ghi kết quả ra bộ nhớ ngoài
Để thực hiện đánh giá tác giả đã cài đặt 2 phiên bản. Một phiên bản thực hiện song song với OpenMP trên máy tính thông thường, và một phiên bản thực hiện song song trên GPU của NVIDIA. Máy tính thực hiện có trang bị 4GB bộ nhớ và tốc độ 2 x 2.4GHz. GPU so sánh là GeForce 9600M GS 32
65 cores, cores speed 430MHz, memory 1024MB, kết quả thử nghiệm được đưa ra trong hình 3.2.
Hình 3.2. So sánh hiệu năng mô phỏng N-Body giữa GPU và CPU. Nhận xét rằng, khi số thực thể tăng dần thì hiệu năng của GPU cũng tăng lên nhiều so với CPU. Cụ thể, với số thực thể N=25000 thì hiệu năng của GPU gấp 78 lần CPU. Khi N=50000 thì hiệu năng GPU gấp 225 lần CPU và gấp 424 lần CPU khi N=100000. Các kết quả đưa ra trong thử nghiệm này là hoàn toàn phù hợp với những công bố trong tài liệu [12].
3.2. Mô phỏng chất lưu
3.2.1 Giới thiệu bài toán
Chất lưu có ở mọi nơi: nước chảy giữa các bờ sông, khói cuộn lên từ điếu thuốc đang cháy, hơi bốc lên từấm pha trà, hơi nước trong các đám mây hay sơn được pha trộn trong các thùng. Về cơ bản chúng đều là dòng chất lưu. Việc mô phỏng chất lưu có ý nghĩa to lớn trong việc mô phỏng các hiện tượng tự nhiên.
66
Để mô phỏng hoạt động của chất lưu, chúng ta cần phải có một biểu diễn toán học của trạng thái chất lưu ở thời gian bất kỳ cho trước. Đại lượng quan trọng nhất để biểu diễn chất lưu đó là vận tốc của chất lưu, bởi lẽ vận tốc sẽ xác
định cách di chuyển của chất lưu và những thứ khác trong nó. Vận tốc của chất lưu biến thiên theo cả thời gian và không gian, vì thế chúng ta sẽ biểu diễn nó