Đề tài Nghiên cứu tính toán lưới và thực nghiệm trên một số thuật toán lý thuyết đồ thị

Lời nói đầu Nhân lọai ngày nay đang chứng kiến sự phát triển mạnh mẽ của ngành Công nghệ Thông tin, một trong những nghành mũi nhọn của nhiều quốc gia trên thế giới. Sự phát triển vượt bậc của nó là kết quả tất yếu của sự phát triển kèm theo các thiết bị phần cứng cũng như phần mềm tiện ích. Sự phát triển đó đã kéo theo rất nhiều nghành khác phát triền theo, trong đó có lĩnh vực nghiên cứu khoa học. Tuy công nghệ ngày càng phát triển, tốc độ xử lý của các thiết bị cũng không ngừng tăng cao, nhưng nhu cầu tính toán của con người vẫn còn là rất lớn. Hiện nay vẫn còn rất nhiều vấn đề mà các nhà khoa học cùng với khả năng tính toán của các máy tính hiện nay vẫn chưa giải quyết được hay giải quyết được nhưng với thời gian rất lớn. Các vấn đề đó có thể có thể là : ã Mô hình hóa và giả lập ã Xử lý thao tác trên các dữ liệu rất lớn ã Các vấn đề “grand challenge” (là các vấn đề không thể giải quyết trong thời gian hợp lý) Lời giải cho những vấn đề này đã dẫn đến sự ra đời của các thế hệ siêu máy tính. Tuy nhiên việc đầu tư phát triển cho các thiết bị này gần như là điều quá khó khăn đối với nhiều người, tổ chức, trường học . Chính vì lẽ đó mà ngày nay người ta đang tập trung nghiên cứu cách cách sử dụng các tài nguyên phân bố một cách hợp lý để tận dụng được khả năng tính toán của các máy tính đơn. Những giải pháp này được biết đến với nhiều tên gọi khác nhau như meta-computing, salable-computing, global- computing, internet computing và gần nhất hiện nay là peer to peer computing hay Grid computing. Đây là phương pháp nhằm tận dụng khả năng của các máy tính trên toàn mạng thành một máy tính “ảo” duy nhất, nhằm hợp nhất tài nguyên tính toán ở nhiều nơi trên thế giới để tạo ra một khả năng tính toán khổng lồ, góp phần giải quyết các vấn đề khó khăn trong khoa học và công nghệ. Ngày nay nó đang càng được sự hỗ trợ mạnh hơn của các thiết bị phần cứng, băng thông Grid Computing có khả năng chia sẻ, chọn lựa, và thu gom một số lượng lớn những tài nguyên khác nhau bao gồm những siêu máy tính, các hệ thống lưu trữ, cùng với những nguồn dữ liệu, các thiết bị đặt biệt Những tài nguyên này được phân bố ở các vùng địa lý khác nhau và thuộc về các tổ chức khác nhau.

pdf153 trang | Chia sẻ: lvcdongnoi | Lượt xem: 2391 | Lượt tải: 1download
Bạn đang xem trước 20 trang tài liệu Đề tài Nghiên cứu tính toán lưới và thực nghiệm trên một số thuật toán lý thuyết đồ thị, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
hêm các tham số trong thủ tục như sau nhằm để quản lý các tiến trình gửi ht t p : / / e t r i t h u c . v n Trang 104 Request object : Kiểu dữ liệu MPI_Request Ý nghĩa : Nonblocking communication sử dụng đối tượng request để yêu cầu đối tượng nhận diện những họat động truyền thông và liên kết với những thao tác xem xét sự hoàn thành của tác vụ. Đối tượng request được định rõ vị trí trong bộ nhớ hệ thống MPI. Người dùng không biết đến cấu trúc của những đối tượng này. Chương trình ứng dụng chỉ vận dụng thẻ điều khiển (handle) đối tượng. Hệ thống có thể dùng đối tượng request này để xác định một số thuộc tính khác của những thao tác truyền thông, chẳng hạn như bộ đệm truyền thông liên quan đến nó, hay để lưu trữ thông tin về những về trạng thái chưa được giải quyết của các họat động truyền thông (gửi ,nhận). 4.3.3.1. Non-Blocking Send Tương tự các Blocking Send hay Receive của blocking communication, nhưng tên hàm còn có thêm chữ “I” phía trước. MPI_Isend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) MPI_Issend (void *buf,int count,MPI_Datatype datatype,int dest,int tag,MPI_Comm comm,MPI_Request *request) Giống như MPI_Isend(), ngoại trừ hàm MPI_Wait() hoặc MPI_Test chỉ ra rằng khi nào thì xử lý đích đã nhận được thông điệp. MPI_Ibsend (void *buf,int count,MPI_Datatype datatype,int dest,int tag,MPI_Comm comm,MPI_Request *request) Giống như MPI_Bsend() ngoại trừ MPI_Wait() hay MPI_Test chỉ ra rằng xử lý đích đã nhận được thông điệp hay chưa. Hàm này phải được sử dụng với hàm MPI_Buffer_attach. MPI_Irsend(void *buf,int count,MPI_Datatype datatype,int dest, int tag,MPI_Comm comm,MPI_Request *request) ht t p : / / e t r i t h u c . v n Trang 105 Giống như MPI_Rsend() ngoại trừ MPI_Wait() hay MPI_Test() chỉ ra rằng khi nào thì xử lý chính nhận thông điệp. Chỉ nên sử dụng nếu lập trình viên chắc chắn rằng có một trình nhận thông điệp thích hợp ở xử lý đích đã được đưa lên. 4.3.3.2. Non-Blocking Receive MPI_Irecv(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request) thêm vào đối tượng có kiểu dữ liệu MPI_Request dùng để trả về thẻ điều khiển. Và thẻ này dùng trong các trường hợp chờ trạng thái hoàn thành của các Posting Operations, quản lý tiến trình nhận và gửi thông điệp. Lưu ý rằng bộ đệm gửi và bộ đệm nhận sẽ không được truy xuất khi các Posting Operations đã được thực hiện cho đến khi nó hoàn thành.Nếu truy xuất bộ đệm ứng dụng trong khi các thao tác gửi và nhận chưa hòan thành thì sẽ gây ra dữ liệu bị sai. Chằng hạn, khi hệ thống bắt đầu sao chép dữ liệu ra khỏi bộ đệm gửi thì không được truy xuất những phần của bộ đệm này cho đến khi nhận giá trị trả về của hệ thống thực thi gửi thông điệp, tương tự truy xuất cho thao tác nhận. 4.3.3.3. Kiểm tra sự hoàn thành của các tiến trình nhận Bởi vì các hàm trong Posting Operation không được biết trước sự hoàn thành khi được khởi chạy. Nên MPI cung cấp chức năng chờ cho đến khi thao tác Send và Receive đã hoàn thành. Nếu thao tác non blocking Send và Receive có thêm các thao tác chờ sự hoàn thành thì giống như hàm blocking. Có thể hình dung như thế này MPI_Isend()+MPI_Wait() = = MPI_Send() MPI_Irecv()+MPI_Wait() = = MPI_Recv() Phân loại kiểu kiểm sự hoàn thành trong truyền thông Blocking ht t p : / / e t r i t h u c . v n Trang 106 Kiểm tra sự hoàn thành của các tiến trình gửi hoặc nhận và dùng lại các thông tin liên quan đến các đối tượng này, chủ yếu là vùng nhớ ứng dụng dùng để gửi dữ liệu .Các hàm do MPI cung cấp có tên Wait. Non-blocking : Chỉ kiểm tra xem đã hoàn thành 1 tác vụ hay chưa, hàm này trả về ngay tại thời điểm đó. Và nếu tác vụ đã hoàn thành thì trả về thêm các thông tin liên quan đến sự hoàn thành đó ,ngược lại trả về một giá trị không định nghĩa .Các hàm này mang tên Test MPI_Wait(MPI_Request *request,MPI_Status *status) Chờ cho đến khi tiến trình nhận thông điệp nhận được thông điệp hoàn toàn ,thông qua thẻ điều khiển request liên quan đến tiến trình nhận thông điệp. Và biến status sẽ nhận được trạng thái của tiến trình nhận như định danh thông điệp v.v... MPI_Test (MPI_Request *request,int *flag,MPI_Status *status) Gọi hàm MPI_Test, flag trả về bằng true nếu thao tác gửi hoặc nhận được xác định bằng đối tượng request đã hoàn thành. Trong trường hợp này, đối tượng trạng thái status được thiết lập nội dung thông tin trên thao tác đã hoàn thành và đối tượng request sẽ được gán giá trị MPI_REQUEST_NULL. Ngược lại, flag trả về bằng false. Trong trường hợp này, giá trị của đối tượng trạng thái status là không xác định. Cả hàm MPI_Wait và MPI_Test, thông tin trả về thao tác hoàn thành đều dựa trên thông tin đối tượng trạng thái status. Nội dung của đối tượng trạng thái status. Nếu muốn chờ một tập các truyền thông giữa các tiến trình hoàn thành thì dùng các hàm sau MPI_ Waitany(int count,MPI_Request *arrayofrequest,int *index,MPI_Status *status) Khóa một thao tác truyền thông (gửi hoặc nhận), liên kết với một đối tượng request trong mảng các đối tượng Request. Nếu nhiều hơn hay bằng một thao tác ht t p : / / e t r i t h u c . v n Trang 107 đã hoàn thành, thì MPI_Waitany tùy ý lấy một đối tượng request trong mảng đó và hoàn thành nó, MPI_Waitany sẽ trả về chỉ số của phần tử request trong mảng và trả về trạng thái hoàn thành. Đối tượng request đó sẽ được giải phóng và được gán giá trị MPI_REQUEST_NULL MPI_Testany(int count,MPI_Request *array_of_requests,int *index,int *flag,MPI_Status *status) Nếu có một hay nhiều hơn một thao tác đã hoàn thành, nó trả về flag=true, và chỉ số của đối tượng request trong mảng đối tượng Request, và trạng thái của thao tác đó và đối tương trạng thái đó được giải phóng và gán giá trị MPI_REQUEST_NULL. Ngược lại, flag=false, và index=MPI_UNDEFINED và status=MPI_UNDEFINED, các giá trị này do MPI định nghĩa dùng trong trường hợp các giá trị trả về không thích hợp. MPI_ Waitall(int count,MPI_Request *arrayofrequest,MPI_Status *arrayofstatus) Chờ cho đến khi tất cả các thao thác có đối tượng request trong mảng Request hoàn thành. Chỉ số trạng thái thứ i trong mảng các trạng thái trả về trạng thái hoàn thành thứ i của thao tác. Và tất cả các đối tương trong mảng đối tượng Request được giải phóng và được gán giá trị MPI_REQUEST_NULL, MPI_Testall(int count,MPI_Request *array_of_request,int *flag,MPI_Status *array_of_statuses) Nếu tất cả các thao tác đã hoàn thành thì flag=true,và trả về trạng thái status tương ứng trong mảng các trạng thái, giải phóng toàn bộ các đối tượng request và được gán giá trị là MPI_REQUEST_NULL, MPI_ Waitsome(int incount,MPI_Request *arrayofrequest,int *outcount,int *offsets,MPI_Status*arrayofstatuses) Chờ cho đến khi nhiều hơn một truyền thông được hoàn thành ,liên kết với những đối tượng request trong mảng .MPI_Waitsome trả về số lượng đối tượng request đã hoàn thành. ht t p : / / e t r i t h u c . v n Trang 108 MPI_Testsome(int incount,MPI_Request *array_of_request,int *outcount,int *array_of_indices,MPI_Status *array_of_statuses) Cũng như hàm MPI_Waitsome,ngọai trừ nó trả về ngay lập tức. Nếu không có thao tác nào hoàn thành thì outcount=0 MPI_Iprobe(int source,int tag,MPI_Comm comm,int *flag,MPI_Status *status) Là hàm thuộc dạng non-blocking, trả về flag=true nếu có một thông điệp mà có thể được nhận với những thông tin đặc tả về source, tag,comm. Lời gọi cũng tương thích cùng thông điệp mà đã được nhận với hàm MPI_Recv(với những tham số như thế này). 4.3.3.4. Các thủ tục liên quan đến đối tượng request Giải phóng Request(Freeing Requests) Một đối tượng request có thể được giải phóng dựa vào các hàm MPI_Wait hay MPI_Test.Ngòai ra ,nó còn dược giải phóng bởi sử dụng những thao tác khác như: MPI_Request_free(MPI_Request *request) Nhưng các thao tác này không giải phóng được đối tượng request khi có một sự giao tiếp liên quan đến đối tượng này vẫn tồn tại .Vì thế MPI cung cấp thêm một hàm nữa là : MPI_Cancel(MPI_Request *request) Đánh dấu cho việc hủy bỏ thao tác truyền thông nonblocking (gửi hay nhận) đang bị treo. Nó trả về ngay lập tức, có thể trước khi sự thao tác đó bị hủy bỏ. Sau này, nó sẽ tiếp tục hoàn thành sự truyền thông mà đã đánh dấu hủy bỏ. Cũng giống như các hàm MPI_IRecv(..). Hàm này cũng có thể áp dụng các hàm liên quan đến việc chờ đợi như MPI_Wait(..) để biết hàm này đã thực hiện xong hay chưa. Nếu sự truyền thông không bị hủy bỏ (bởi vì sự truyền thông đã xảy ra trước khi hàm này tỏ ra hiệu quả ), ngược lại thì đối tượng request sẽ được giải phóng và trả về trạng thái thông tin đã bị hủy bỏ bằng cách sử dụng ht t p : / / e t r i t h u c . v n Trang 109 hàm MPI_Test_cancelled(MPI_Status *status,int *flag): status lưu thông tin của thao tác cần kiểm tra xem có phải thao tác này đã bị hủy bỏ hay chưa. flag=true nếu xảy ra điều đó, ngược lại flag=false. 4.4. Trao đổi thông tin tập hợp 4.4.1. Đồng bộ hóa MPI_Barrier (MPI_Comm comm) Tạo ra một rào cản đồng bộ trong một nhóm. Mỗi tác vụ, khi tiến tới lời gọi MPI_Barrier, thì khóa lại cho đến khi nào tất cả các tác vụ khác trong nhóm cũng tiến tới cùng một lời gọi MPI_Barrier. Chức năng này giúp tất cả các xử lý trong nhóm đã nhận hết được dữ liệu. 4.4.2. Di dời dữ liệu trong nhóm MPI_Bcast(void *buffer,int count,MPI_Datatype datatype,int root,MPI_Comm comm) Broadcast (gửi) một thông điệp từ xử lý với rank=root tới tất cả các xử lý còn lại trong cùng một communicator (trong cùng một nhóm với nhau) Các xử lý trong một communicator sẽ nhận dữ liệu của tiến trình gửi broadcast Tham số Diễn giải Buffer địa chỉ của bộ đệm ứng dụng Count Số lượng thực thể trong buffer Datatype Dạng dữ liệu của buffer Root Rank của xư lý gọi Broadcast Comm Communicator _phạm vi ảnh hường ht t p : / / e t r i t h u c . v n Trang 110 Hình 4-10 : Broadcast dữ liệu MPI_Scatter(void *sendbuff,int sendcnt,MPI_Datatype sendtype,void *recvbuf,int recvcnt,MPI_Datatype recvtype,int root,MPI_Comm comm) Phân phối và chia nhỏ thông điệp thành n phần khác nhau từ một xử lý nguồn và gửi đến mỗi xử lý còn lại trong communicator. Hàm này giống như gồm n hàm MPI_Send với n là số tiến trình và mỗi tiến trình đều thực hiện hàm MPI_Recv(...) . Tất cả các tham số đều quan trong với tiến trình gửi Broadcast, còn lại các tiến trình khác chỉ quan tâm bộ đệm nhận, communicator, tiến trình gửi, dạng dữ liệu và số dữ liêu được nhận. Cho phép một số lượng dữ liêu được gửi đến mỗi tiến trình, bởi vì sendcount là một mảng và còn có thêm một tham số nữa là mảng displs dùng để quy định các địa chỉ của sendbuf được chia ra như thế nào. ht t p : / / e t r i t h u c . v n Trang 111 Hình 4-11 : Ví dụ hàm Scatter Sự mở rộng của hàm Scatter là: MPI_Scatterv(void *sendbuf,int *sendcounts,int *displs,MPI_Datatype sendtype,void *recvbuf,int recvcount,MPI_Datatype recvtype,int root,MPI_Comm comm) Trái ngược với các hàm MPI_Scatter thì gồm các hàm sau đây MPI_Gather(void *sendbuff,int sendcnt,MPI_Datatype sendtype,void *recvbuf,int recvcount,MPI_Datatype recvtype,int root, MPI_Comm comm) Hàm này có nhiệm vụ lấy lại tất cả các thông điệp từ mỗi tác vụ trong một nhóm vào một tác vụ đích duy nhất. Mỗi xử lý (kế cả xử lý ở root) đều phải gửi thông tin về sendbuf đến tiến trình root,và tiến trình root sẽ nhận thông điệp và lưu chúng trong một danh sách có thứ tự các định danh tiến trình (rank) . ht t p : / / e t r i t h u c . v n Trang 112 Hình 4-12 : Hàm MPI_Gather MPI_Allgather(void *sendbuf,int sendcount,MPI_Datatype sendtype,void * recvbuf,int recvcount,MPI_Datatype recvtype,MPI_Comm comm) Cũng giống như hàm MPI_Gather(...) ngoại trừ là tất cả các tiến trình tham gia đều có giá trị thay vì chỉ một tiến trình root sau khi thực hiện gather. Kết qủa của hàm gọi MPI_Allgather(..) cũng như là tất cả các tiến trình thực thi hàm MPI_Gather và đều nhận được kết quả như nhau Hình 4-13 : Hàm MPI_Allgather MPI_Alltoall(void *sendbuf,int sendcount,MPI_Datatype sendtype,void *recvbuff,int *recvcount,MPI_Datatype recvtype,MPI_Comm comm) ht t p : / / e t r i t h u c . v n Trang 113 là một sự mở rộng của hàm MPI_Allgather. Mỗi xử lý sẽ gửi dữ liệu phân biệt đến mỗi trình nhận.Xử lý thứ i sẽ gửi khối thứ j đến xử lý thứ j và được thay thế trong khối thứ i của bộ nhớ lưu trữ nhận Hình 4-14 : Hàm MPI_Alltoall 4.4.3. Tính toán gộp Các hàm ở dạng này cung cấp thêm tính toán gộp trên các xử lý với nhau (ví dụ như tính tổng ,tính giá trị lớn nhất...) các gía trị thuộc về các thành viên của nhóm xử lý . Cũng giống như các hàm collective operation khác, những hàm này cũng có hai dạng, đó là kết quả tính toán trả về trên một nút hoặc kết quả tính toán trả về trên nhiều nút (nút ở đây xem như là một xử lý hay một máy trên một mạng) MPI_Reduce(void *sendbuf,void *recvbuf,int count,MPI_Datatype datatype,MPI_Op op,int root,MPI_Comm comm) ht t p : / / e t r i t h u c . v n Trang 114 Tính giá trị của tất cả các tiến trình trong communicator gửi đến và sau đó đưa giá trị tính toán đó về một tiến trình có rank=root. Hình 4-15 : Hàm MPI_Reduce ht t p : / / e t r i t h u c . v n Trang 115 4.4.3.1. Một số kiểu tính toán do MPI cung cấp CÁC THỦ TỤC TÍNH TOÁN KIỂU DỮ LIỆU ĐƯỢC SỬ DỤNG MPI_MAX Tính giá trị lớn nhất integer,float MPI_MIN Tính giá trị nhỏ nhất integer,float MPI_SUM Tính tổng integer,float MPI_PROD product integer,float MPI_LAND Toán tử luận lý AND integer MPI_BAND Toán tử dấu bit AND integer,MPI_BYTE MPI_LOR Toán tử luận lý OR integer MPI_BOR Toán tử dấu bit OR integer,MPI_BYTE MPI_LXOR Toán tử luận lý XOR integer MPI_BXOR Toán tử dấu bit XOR integer,MPI_BYTE MPI_MAXLOC Tính giá trị lớn nhất và thêm thông tin rank của tiến trình chứa giá trị lớn nhất integer,double,vaø long double MPI_MINLOC Tính giá trị nhỏ nhất và thêm thông tin rank của tiến trình chứa giá trị nhỏ nhất integer,double,vaø long double 4.4.3.2. Một số kiểu tính toán do người dùng định nghĩa Để viết một hàm với chức năng do người dùng định nghĩa dựa vào thuật toán của hàm MPI_Reduce cung cấp có thể thực hiện theo 3 bước sau : Kiểm tra phương pháp tính toán có phù thuộc vào những yêu cầu sau đây không? ht t p : / / e t r i t h u c . v n Trang 116 Bước 1: Có tính kết hợp Ví dụ a + (b+c)=(a+b)+c Có tính giao hoán Ví dụ a+b=b+a Bước 2 : bổ sung toán tử vào hàm gộp (reduction) theo quy tắc sau Hàm myfunc(void *in,void *inout,int *len,MPI_Datatype *datatype) Bước 3 : Đăng ký hàm này với MPI int commute,myop; commute=1 MPI_Op_create(myfunc,commute,&myop) MPI_Reduce(...,myop,..) Hình 4-16 : Sử dụng 8 xử lý để tính giá trị tuyệt đối Một số hàm tính toán gộp còn lại : MPI_Allreduce(void *sendbuf,void *recvbuf,int count,MPI_Datatype datatype,MPI_Op op,MPI_Comm comm) Cũng giống như hàm MPI_Reduce nhưng tất cả các xử lý trong cùng một communicator sẽ nhận giá trị tính toán được ht t p : / / e t r i t h u c . v n Trang 117 Hình 4-17 Hàm Mpi-Allreduce MPI_Reduce_scatter(void *sendbuf,void *recvbuf,int recvcount,MPI_Datatype datatype,MPI_Op op,MPI_Comm comm) MPI_Reduce_scatter thực hiện hai bước. Bước thứ nhất là dùng tính toán gộp trên mỗi giá trị từ bộ đệm gửi (và dĩ nhiên là mỗi giá trị này sẽ lưu trong một mảng). Sau đó mảng này sẽ được phân chia ra thành n đoạn (với n là số xử lý ). Đoạn thứ i sẽ chứa giá trị thứ i trong mảng đã tính toán. Và đoạn thứ i sẽ được gửi đến xử lý thứ i và lưu trong bộ đệm nhận. Hình 4-18 : Hàm MPI_Reduce_scatter MPI_Scan(void *sendbuf,void *recvbuf,int count,MPI_Datatype datatype,MPI_Op op,MPI_Comm comm) MPI_Scan(...) được sử dụng để thực hiện một dạng tính toán gộp theo kiểu Reduction ở trên nhưng với tiến trình i, kết quả sẽ là thực hiện tính toán gộp ht t p : / / e t r i t h u c . v n Trang 118 trên các tiến trình từ 0 dến i. Hình vẽ sau đây sẽ minh họa rõ hơn về điều này Hình 4-19 : Hàm MPI_Scan 4.5. Các kiểu dữ liệu 4.5.1. Những kiểu dữ liệu đã được định nghĩa Kiểu dữ liệu của MPI Kiểu dữ liệu của C MPI_CHAR signed char MPI_SHORT signed short int MPI_INT signed int MPI_LONG signed long int MPI_UNSIGNED _CHAR unsigned char MPI_UNSIGNED_SHORT unsigned short int MPI_UNSIGNED unsigned int MPI_UNSIGNED_LONG unsigned long int MPI_FLOAT float MPI_DOUBLE double MPI_LONG_DOUBLE long double MPI_BYTE MPI_PACKED Kiểu dữ liêu khác MPI_BYTE khác với MPI_CHAR, với một số máy tính thì MPI_CHAR có thể là một ký tự 8 bit (1 byte), nhưng một số máy khác thì MPI_CHAR có thể là ht t p : / / e t r i t h u c . v n Trang 119 một ký tự 2 byte (16 bit _mã Unicode chẳng hạn) còn giá trị của MPI_BYTE là số 1 byte (8 bit) trong tất cả các trường hợp. Tất cả các kiểu dữ liệu còn lại gần giống với các kiểu dữ liệu của C .Ngoài ra còn có thêm kiểu dữ liệu MPI_Packed ,một dạng dữ liệu nén mà chúng ta sẽ đề cập sau 4.5.2. Các kiểu dữ liệu bổ sung Ngoài những kiểu dữ liêu do MPI cung cấp như MPI_BYTE,MPI_DOUBLE v.v…thì MPI còn cung cấp khả năng cho việc định nghĩa chính cấu trúc dữ liệu dựa trên các dạng dữ liệu chính. Những cấu trúc do người dùng định nghĩa được gọi là dạng dữ liệu bổ sung. MPI cung cấp một số phương pháp cho việc cấu trú những kiểu dữ liệu sau đây : + Contiguous + Vector + Indexed + Struct 4.5.2.1. Contiguous Cấu trúc đơn giản nhất .Sản sinh ra một dạng dữ liệu mới bằng cách tạo một số lượng những bản sao chép của một dạng dữ liệu đã có MPI_Type_contiguous(in count,in oldtype, out *newtype) ht t p : / / e t r i t h u c . v n Trang 120 Hình 4-20 : MPI_Type_contiguous 4.5.2.2. Vector MPI_Type_vector(int count,int blocklenght,int stride, MPI_Datatype oldtype, MPI_Datatype*newtype) Cũng giống như contiguous, nhưng cho phép khoanh vùng dữ liệu muốn đăc tả .MPI_Type_hvector cũng giống như MPI_Type_vector ngoại trừ phạm vi được đặc tả ở dạng byte IN : count : số lượng block blocklenght : số lượng của những thành phần bên trong mỗi block stride : số lượng của những thành phần giữa vị trí bắt đầu của mỗi block oldtype : dạng dữ liệu cũ (handle) OUT : newtype : dạng dữ liệu mới ht t p : / / e t r i t h u c . v n Trang 121 Hình 4-21 : MPI_Type_vetor 4.5.2.3. Indexed Định nghĩa dữ liệu không liên tục ,khoảng cách không cố định và đều đặn. Hàm được sử dụng là MPI_Type_indexed(int count,int []blocklens,int []indices,MPI_Datatype old_type,MPI_Datatype *newtype) Dạng dữ liệu tạo ra theo chỉ số của mảng dữ liệu đầu vào và vùng mảng được đặc tả ht t p : / / e t r i t h u c . v n Trang 122 Hình 4-22 : MPI_Type_indexed 4.5.2.4. Struct Vấn đề đặt ra là MPI không cho truyền dữ liệu bằng kiểu cấu trúc .Do đó ,lập trình viên phải định nghĩa cấu trúc sau đó đặt tả cầu trúc đó theo dạng byte theo sự trợ giúp của các hàm MPI . MPI_Type_struct (int count,int []blocklens,int[]offsets,MPI_Datatype oldtypes,MPI_Datatype *newtype) Hình 4-23 : MPI_Type_struct ht t p : / / e t r i t h u c . v n Trang 123 Các hàm liên quan khác đến dạng dữ liệu bổ sung : MPI_Type_extent(MPI_Datatype,MPI_Aint* extent) Trả về kích thước của dạng dữ liệu được đặc tả.Trả về kích thước theo dạng BYTE. MPI_Type_commit( MPI_Datatype datatype) Đăng ký dạng dữ liệu mới với hệ thống. Đây là hàm yêu cầu cho tất cả các hàm tạo ra kiểu dữ liệu mới trước khi sử dụng 4.5.3. Pack và UnPack Cung cấp những chức năng cho việc truyền nhận dữ liệu không liên tục . Ứng dụng sẽ nén dữ liệu vào một bộ đệm liên tục trước khi gửi di .và giải nén sau khi nhận được . Hai hàm thường sử dụng cho việc nén và giải nén là MPI_Pack(void* inbuf, int incount, MPI_Datatype datatype, void *outbuf, int outsize, int *position, MPI_Comm comm) MPI_Pack nén thông điệp với nội dung thông điệp là inbuf, incount,datatype,comm vào không gian bộ đệm (với thông tin outbuf và outsize). Bộ đệm vào (inbuf) có thể là bất kỳ bộ đệm truyền thông (mà được cho phép trong giao tiếp gửi MPI_SEND) và dữ liệu ra (output buffer) là một vùng lưu trữ liên tục chứa outsize bytes, bắt đầu với địa chỉ outbuf MPI_Unpack(void* inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm) Ngược lại với chức năng của hàm MPI_Pack ,MPI_Unpack giải nén một thông điệp được đặc tả thông tin ( giá trị inbuf,kích thước insize) vào bộ đệm nhận được đặt tả với thông tin (giá trị outbuf,kích thước outsize, dạng dữ liệu datatype) .Bộ đệm đầu ra thuộc bất kỳ dạng dữ liệu nào được truyền thông trong hàm nhận MPI_Recv. Bộ đệm đầu vào (input buffer) là dạng dữ liệu liên tục chứa insize bytes, bắt đầu với địa chỉ của ìnbuf ht t p : / / e t r i t h u c . v n Trang 124 Việc phân biệt ngữ nghĩa giữ kiểu dữ liệu bổ sung và kiểu dữ liệu MPI_PACKED rất khó .Tuỳ theo ngữ cảnh mà người dùng tự chọn cho mình cách biểu thị tốt nhất cho bài toán tối ưu về mặt ngữ nghĩa và truyền thông trong các thuật toán cụ thể 4.6. Quản lý nhóm và communicator 4.6.1. Tổng quan Nhắc lại, Group là một tập hợp có thứ tự các xử lý. Mỗi xử lý trong một nhóm là một sự kết hợp với một rank duy nhất. Gía trị Rank trong phạm vi từ 0 đến N-1, trong đó N là số xử lý trong nhóm. Bên trong MPI, một Nhóm đại diện bên trong bộ nhớ hệ thống như một đối tượng. Người lập trình truy xuất đó như một ‘handle’. Một nhóm luôn luôn liên kết với một đối tượng communicator. Một Communicator đặt trưng cho một nhóm các xử lý cái mà sẽ giao tiếp với các nhóm khác. Tất cả thông điệp phải đặt tả một communicator. Trong trường hợp đơn giản nhất, một communicator là một ‘tag’ mở rộng cái mà bao gồm với những lời gọi MPI. Giống như những nhóm, những communicators được thể hiện bên trong hệ thống bộ nhớ như những đối tượng và những lập trình viên chỉ truy xuất bằng các ‘handle’. Ví dụ,handle của một communicator mà quản lý tất cả các tác vụ là MPI_COMM_WORLD Mục tiêu chính của Group và những đối tượng Communicator: • Cho phép tổ chức tác vụ, dựa trên chức năng, vào trong những nhóm tác vụ. Làm cho những thao tác Collective Communication có thể thực hiện liên kết một tập các tác vụ liên quan. • Cung cấp nền tảng cho mô hình vật lý ảo do người dùng dịnh mô phỏng (virtual topology) • Cung cấp sự giao tiếp an toàn dữ liệu khi truyền nhận do sử dụng tối ưu các thuật toán liên quan trên một communicator Group và ht t p : / / e t r i t h u c . v n Trang 125 Communicator là tự động, chúng có thể được tạo và hủy bỏ trong suốt sự thực thi của chương trình Những xử lý có thể xuất hiện nhiều hơn trên một group /communicator.Chúng sẽ có một rank duy nhất trong mỗi group /communicator MPI cung cấp trên r ất nhiều hàm liên quan đến group, communicator Communicator là một đối tượng không rõ ràng, với một số thuộc tính, và một số quy tắc đơn giản. A Communicator đặc tả miền truyền thông có thể được sử dụng cho sự truyền thông point-to-point. Intracommunicator được sử dụng cho truyền thông bên trong một nhóm của những xử lý trong nhóm đó .Intracommunicator cũng được sử dụng cho những collective operations Một intercommunicator được sử dụng cho truyền thông điểm điểm (point- to-point) giữa những nhóm xử lý tách rời nhau.Những thuộc tính của môt intercommunicator là hai nhóm .Không có virtual topology nào (sẽ đề cập sau) liên kết với một intercommunicator Chức năng Intracommunicator Intercommunicator Số nhóm 1 2 An toàn giao tiếp Có Có Collecitve Communication Có Không Topologies Có Không Group Management (Sự quản lý nhóm) Mô tả sự vận dụng những nhóm xử lý bên trong MPI. Những thao tác này là local và sự thực thi của nó không yêu cầu sự giao tiếp giữa các xử lý. MPI cho phép sự vận dụng những nhóm bên ngoài những communicator nhưng những nhóm chỉ có thể được sử dụng cho truyền thông điệp bên trong một communicator Communicator management(Sự quản lý communicator) ht t p : / / e t r i t h u c . v n Trang 126 Mô tả sự vận dụng của những communicator bên trong MPI.Những thao tác này truy xuất communicators cái mà local và những sự thực thi của nó không yêu cầu sự giao tiếp giữa các xử lý .Những thao tác này tạo ra những communicator chung và có thể yêu cầu giao tiếp giữa các xử lý. Chúng ta mô tả những cư xử của những hàm này, thừa nhận rằng tham số comm là một intracommunicator 4.6.2. Nguyên tắc sử dụng 1. Mở rộng handle của nhóm cha từ MPI_COMM_WORLD bằng cách sử dụng MPI_Comm_group 2. Tạo ra một nhóm mới như một tập con của nhóm cha bằng cách sử dụng MPI_Group_incl 3. Tạo ra một communicator mới cho mỗi nhóm mới bằng cách sử dụng MPI_Comm_create 4. Xác định rank mới trong communicator mới bằng cách sử dụng MPI_Comm_rank 5. Thực hiện giao tiếp các communicator sử dụng các hàm MPI Messsage pasing 6. Khi kết thúc ,giải phóng communicator mới và group mới tạo với hàm MPI_Comm_free và MPI_Group_free Group Tạo group MPI_Comm_group (MPI_Comm comm ,*group): Xác định group liên kết với một communicator cho trước MPI_Group_excl(MPI_Group group,int n,int *rank,MPI_Group *newgroup): Phát sinh ra một nhóm bằng các sắp xếp lại một nhóm đã có và chỉ lấy những thành viên chưa được lệ kê trong danh sách ht t p : / / e t r i t h u c . v n Trang 127 MPI_Group_incl(MPI_Group group,int n,int *ranks,MPI_Group*newgroup) : Phát sinh một nhóm bằng các sắp xếp lại một nhóm sẵn có và chỉ lấy những thành viên được liệt kê trong danh sách MPI_Group_intersection(MPI_Group group1,MPI_Group group2,MPI_Group*newgroup): Phát sinh ra một nhóm là giao của hai nhóm cho trước với nhau MPI_Group_union(MPI_Group group1,MPI_Group group2,MPI_Group*newgroup):Giống như MPI_Group_intersection nhưng là hợp của hai nhóm MPI_Group_difference(MPI_Group group1,MPI_Group group2,MPI_Group *newgroup) :Tạo ra một nhóm mới từ hiệu của hai nhóm cho trước Truy xuất group MPI_Group_rank(MPI_Group group,int *rank):Trả về rank của xử lý này bên trong một group cho trước ,hay trả về MPI_UNDEFINED nếu xử lý không phải là một thành viên MPI_Group_size (MPI_Group group,int *size) : Trả về số lượng xử lý của một group Lưu ý : Mỗi rank phải là một rank hợp lệ trong group và tất cả các thành phần phải được phân biệt hay chức năng là không lỗi MPI_Group_compare(MPI_Group group1,MPI_Group group2,*result): So sánh hai nhóm và trả về một số integer ,số này là MPI_IDENT nếu trật tự và những thành viên của hai nhóm là như nhau.MPI_SIMILAR nếu chỉ những thành viên là như nhau ,và mặt khác là MPI_UNEQUAL Huỷ group MPI_Group_free(MPI_Group *group):Xác định group cho trước cần giải phóng ht t p : / / e t r i t h u c . v n Trang 128 Communicator Tạo communicator MPI_Comm_create(comm,group,*newcomm):Tạo mới một communicator từ communicator cũ và nhóm mới MPI_Comm_dup(MPI_Comm com,MPI_Comm *newcomm): Nhân đôi một communicator đã có với tất cả thông tin được liên kết Truy xuất communicator MPI_Comm_size(MPI_Comm comm, int *size): Lấy số lượng (size) các xử lý trong communicator MPI_Comm_rank(MPI_Comm comm,int *rank): Lấy định danh của xử lý bên trong một commmunicator ho trước MPI_Comm_compare(MPI_Comm comm1,MPI_Comm comm2,*result) So sánh hai communicator và trả về kết quả là một số integer. Nếu là MPI_IDENT thì ngữ cảnh và n ững nhóm là như nhau. MPI_CONGRUENT nếu có sự khác biệt về ngữ cảnh, MPI_SIMILAR nếu sự khác biệt về ngữ cảnh nhưng giống nhau về group,và còn lại là MPI_UNEQUAL Huỷ communicator MPI_Comm_free(MPI_Comm *comm): Giải phóng đối tượng communicator ht t p : / / e t r i t h u c . v n Trang 129 Chương 5. Thử nghiệm các thuật toán lý thuyết đồ thị 5.1. Các khái niệm cơ bản 5.1.1. Đồ thị Một đồ thi G=(V,E) trong đó V là tập các đỉnh của đồ thị,E là tập các cạnh của đồ thị ,mỗi cạnh sẽ là sự liên kết của hai đỉnh Chỉ đề cập đến hai loại đồ thị là : Đồ thị vô hướng :nếu những cạnh không có thứ tự hai chiều Hai đỉnh u và v trong một đồ thị vô hướng G được gọi là liền kề hay( láng giềng ) nếu u ,v là một cạnh của G .Nếu e=(u,v) thì e được gọi là cạnh liên thuộc với các đỉnh u và v . Cạnh e cũng được gọi là ạnh nối các đỉnh u và v. Các đỉnh u và v được gọi là các điểm đầu mút của cạnh (u,v) Đồ thị hữu hướng khi cạnh có hai chiều nối hai đỉnh khác nhau Khi (u,v) là cạnh của đồ thị có hướng G , thì u được gọi là nối tới v ,và v được gọi là được nối từ u . Đỉnh u gọi là đỉnh đầu , đỉnh v gọi là đỉnh cuối của cạnh (u,v). Đỉnh đầu và đỉnh cuối của khuyên là trùng nhau Một đồ thị trọng số là đồ thị có độ dài cho mỗi cạnh Ký hiệu :G(V,E, w) Ma trận liền kề : A=(ai,j) ,là một mảng 2 chiều với thông tin như sau nếu cạnh (i,j) tồn tại thì ai,j=1(hay độ dài của cạnh ) ngược lại thì ai,j=0 5.1.2. Đường đi Đường đi từ đỉnh u đến đỉnh v là một tập thứ tự các đỉnh ,trong đó v0=v và vk=v và (vi,vi+1) thuộc tập cạnh E với mọi i từ 0 đến k Chiều dài của đường đi là tổng giá trị độ dài các cạnh Đường đi (u,v): Nếu đường đi tồn tại , u tiến tới v Đơn giản nếu tất cả các đỉnh trung gian đi qua là phân biệt Là khuyên nếu vo=vk ht t p : / / e t r i t h u c . v n Trang 130 G là đồ thị liên thông nếu có một đường đi từ một đỉnh đến bất kỳ các đỉnh còn lại 5.2. Dijkstra 5.2.1. Tuần tự Thuật toàn do E.Dijkstra ,nhà toán học người Hà Lan , đề xuất vào năm 1959 . Các bước thực hiện thuật toán Dijkstra: Đầu tiên gán cho đỉnh α nhãn bằng 0 và các đỉnh khác là ∞ .Ta ký hiệu L(α)=0 và L(v)= ∞ cho tất cả các đỉnh khác ( bước lặp thứ 0 ). Các nhãn này là độ dài của đường đi ngắn nhất từ đỉnh α tới các đỉnh này., trong đó đường đi này chỉ chứa đỉnh α. (Vì không có đường đi từ α tới các đỉnh khác α nên ∞ là độ dài đường đi ngắn nhất giữa α và các đỉnh này). Theo thuật toán Dijkstra ta sẽ xây dựng tập đặc biệt các đỉnh. Gọi Sk là tập này sau bước lặp thứ k của thủ tục gán nhãn. Chúng ta bắt đầu bằng So=Φ. Tập Sk được tạo thành từ Sk-1 bằng cách thêm vào đỉnh u không thuộc Sk-1 có nhãn nhỏ nhất. Khi đỉnh u được gộp vào Sk chúng ta sửa đổi nhãn của các đỉnh không thuộc Sk sao cho Lk(v),nhãn của v tại bước k, là độ dài của đường đi ngắn nhất từ α tới v mà đường đi này chỉ chứa các đỉnh thuộc Sk (tức là các đỉnh đã thuộc tập đặc biệt các đỉnh cùng với u) Giả sử v là một đỉnh không thuộc Sk. Để sửa nhãn của v ta thấy Lk(v) là độ dài của đường đi ngắn nhất từ α tới v và chỉ chứa các đỉnh thuộc Sk. Để sửa đổi nhãn ta lưu ý rằng đường đi ngắn nhất từ α tới u ở bước k-1 cùng với cạnh (u,v). Nói cách khác ta có. L(a,v)=min(Lk-1(a,v),Lk-1(a,u)+ w(u,v)) ht t p : / / e t r i t h u c . v n Trang 131 Thủ tục này được lặp bằng cách liên tiếp thêm các đỉnh vào tập đặc biệt các đỉnh cho tới khi đạt tới đỉnh z. Khi thêm z vào tập đặc biệt các đỉnh thì nhãn của nó bằng độ dài của đường đi ngắn nhất từ α tới z. Sau đây là một minh dụ cụ thể thực hiện thuật toán Dijkstra trên một đồ thị vô hướng 6 đỉnh Hình 5-1.1. ht t p : / / e t r i t h u c . v n Trang 132 Hình 5-1.2. Hình 5-1.3. ht t p : / / e t r i t h u c . v n Trang 133 Hình 5-1.4. Hình 5-1.5 ht t p : / / e t r i t h u c . v n Trang 134 Hình 5-1.6 Hình 5-1. Thuật toán Dijkstra tuần tự 5.2.2. Song song Thuật toán Dijkstra tuần tự có rất nhiều điểm dễ song song hoá.Do đó thuật toán này đã được các nhà lập trình song song hóa rất nhiều. Cụ thể sau đây là cách song song hóa của thuật toán Dijkstra tuần tự. Lúc này chúng ta nghĩ rằng không phẩi thực thi thuật toán trên một bộ xử lý nữa mà phân phối các công việc khác nhau cho mỗi bộ xử lý khác nhau thực hiện.Mỗi bộ xử lý sẽ đảm nhận một số đỉnh của đồ thị cùng với ma trận mô tả quan hệ của các đỉnh đó với các đỉnh còn lại • Triển khai thuật toán p l à số bộ xử lý, n là số đỉnh của đồ thị Mỗi bộ xử lý quản lý n/p số đỉnh Nếu n/p dư lẻ thì P0 đến P n-2 sẽ quản lý n/p số đỉnh Số đỉnh còn lại sẽ do P n-1 quản lý ht t p : / / e t r i t h u c . v n Trang 135 Mỗi bộ xử lý sẽ lưu một ma trận với số cột là số đỉnh do chính Pi quản lý và số dòng là n (tương ứng với số đỉnh của đồ thị) liên quan đến ma trận liền kề A Tương ứng với các đỉnh đó sẽ gồm mảng mô tả độ dài trọng số của đỉnh Li[] Hình 5-2 : Thuật toán Dijkstra song song Các bước thực hiện song song hoá như sau : Bước 1: Khởi tạo tập đỉnh Vt={r},L[k]=∞ ∀k ,L[r]=0; Lấy dữ liệu từ tập tin Phân chia dữ liệu trong ma trận trọng số A[][]đến các bộ xử lý .Với mỗi bộ xử lý sẽ có một ma trận con tương đương với một ma trận con của A nhận dữ liệu Mỗi Pi ngoại trừ P0 sẽ lưu một mảng đỉnh riêng cho mình Vi Bước 2: Từ bộ xử lý master , P0 broadcast đỉnh được chọn là r đến các bộ xử lý còn lại ht t p : / / e t r i t h u c . v n Trang 136 Bước 3: Lặp cho đến khi nào đỉnh đích đã được chọn Mỗi Pi sẽ cập nhật mảng L[] với L[k]=Min[L[k],L(Selected)+ W(selectedV,k)] với mọi k thuộc về tập đỉnh Vi Mỗi Pi sẽ tính toán Min Li=Min(trong số mảng Li) Thực hiện việc tính Min trên tất cả các bộ xử lý và lấy giá trị nhỏ nhất .Sau đó chọn được đỉnh có độ dài nhỏ nhất là SelectedV P0 sẽ broadcast giá trị SelectedV đến các bộ xử lý còn lại P0 sẽ đưa đỉnh được chọn vào tập Vt 5.2.3. Thực nghiệm chương trình Phiên bản 1: Giống như hình vẽ minh họa thuật toán Dijkstra song song. Chương trình được viết dưới dạng C chuẩn trên Linux.Chương trình không bao gồm các cấu trúc đồ thị mà lưu tất cả các thông tin đều bằng mảng,mục đích cho việc truyền dữ liệu được nhanh và gọn hơn. Chương trình được chia thành các ập tin sau đây: Đọc dữ liệu từ tập tin • ReadFile.h • ReadFile.cpp Định nghĩa dạng dữ liệu được truyền giữa các tiến trình trong ứng dụng MPI • Vector.h • Vector.cpp Chương trình chính • Dijkstra.h • Dijkstra.cpp Phiên bản 2: ht t p : / / e t r i t h u c . v n Trang 137 Chương trình này được phân chia rõ ràng nhiệm vụ của các nút trên môi trường LAM. Chương trình được viết dưới dạng các đối tượng Lớp Slave quản lý công việc của các nút Lớp Smatter quản lý thông tin của đồ thị có nhiệm vụ , đọc thông tin từ tập tin và phân phối thông tin đến các nút còn lại trên mạng. Chương trình được chia thành các tập tin sau đây: Cấu trúc đồ thị: • Graph.h • Graph.cpp Quản lý công việc các nút: • Slave.h • Slave.cpp Định nghĩa dạng dữ liệu được truyền giữa các tiến trình trong ứng dụng MPI • Vector.h • Vector.cpp Chương trình chính • Dijkstra.h • Dijkstra.cpp Biên dịch chương trình: Được đóng gói thành tập tin makefile Tạo tập tin Makefile có nội dung như sau all:Dijkstra CC=mpic++ Dijkstra: Dijkstra.o Slave.o Vector.o Graph.o #(CC) -o myapp Dijkstra.o Slave.o Vector.o Graph.o Dijkstra.o: Dijkstra.cpp Dijkstra.h Slave.o Vector.o Graph.o #(CC) -c Dijkstra.cpp Dijkstra.h Slave.o Vector.o Graph.o ht t p : / / e t r i t h u c . v n Trang 138 Slave.o:Slave.cpp Slave.h Dijkstra.h #(CC) -c Slave.cpp Vector.o:Vector.cpp #(CC) -c Vector.cpp Graph.o:Graph.cpp #(CC) -c Graph.cpp Nếu đăng nhập với quyền sử dụng là root thì tập tin Makefile có nội dung như trên ,nếu đăng nhâp với quyền sử dụng khác thì thay dấu # bằng dấu $. Chuyển thư mục hiện hành sang thư mục chứa tập tin Makefile Từ dòng lệnh của màn hình Console gõ #make Thực thi chương trình Sau khi biên dịch ,tập tin thực thi là Dijkstra Đổi quyền đăng nhập khác root để sử dụng LAM.Ví dụ $user Thực thi như sau : $lamboot $mpirun –np k Dijkstra với k là số bộ xử lý của môi trường LAM 5.3. Prim 5.3.1. Tuần tự Do Robert Prim đưa ra vào năm 1957, mặc dù ý tưởng cở bản của nó đã có từ sớm hơn rất nhiều. Để thực hiện thuật toán Prim ,ta bắt đầu bằng việc chọn một cạnh bất kỳ có trọng số nhỏ nhất, đặt nó vào cây khung. Lần lượt ghép vào cây và không tạo ra chu trình trong cây. Thuật toán sẽ dừng khi n-1 cạnh đã được ghép vào cây. b1: ht t p : / / e t r i t h u c . v n Trang 139 b2: b h c i g d e f a 4 8 7 9 10 14 8 11 7 2 4 6 b3: b4: b5: ht t p : / / e t r i t h u c . v n Trang 140 b6: b7: b h i g e f a 4 8 7 9 10 14 8 11 7 2 4 6 c d b8: b h i g e f a 4 8 7 9 10 14 8 11 7 2 4 6 c d b9: ht t p : / / e t r i t h u c . v n Trang 141 b h i g e f a 4 8 7 9 10 14 8 11 7 2 4 6 c d Hình 5-3. Thuật toán Prim tuần tự 5.3.2. Song song Do hai thuật toán Dijkstra và Prim gần giống nhau về mặt bản chất đó là cùng tìm giá trị nhỏ nhất. Đối với Prim đó là cạnh có trọng số nhỏ nhất, còn đối với Dijkstra là đỉnh có độ dài từ đỉnh nguồn đến nó nhỏ nhất. Nên về mặt song song hóa thuật toán Prim cũng tương tự như thuật toán Dijkstra • Triển khai thuật toán p bộ xử lý ,n=số đỉnh của đồ thị Mỗi bộ xử lý quản lý n/p số đỉnh Nếu n/p dư lẻ thì P0 đến Pn-2 sẽ quản lý n/p số đỉnh Số đỉnh còn lại sẽ do Pn-1 quản lý Mỗi bộ xử lý sẽ lưu một ma trận với số cột là số đỉnh do chính Pi quản lý và số dòng là n (tương ứng với số đỉnh của đồ thị) của ma trận liền kề A ht t p : / / e t r i t h u c . v n Trang 142 Hình 5-3 : Thuật toán Prim song song Các bước xử lý thuật toán : Bước 1: Khởi tạo tập đỉnh Vt={r},L[k]=∞ ∀k ,L[r]=0; Lấy dữ liệu từ tập tin Phân chia dữ liệu trong ma trận trọng số A[][]đến các bộ xử lý .Với mỗi bộ xử lý sẽ có một ma trận con tương đương với một ma trận con của A nhận dữ liệu Mỗi Pi ngoại trừ P0 sẽ lưu một mảng đỉnh riêng cho mình Vi Bước 2: Từ bộ xử lý master , P0 broadcast đỉnh được chọn là r đến các bộ xử lý còn lại Bước 3: Lặp cho đến khi nào đ ã c ó n đỉnh được chọn (với n là số đỉnh của đồ thị) Mỗi Pi sẽ cập nhật mảng L[] với L[k]=Min[L[k],W(selectedV,k)] với mọi k thuộc về tập đỉnh Vi Mỗi Pi sẽ tính toán Min Li=Min(trong số mảng Li) Thực hiện việc tính Min trên tất cả các bộ xử lý và lấy giá trị nhỏ nhất .Sau đó chọn được đỉnh có độ dài nhỏ nhất là SelectedV ht t p : / / e t r i t h u c . v n Trang 143 P0 sẽ broadcast giá trị SelectedV đến các bộ xử lý còn lại P0 sẽ đưa đỉnh được chọn vào tập Vt 5.3.3. Thực nghiệm chương trình 5.4. Bellman – Ford 5.4.1. Tuần tự Thuật toán Bellman – Ford giải quyết bài toán tìm đường đi ngắn nhất trong trường hợp tổng quát hơn so với thuật toán Dijkstra , trong đó trọng số của các cạnh nối có thể là số âm. Cho một đồ thị G=(V, E) có hướng và trọng số, với đỉnh nguồn s và hàm trọng số w : E →R, thuật toán trả về giá trị kiểu boolean cho biết từ đỉnh nguồn có thể đến được mạch âm hay không. Nếu có thì thuật toán sẽ kết thúc mà không có lời giải, còn nếu không sẽ kết xuất tất cả các con đường ngắn nhất cùng với chiều dài của chúng. Giống như thuật toán Dijkstra, thuật toán Bellman – Ford cũng sử dụng kỹ thuật relaxation, bằng cách ngày càng giảm chiều dài của đường đi ngắn nhất từ đỉnh s đến các đỉnh v∈V cho đến khi đạt được giá trị ngắn nhất. Thuật toán trả về giá trị TRUE khi và chỉ khi từ đỉnh gốc không đến được mạch âm. BELLMAN-FORD(G, w, s) INITIALIZE-SINGLE-SOURCE(G, s) for i ←1 to |V[G]|-1 do for each edge (u, v) ∈E[G] do RELAX(u, v, w) for each edge (u, v) ∈E[G] do if d[v] > d[u] + w(u, v) then return FALSE return TRUE b1: ht t p : / / e t r i t h u c . v n Trang 144 Hình a b2 Hình b b3 Hình c b4 ht t p : / / e t r i t h u c . v n Trang 145 Hình d b5 Hình e Hình 5-4: Thuật toán Bellman-Ford tuần tự Hình bên trên thể hiện thuật toán Bellman-Ford với 5 đỉnh. Sau khi thực hiện phép khởi tạo thông thường, thuật toán thực hiện qua |V|-1 bước qua các cạnh của đồ thị. Hình (b) và (e) cho thấy trạng thái của thuật toán sau mỗi bước. Sauk hi thực hiện |V|-1 bước , thuật toán sẽ tiến hành kiểm tra mạch âm và trả về giá trị Boolean thích hợp. Thuật toán Bellman-Ford có chi phí là O(V,E), bởi chi phí cho việc khởi tạo là O(V), còn từng bước sẽ tốn O(E), và dòng lặp cuối cùng cũng tốn chi phí là O(E). ht t p : / / e t r i t h u c . v n Trang 146 Để chứng minh tính đúng đắn của thuật toán, chúng ta sẽ chứng minh là nếu đồ thị không chứa mạch âm thì có thể tính được đường đi ngắn nhất từ đỉnh gốc đến mọi đỉnh còn lại. Bổ đề Cho G = (V, E) là một đồ thị có hướng và trọng số với đỉnh nguồn s và hàm trọng số w : E →R, và giả sử trong G đỉnh s không tới được mạch âm. Do đó khi kết thúc thuật toán, chúng ta có d[v] = δ (s, v) với mọi đỉnh v có thể đến từ s. Chứng minh Gọi v là đỉnh có thể đến được từ s, và p = (k<=|V|-1) là đường đi ngắn nhất từ s đến v, trong đó v0 = s và vk = v. Chúng ta sẽ chứng minh baằng phương pháp quy nạp vớ i = 0, 1, …, k, chúng ta có d[vi] =δ (s, vi) sau bước thứ i, và kết quả này vẫn được duy trì về sau. Bởi vì có |V|-1 bước, chúng ta sẽ dùng kết quả này để chứng minh bổ đề Sau bước khởi gán, chúng ta có d[v0] = δ (s, v0) = 0. Trong bước quy nạp chúng ta giả sử đúng với trường hợp i-1, d[vv-1] = δ (s, vi-1) theo bổ đề ở trên chúng ta kết luận được d[vi] = δ (s, vi) sau bước thứ i, do đó bổ đề được chứng minh. Hệ quả Cho G = (V, E) là một đồ thị có hướng và trọng số với đỉnh nguồn s và hàm trọng số w : E →R. Với mỗi đỉnh v∈V, tồn tại một đường đi từ s đến v khi và chỉ khi thuật toán Bellman-Ford kết thúc với d[v]<∞ Chứng minh Chứng minh tương tự như bổ đề. Định lý (Tính đúng đắn của thuật toán Bellman-Ford) Thực hiện thuật toán Bellman-Ford trên đồ thị G = (V, E) có hướng và trọng số với đỉnh nguồn s và hàm trọng số w : E →R. Nếu từ s không đến được ht t p : / / e t r i t h u c . v n Trang 147 mạch âm trong G, thuật toán trả về giá trị TRUE, và ta có d[v] = δ (s, v) với mọi đỉnh v∈V, ngược lại trả về giá trị FALSE. Chứng minh Giả sử trong đồ thị G, từ đỉnh s không đến được mạch âm. Trước tiên chúng ta sẽ chứng minh tại thời điểm kết thúc, d[v] = δ (s, v) với mọi đỉnh v∈V. Nếu từ s đến được đỉnh v, thì định lý đã cho là đúng với bổ đề trên. Bây giờ chúng ta sẽ sử dụng phát biểu trên để chứng minh thuật toán BELLMAN-FORD trả về giá trị TRUE. Khi kết thúc chúng ta có tất cả các cạnh (u, v) ∈E. d[v] = δ (s, v) ≤ δ (s, u) + ω (u, v) = d[u] + ω (u, v) Do đó theo như mô tả trong thuật toán, giá trị trả về là TRUE. 5.4.2. Song song Mô tả chương trình Như đã đề cập ở trên ta có 2 khái niệm cần được phân biệt rõ, là xây dựng chương trình song song từ một thuật toán song song cụ thể đã được thực nghiệm và kiểm tra tính đúng đắn, còn một cách khác để cài đặt một chương trình song song là tiến hành song song hóa các phần trong chương trình tuần tự. Trong thực tế, phần lớn người ta thường sử dụng cách thứ hai do việc thiết kế ra một thuật toán song song là điều rất khó khăn và cần nhiều thời gian làm việc. Chương trình song song cài đặt thuật toán Bellman-Ford được dùng ở đây là tiến hành theo cách thứ hai như đề cập phần trên. Đặc điểm chương trình: o Số bộ xử lý tham gia là biến động tùy thuộc vào người sử dụng. o Nội dung ma trận kề của đồ thị được nhận vào từ tập tin. o Có thể thay đổi các tham số của chương trình như : đỉnh nguồn, đỉnh đích, tên tập tin nhập liệu. Thực hiện ht t p : / / e t r i t h u c . v n Trang 148 Quy ước : + ta có thể hiểu khái niệm tiến trình ở đây là trên một bộ xử lý, số lượng các tiến trình tham gia cũng là số bộ xử lý cần có (cũng là số máy trạm nếu trên một máy chỉ có một bộ xử lý). + P là ma trận gồm 2 dòng, có số cột bằng với số đỉnh của đồ thị, dùng để lưu đường đi ngắn nhất. + L là ma trận kề lưu nội dung của đồ thị. Các bước thuật toán b1: khởi tạo ma trận P và L. b2: tiến trình chính gửi cho các tiến trình con một số cột của ma trận L, tùy vào số máy để chạy chương trình. b3: tiến trình chính broadcast 1 dòng của ma trận P, rồi nhận lại P sau khi các tiến trình con thực hiện tính toán, chương trình kết thúc khi: 2 dòng của ma trận P tương tự nhau, lúc đó kết luận từ đỉnh nguồn có thể đến được mạch âm, bài toán kết thúc mà không có lời giải. Khi số lần thực thi bước 3 lớn hơn số đỉnh của đồ thị, chương trình kết xuất đường đi ngắn nhất từ đỉnh nguồn đến các đỉnh còn lại theo như trong ma trận P. ht t p : / / e t r i t h u c . v n Trang 149 Tieán trình chính Tieán trình 1 Tieán trình 2 Tieán trình chính Broadcast 1 doøng cuûa P Nhaän laïi P sau khi caùc tieán trình con tính tính toaùn Tieán trình chính Tieán trình 1 Tieán trình 2 Tieán trình chính Hình 5-5 : Thuật toán Bellman-Ford song song Thực hiện + Ma trận P được lưu theo lớp TMatrixRow (tổ chức ma trận theo mảng các phần tử được đánh số theo dòng rồi theo cột) class TMatrixRow { public: void printMatrix(); void init(unsigned int mLen, unsigned int nLen); TMatrixRow(unsigned int mLen, unsigned int nLen); void freeMatrix(); unsigned int m; //number of rows unsigned int n; //number of columns ht t p : / / e t r i t h u c . v n Trang 150 double *data; //data, ordered by row, then by column double **rows; //pointers to rows in data TMatrixRow(); virtual ~TMatrixRow(); }; + Ma trận L được lưu theo lớp TMatrixCol(tổ chức ma trận theo mảng các phần tử được đánh số theo cột rồi theo dòng) class TMatrixCol { public: void init(unsigned int len); void printMatrix(); void freeMatrix(); int read(char* filename); unsigned int n; //number of rows = number of columns double *data; //data, ordered by column, then by row double **cols; //pointers to cols in data TMatrixCol(); TMatrixCol(unsigned int len); virtual ~TMatrixCol(); }; ht t p : / / e t r i t h u c . v n Trang 151 Chương 6. Kết luận – Hướng phát triển 6.1. Kết luận Dựa trên các kiến thức đã tìm hiểu được về tính toán lưới, cách thức lập trình trên môi trường song song và phân tán, cũng như các môi trường hỗ trợ phát triển, đề tài đã đat được mục tiêu đề ra là nghiên cứu về tính toán lưới và thực nghiệm trên một số thuật toán lý thuyết đồ thị. Mặc dù thời gian thực hiện có hạn, bên cạnh đó gặp phải một số khó khăn trong việc triển khai ứng dụng, do đây là một đề tài khá mới ít được quan tâm phát triển trước đây, nhưng chúng em cũng cố gắng thực hiện để đạt được các yêu cầu đề ra. Các chương trình viết ra đều có khả năng thực hiện trên nhiều máy với số máy xử lý tùy biến và thực hiện đúng kết quả cần thiết. Chúng em hy vọng sẽ tiếp tục cải tiến chương trình, tối ưu các thuật toán để đạt được kết quả tốt nhất, tận dụng được sức mạnh của các máy con để giải quyết vấn đề. Bên cạnh đó, chúng em cũng hy vọng có thể triển khai trên các hệ thống lớn hơn. 6.2. Hướng phát triển Tương lai của tính toán song song và phân bố, và xa hơn nữa là tính toán lưới vẫn còn rất lớn. Trên thế giới hiện nay người ta vẫn đang tiếp tục nghiên cứu mạnh mẽ nhằm có thể tận dụng được khả năng của các hệ thống máy tính lớn trên thế giới, góp phần trong công tác nghiên cứu khoa học cũng như thương mại. Do trong thời hạn cho phép cũng như khả năng và kiến thức hiện nay vẫn chưa thực sự phát huy được hết khả năng của tính toán lưới, vì thế đề tài hiện còn khả năng phát triển rất lớn. • Cải tiến chương trình ht t p : / / e t r i t h u c . v n Trang 152 Tìm hiểu sâu hơn về thư viện MPI để phát huy tiềm năng rất lớn của nó, bên cạnh đó cũng cần thực hiện một số phương pháp tối ưu như giảm chi phí do tính toán, truyền tải thông tin…Bên cạnh đó nếu có điều kịện sẽ sử dụng trên môi trường hỗ trợ tính toán lưới phổ biến nhất hiện nay là Globus. • Triển khai thực tế Do hạn chế về cơ sở vật chất nên các chương trình chưa được triển khai trên các hệ thống lớn, thực tế để qua đó có thể đánh giá đúng được khả năng của chương trình. Do đó chúng em hy vọng có thể tiếp tục phát triển được đề tài để tận dụng được sức mạnh của tính toán lưới, đem lại hiệu quả cao trong công tác nghiên cứu khoa học và áp dụng vào thực tế. ht t p : / / e t r i t h u c . v n Trang 153 Tài liệu tham khảo Tài liệu viết: [1] Wolfgang Gentzsch, Grid Computing : A New Technology for the Advanced Web, Sun Microsystem Inc, 2001 [2] Ananth Gramma, Anshul Gupta, George Karypis, Vipin Kumar : Introduction to Parallel Computing 2nd, Addison Wesley, USA, 2003 [3] Behrooz Parhami, Introduction to Parallel Processing Algorithms and Architectures, Kluwer Academic, 2002 [4] Fran Berman, Anthony J.G.Hey, Geoffrey C.Fox, Grid Computing Making the Global Infrastructure a Reality, Wiley, 2003 [5] IBM RedBooks, Introduction to Grid Computing with Globus, 2003 [6] Graeme S.McHale, Parallel Programming on Linux Networks Using MPI & PVM [7] Mark Baker, Rajkumar Buyya, Domenico Laforenza, Grids and Grid technologies for wide-area distributed computing, Software – Practice and Experience, 2002 [8] 7.1.1 Lam Install ,LAM/MPI Team Open Systems LAb [9] 7.1.1 Lam User Guide,LAM/MPI Team Open Systems LAb Website: [10] Global Grid Forum, [11] Ian Foster, Designing and Building Parallel Programs, 1995, [12] [13] [14] [15] [16] Application of Parallel Computers [17]

Các file đính kèm theo tài liệu này:

  • pdfNghiên cứu tính toán lưới và thực nghiệm trên một số thuật toán lý thuyết đồ thị.pdf