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
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.
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) .
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)
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) 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
4.4.3. Tính tốn gộp
Các hàm ở dạng này cung cấp thêm tính tố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 tốn trả về trên một nút hoặc kết quả tính tố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)
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 tốn đĩ về một tiến trình cĩ rank=root.
4.4.3.1. Một số kiểu tính tốn do MPI cung cấp
CÁC THỦ TỤC TÍNH TỐ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 Tốn tử luận lý AND integer
MPI_BAND Tốn tử dấu bit AND integer,MPI_BYTE MPI_LOR Tốn tử luận lý OR integer
MPI_BOR Tốn tử dấu bit OR integer,MPI_BYTE MPI_LXOR Tốn tử luận lý XOR integer
MPI_BXOR Tố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,và 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,và long double
4.4.3.2. Một số kiểu tính tố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 tố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 tốn cĩ phù thuộc vào những yêu cầu sau đây khơng?
Bước 1: Cĩ tính kết hợp Ví dụ a + (b+c)=(a+b)+c
Ví dụ a+b=b+a
Bước 2 : bổ sung tố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 tố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 tốn được
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 tố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 tố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 tố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 tốn gộp 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ĩ
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. Ngồ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
Ngồ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úc 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)
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
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à :
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
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 đi 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 inbuf.
Chương 5. Thử nghiệm các thuật tốn lý thuyết đồ thị
Các chương trình mà nhĩm chúng em test ởđây sử dụng các máy với cấu hình như sau:
• Khi trên 1 máy : Pentium IV 3GHz, RAM 512MB
• Khi trên 2 máy : Pentium IV 3GHz, RAM 512MB và Pentium IV 800 MHz, RAM 256MB
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à cạ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 <v0,v1,...,vk, 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
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
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 tốn do E.Dijkstra, nhà tốn học người Hà Lan, đề xuất vào năm 1959.
Các bước thực hiện thuật tố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 tố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
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 ví dụ cụ thể minh họa thuật tốn Dijkstra trên một đồ thị vơ hướng 6 đỉnh
Hình 5-1.3.
Hình 5-1.5
Hình 5-1.6
5.2.2. Song song
Thuật tốn Dijkstra tuần tự cĩ rất nhiều điểm dễ song song hố. Do đĩ thuật tố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 tốn Dijkstra tuần tự.
Lúc này chúng ta nghĩ rằng khơng phẩi thực thi thuật tố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 tố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 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ị) liên quan đến ma trận liền kề A
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 đỉ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 tố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 Tuần tự 0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 300 Đỉnh 700 Đỉnh 1000 Đỉnh Số đỉnh của đồ thị T ho i g ia n x ử l ý ( gi ây )
0 2 4 6 8 10 12 14 2 4 6 8 10 12 14 16 Số tiến trình T hờ i g ian x ử ly ù ( gi â y) 300 Đỉnh 700 Đỉnh 1000 Đỉnh Kiểm tra với đồ thị vừa 0 2 4 6 8 10 12 2 4 6 8 10 12 14 16 Số tiến trình T hơ