Lập trình song song giải thuật dijkstra Áp dụng tính toán song song vào giải quyết bài toán tìm đi ngắn nhất xuất phát từ một đỉnh sử dụng giải thuật Dijkstra. I Tổng quan về mô hình lập trình song song OpenMP 1 Giới thiệu về mô hình OpenMP 2 Mô hình lập trình song song OpenMP 3 Một số chỉ thị trong OpenMP 4 Một số mệnh đề thường gặp trong OpenMP 5 Thư viện và các biến môi trường II Bài toán tìm đường đi ngắn nhất 1 Các khái niệm mở đầu 2 Bài toán đường đi ngắn nhất xuất phát từ một đỉnh III Giải thuật Dijkstra 1 Thuật toán Dijkstra 2 Tính đúng đắn của thuật toán Dijkstra 3 Độ phức tạp của thuật toán Dijkstra IV Kết quả thực nghiệm 1 Cài đặt bằng lập trình tuần tự 2 Cài đặt bằng lập trình song song 3 Kết luận.
Trang 1TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG
Bài tập lớn Lập trình song song
Đề tài : Áp dụng tính toán song song vào giải quyết bài toán tìm đi ngắn nhất
xuất phát từ một đỉnh sử dụng giải thuật Dijkstra.
Giảng viên hướng dẫn : Ths Nguyễn Tiến Dũng
Sinh viên thực hiện :
1- Nguyễn Thị Thúy SHSV:20082599
2- Nguyễn Đình Hưởng SHSV:20081338
Lớp : Hệ Thống Thông Tin K53
Hà Nội tháng 11/2011
Trang 2Mục lục
Theo Wikipedia:
Trang 3OpenMP- Open Multi-Processing là một giao diện lập trình ứng dụng API (Application programming interface) hỗ trợ đa nền tảng dựa trên cấu trúc chia sẻ bộ nhớ chung, đa ngôn ngữ lập trình C, C++, Fortran và hầu hết các bộ kiến trúc vi xử lý và hệ điều hành Linux, Unix, Mac OS X, nền tảng Microsoft Windows
Nó bao gồm :
OpenMP Architecture Review Board(ARB) được công bố như một giao diện lập trình ứng dụng đầu tiên, phiên bản 1.0 ra đời vào tháng 10 năm 1997 cho Fortran Tháng 10 năm sau đó là phiên bản cho C/C++
Năm 2000 phiên bản 2.0 cho Fortran và năm 2002 là phiên bản 2.0 cho C/C++.
Năm 2005 phiên bản 2.5 cho C/C++/Fortran ra đời.
Tháng 5-2008 phiên bản 3.0 ra đời bao gồm thêm nhiều tính năng mới các khái niệm về task và nhiện vụ của task.
Trang 42-Mô hình lập trình song song OpenMP
OpenMP sử dụng mô hình Fork-Join để thực thi song song
Trong mô hình này tất cả các chương trình song song đều bắt đầu với việc xử lý đơn bởi một luồng chủ (master thread) Luồng chủ này sẽ thực thi một cách tuần tự cho tới khi bắt gặp vùng song song (parallel region) đầu tiên
FORK: Có nghĩa là luồng chủ sau đó sẽ tạo ra một tập các luồng song song Và sau đó đoạn mã trong vùng song song được thực thi song song bởi tập luồng song song vừa tạo ra
JOIN: Khi mà tập luồng song song đã hoàn thành đoạn mã trong vùng song song chúng sẽ được đồng bộ và kết thúc rồi sau đó công việc lại được thực hiện bởi luồngchủ
Chỉ thị trong OpenMP được cho dưới dạng sau
# pragma omp directive-name [clause ] newline
• # pragma omp: Yêu cầu bắt buộc đối với mọi chỉ thị OpenMP C/C++
• directive-name: Là tên của chỉ thị phải xuất hiện sau #pragma omp và đứng trước bất kì mệnh đề nào
• [clause ]: Các mệnh đề này không bắt buộc trong chỉ thị
Trang 5• newline : Yêu cầu bắt buộc với mỗi chỉ thị nó là tập mã lệnh nằm trong khối cấu trúc được bao bọc bởi chỉ thị
Ví dụ:
#pragma omp parallel default ( shared ) private (beta,pi)
Đó là những đoạn mã nguyên bản trong phạm vi từ đầu đến cuối khối cấu trúc
cho sau mỗi chỉ thị Phạm vi tĩnh của chỉ thị không mở rộng đến các thủ tục và các tệp chứa mã.
Chỉ thị đơn độc là chỉ thị xuất hiện độc lập với chỉ thị khác Nó tồn tại ở ngoài
phạm vi tĩnh của chỉ thị khác Chỉ thị đơn độc mở rộng với các thử tục và các tệp mã nguồn
Phạm vi động của chỉ thị bao gồm phạm vi tĩnh của của chỉ thị và phạm vi của các chỉ thị mồ côi
OpenMP có rất nhiều chỉ thị như: atomic, barrier, critical, flush, for, master, ordered, parallel, section, single, threadprivate.
Các cấu trúc thường gặp :
Trong phạm vi bài tập lớn này em xin trình bày các chỉ thị đã dùng:
3.3.1 Chỉ thị do/for
• Mệnh đề schedule
#pragma omp for schedule(static,t)
T= so dinh cua do thi / so luong.
For(i=1;i<n;i++)
{
Printf(“\n nhap du lieu o luong ”)
C[i][j]= ran();
}
#pragma omp for schedule(dynamic,t)
T là biến chunk_size
• Mệnh đề Ordered
• Mệnh đề Nowait
Với mệnh đề này thì các luồng không cần đồng bộ tại điểm cuối cùng của vòng lặp song song Các luồng sẽ xử lý trực tiếp đoạn mã lệnh cho tiếp sau vòng lặp
3.3.2 Chỉ thị sections
• Chỉ thị này dùng để chỉ ra các phần mã trong vùng song song chia cho các luồng thực hiện trong phạm vi của chỉ thị sections có các chỉ chị section mỗi một section sẽ được thực hiện bởi các luồng khác nhau Khuôn dạng của chỉ thị sections.
3.3.3 Chỉ thị Single
Chỉ thị single chỉ ra rằng đoạn mã bao quanh chỉ thị chỉ được thực hiện bởi một luồng trong tập các luồng.Nếu không có mệnh đề Nowait được khai báo thì các luồng khác (các luồng không thực thi đoạn mã Single ) sẽ phải đợi cho đến khi
Trang 6luồng thực thi đoạn mã trong chỉ thị kết thúc mới được thực hiện các công việc khác ngoài chỉ thị
Trong chỉ thị Single chỉ có hai mệnh đề là Private và firstprivate
{
}
Chỉ thị Single cho phép bạn chỉ định một phần của đoạn mã được thực hiện trên một luồng duy nhất , không nhất thiết là luồng master
Các clause có thể là một trong số sau :
• Private
• Firstprivate
• Copyprivate
• Nowait Các luồng khác mà không thực thi đoạn mã trong chỉ thị SINGLE sẽ phải đợi
đến khi luồng thực thi đoạn mã trong chỉ thị kết thúc mới được thực hiện các công việc ngoài chỉ thị SINGLE nếu không có mệnh đề NOWAIT được đưa ra Lưu ý trong chỉ thị SINGLE chỉ có hai mệnh đề là private và firstprivate
.
3.4 Cấu trúc đồng bộ dữ liệu
3.4.1 Chỉ thị master
Trong chỉ thị master đoạn mã bao quanh chỉ thị chỉ được thực hiện bởi luồng chủ trong tập các luồng
Trong chỉ thị này không có bất cứ mệnh đề nào và các luồng khác không cần chờ đến khi luồng chủ thực hiện xong công việc cho bởi chỉ thị master mới được thực hiện công việc của mình.
3.4.2 Chỉ thị critical
với chỉ thị Critical thì vùng mã được cho bởi chỉ thị tại một thời điểm chỉ được thực hiện tại một luồng
Nếu một luồng nào đó đang thực hiện công việc cho bởi chỉ thị mà có một luồng khác cố gắng đòi thực hiện công việc đó thì nó sẽ bị khóa cho đếnkhi luồng kia thực hiện xong công việc đó.
có thể tồn tại nhiều chỉ thị critical với các tên khác nhau trong cùng một vùng song song
3.4.3 Chỉ thị Barrier
Chỉ thị này dùng để đồng bộ tất cả các luông trong tập các luồng khi bắt gặp chỉ thị Barrier thì mỗi luồng sẽ chờ tại thời điểm đó(thời điểm bắt gặp chỉ thị Barrier) cho đến khi tất cả các luồng còn lại bắt gặp chỉ thị Barrier Sau đó các luồng sẽ cùng thực thi chỉ thị barrier.
Khuôn dạng chỉ thị Barrier :
#pragma omp barrier new-line
Trang 73.4.4 Chỉ thị Atomic
Trong chỉ thị Atomic các địa chỉ vùng nhớ được cập nhập một cách nguyên tố hơn
là việc dùng nhiều luồng cố gắng ghi lên nó
#pragma omp atomic newline
statemens_expression
Chỉ thị này chỉ áp dụng trực tiếp một trong các lệnh sau
x binop = expr
x++
++x
x- -
- - x
x là biến mở rộng
expr là một biểu thức mở rộng không tham chiếu đến x
binop là một trong +,*,- , / , & , ^ , | , ≥ or ≤
Chú ý rằng chỉ có phép nạp và lưu trữ biến x mới là nguyên tố
Trong OpenMP sử dụng các mện đề sau
- Mệnh đề Private
- Mệnh đề Firstprivate
- Mệnh đề Lastprivate
- Mệnh đề Shared
- Mệnh đề Default
- Mênh đề Reduction
- Mệnh đề Copyin
5.1 Thư viện Runtime
OpenMP cung cấp một thư viện với rất nhiều các hàm chức năng bao gồm các truy vấn liên quan đến số lượng và chỉ số các luồng, thiết lập số lượng các luồng sử dụng, semaphores, và các hàm thiết lập môi trường thực thi Trong C/C++ để có thể sử dụng các hàm trên thì phải đính vào file thư viện omp.h
5.2 Biến môi trường
Các biến môi trường được dùng để điều khiển sự thực hiện đoạn mã song
song Bao gồm các biến môi trường sau:
Trang 8• OMP_SCHEDULE
Trang 9
Một cạnh e = (x, y) được coi là có hướng từ x tới y; x được gọi là điểm đầu/gốc và
y được gọi là điểm cuối/ngọn của cạnh.
của nó và c(x,y)= ∞ nếu e=(x,y) không thuộc E.
như sau :
p
∑ c(v i-1 , v i )
I=1
Tức là độ dài của đường đi chính là tổng các trọng số trên các cạnh đó.
Bài toán tìm đường đi ngắn nhất là bài toán quan trọng trong Lý thuyết đồ thị, nó được
áp dụng để giải quyết rất nhiều bài toán trong thực tế như điều khiển tối ưu, giao thông vận tải, mạng viễn thông
Bài toán này có thể chia làm 2 loại:
Tìm đường đi ngắn nhất giữa một cặp đỉnh: Cho đồ thị G(V,E) có trọng số cạnh và
hai đỉnh u, v thuộc V tìm đường đi ngắn nhất từ đỉnh u đến đỉnh v trên đồ thị G Các giải
thuật được phát triển để giải bài toán dạng này tiêu biểu là các giải thuật: Dijkstra, Bellman-Ford,
Tìm đường đi ngắn nhất giữa tất cả các cặp đỉnh: Cho đồ thị G(V,E) có trọng số
cạnh tìm đường đi từ đỉnh u đến đỉnh v, với mọi cặp đỉnh u, v thuộc V Các giải thuật đã
được phát triển để giải bài toán này là: Floyd-Warshall, Johnson,
Trong thực tế nhiều khi ta không chỉ cần tìm đường đi ngắn nhất giữa hai đỉnh mà còn cần xác định đường đi ngắn nhất giữa một tập đỉnh này đến một tập đỉnh khác Bài toán
đó được phát biểu như sau: Cho đồ thị G(V,E) có trọng số cạnh và hai tập đỉnh A,B ⊂ V tìm đường đi ngắn nhất từ tập đỉnh A đến tập đỉnh B.
Các thuật toán quan trọng nhất giải quyết bài toán này là:
• Thuật toán Dijkstra — giải bài toán nguồn đơn nếu tất cả các trọng số đều không âm Thuật toán này có thể tính toán tất cả các đường đi ngắn nhất từ một đỉnh xuất phát cho trước s tới mọi đỉnh khác mà không làm tăng thời gian chạy
• Thuật toán Bellman-Ford — giải bài toán nguồn đơn trong trường hợp trọng số có thể
có giá trị âm
• Giải thuật tìm kiếm A* giải bài toán nguồn đơn sử dụng heuristics để tăng tốc độ tìm kiếm
• Thuật toán Floyd-Warshall — giải bài toán đường đi ngắn nhất cho mọi cặp đỉnh
• Thuật toán Johnson — giải bài toán đường đi ngắn nhất cho mọi cặp đỉnh, có thể nhanh hơn thuật toán Floyd-Warshall trên các đồ thị thưa
Trang 10• Lý thuyết nhiễu (Perturbation theory); tìm đường đi ngắn nhất địa phương (trong trường hợp xấu nhất)
Ứng dụng thực tế của bài toán tìm đường đi ngắn nhất:
Trong phạm vi bài tập lớn này em chỉ tìm hiểu về đường đi ngắn nhất giữa một cặp đỉnh nào đó.
Trong trường hợp trọng số trên các cạnh là không âm thuật toán do Dijkstra đề xuất để giải bài toán tìm đường đi ngắn nhất từ đỉnh s đến các đỉnh còn lại của đồ thị làm việc hữu hiệu hơn rất nhiều so với các thuật toán đã trình bày ở mục trước Thuật toán dựa trên cơ sở gán cho các đỉnh các nhãn tạm thời Nhãn của mỗi đỉnh cho biết cận trên của độ dài đường đi ngắn nhất từ s đến nó Các nhãn này biến đổi theo một thủ tục lặp, mà ở đó mỗi bước lặp cơ một nhãn tạm thời trở thành nhãn
cố định Nếu nhãn của một đỉnh nào đó trở thành cố định thì nõ sẽ cho ta không phải là cận trên của độ dài mà là độ dài của đường đi ngắn nhất từ đỉnh s đến nó Thuật toán được mô tả cụ thể như sau :
Procedure Dijkstra
(*
Đầu vào : Đồ thị có hướng G=(V,E) với n đỉnh
S thuộc V là đỉnh xuất phát , c[u][v] , u,v thuộc V ma trận trọng số
Giả thiết: a[u,v]>=0 u,v thuộc V
Đầu ra : Khoảng cách từ s đến các đỉnh còn lại d[v], v thuộc V
Truoc[v] ghi nhận đỉnh đi trước v trong đường đi ngắn nhất từ s đến v
*)
Begin
(* khởi tạo *)
For v
Begin
D[v]= c[s,v];
Truoc[v]=s;
End
D[s]=0 ;
T= V\{s} (* T là tập các đỉnh có nhãn tạm thời *)
(* bước lặp *)
While (T do
Begin
Tìm đỉnh u T thỏa mãn d[u]= min{ d[z]: z T};
T= T\{u}; (* cố định nhãn u *)
For v do (* Gán lại nhãn cho các đỉnh trong T*)
If( d[v]>d[u]+a[u,v] )
Begin
d[v]=d[u]+a[u,v];
truoc[v]=u;
end;
Trang 11end;
end;
Ví dụ :
Ma trận trọng số
1 2 3 4 5 6
1 0 1 ∞ ∞ ∞ ∞
2 ∞ 0 5 2 ∞ 7
A= 3 ∞ ∞ 0 ∞ ∞ 1
4 2 ∞ 1 0 4 ∞
5 ∞ ∞ ∞ 3 0 ∞
6 ∞ ∞ ∞ ∞ 1 ∞
Bảng kết quả tính toán theo thuật toán Dijkstra :
Quy ước viết hai thành phần của nhãn theo thứ tự là d[v] và truoc[v]
Trước hết ta đi chứng minh là thuật toán tìm được đường đi ngắn nhất từ đỉnh s đến các đỉnh còn lại của đồ thị.
Giả sử rằng ở một bước lặp nào đó các nhãn cố định cho ta độ dài các đường đi ngắn
Thật vậy ta có
bước lặp đang xét Kết thúc mỗi bước lặp nhãn tạm thời d[v] cho ta độ dài đường đi từ s
Trang 12S 2 là đỉnh đầu tiên như vậy trên đường đi này Do trọng số trên các cung là không âm,
d[z]< d[u * ] –L<d[u * ].
với bước lặp đầu tiên Theo quy nạp toán học suy ra thuật toán cho ta đường đi ngắn nhất từ đỉnh s đến mọi đỉnh của đồ thị.
Định lý : Thuật toán Dijkstra tìm được đường đi ngắn nhất trên đồ thị sau thời gian cỡ O(n 2 )
Bây giờ ta sẽ đánh giá số phép toán cần thực hiện theo thuật toán Ở mỗi bước lặp để tìm ra đỉnh u cần phải thực hiện O(n) phép toán và để gán nhãn lại cũng cần phải thực hiện một số lượng phép toán cũng là O(n) Thuật toán phải thực hiện n-1 bước lặp vậy
Nếu ta tăng dần dữ liệu đầu vào (tăng kích thước ma trận trọng số) Thì thời gian tính toán của thuật toán Dijkstra cài đặt bằng lập trình tuần tự sẽ tăng dần theo đồ thị sau :
Qua kết quả thực nghiệm cho thấy
Thời gian tính toán của lập trình song song là nhanh hơn so với lập trình tuần
tự Khi kích thước bộ dữ liệu đầu vào càng lớn thì thời gian tính toán của lập trình song song càng nhanh hơn so với lập trình tuần tự
Thời gian tính toán của lập trình song song sẽ tăng nếu ta tăng số luồng thực hiện, tăng số bộ sử lý
Định luật Amdahl: Gọi f là phần nhỏ của thao tác tính toán trong quá trình tính toán phải thực hiện một cách tuần tự, 0 ≤ f ≤ 1 Tốc độ tối đa S có thể đạt được bằng cách sử dụng máy tính song song với p BXL được cho bởi công thức
S ≤
Thời gian cho phần việc xử lý song song của ứng dụng sẽ dảm dần đến 0 khi
ta tăng số lượng BXL Thời gian cho việc xử lý tuần tự luôn là hằng số.
Lập trình song song cũng như tính toán hiệu năng cao đang là xu hướng hiện nay trên thế giới Xu hướng này có thể dễ dàng nhận thấy qua sự tăng vọt về số lượng
bộ xử lý vật lý tích hợp trên một chip trong những năm gần đây Thêm vào đó rất
Trang 13nhiều trợ những hệ thống giúp tính toán song song đã được phát triển như Window HPC Server, Rock Cluster… tạo đà cho sự phát triển của lập trình song song.
Tài liệu tham khảo