Cân bằng tải tập trung trong bài toán tìm đường đi ngắn nhất với giải thuật

Một phần của tài liệu Kỹ Thuật Đường Ống Và Cân Bằng Tải Trong Lập Trình Song Song MPI (Trang 62 - 70)

thuật Moore

Cân bằng tải

Khi chia một bài toán vào một số cố định các tiến trình để thực hiện song song thì mỗi tiến trình sẽ biết trước được số lượng công việc cần làm. Thêm vào đó, giả sử rằng các tiến trình được phân bố vào các bộ xử lý mà không có bất kỳ thảo luận nào về tác dụng của các loại bộ xử lý cũng như tốc độ của chúng. Khi đó, có thể một số bộ xử lý sẽ hoàn thành các tác vụ của chúng trước các bộ xử lý khác và trở nên nhàn rỗi bởi vì các công việc được phân chia không bằng nhau hoặc các bộ xử lý tính toán nhanh hơn bộ xử lý khác (hoặc cả hai tình huống). Với ý tưởng đó, chúng ta mong muốn tất cả các bộ xử lý thực hiện liên tục trên các tác vụ để đạt được thời gian thực hiện nhỏ nhất. Để đạt được điều này thì chúng ta phân bố các tác vụ bằng nhau trên các bộ xử lý gọi là cân bằng tải.

Cân bằng tải động

Trong cân bằng tải động, các tác vụ được truyền đến các bộ xử lý trong suốt quá trình thực hiện của chương trình. Cân bằng tải động có hai loại:

- Cân bằng tải động tập trung

- Cân bằng tải động không tập trung

Trong cân bằng tải động tập trung, các tác vụ được thực hiện từ một vị trí tập trung. Sử dụng cấu trúc master-slave (chủ-tớ), tiến trình chủ điều khiển một tập các tiến trình tớ một cách trực tiếp. Ngược lại, cân bằng tải động không tập trung các tác vụ được thực hiện giữa các tiến trình bất kỳ. Một tập hợp các tiểu trình tính toán trên bài toán và tương tác của chúng được gởi đến một tiến trình đơn. Một tiểu trình có thể nhận hoặc gởi nhiệm vụ đến các tiểu trình khác.

Cân bằng tải động tập trung.

Tiến trình chủ quản lý một tập hợp các tác vụ để thực hiện. Các tác vụ được gởi đến các tiến trình tớ. Khi một tiến trình tớ hoàn thành một tác vụ, nó yêu cầu tác vụ

Kỹ thuật phân chia công việc có thể dễ dàng áp dụng cho các bài toán chia để trị đơn giản. Cũng có thể áp dụng cho các bài toán có tác vụ khác nhau và kích cỡ khác nhau. Nhìn chung kỹ thuật này thực hiện tốt cho các tác vụ lớn và phức tạp. Nếu tiến trình lớn đang thực hiện thì tiến trình nhỏ sẽ hoàn thành trước, sau đó đợi tiến trình lớn hoàn thành.

Kỹ thuật phân chia công việc cũng dễ dàng áp dụng khi số tiến trình có thể thay đổi trong suốt quá trình thực hiện của chương trình. Trong một số các thuật toán, đặc biệt là thuật toán tìm kiếm, khi thực hiện tác vụ này có thể phát sinh tác vụ khác, nhưng cuối cùng số tác vụ phải giảm xuống bằng không khi đó việc tính toán mới hoàn tất. Hàng đợi được sử dụng để quản lý các tác vụ, như hình 3.1. Nếu tất cả các tác vụ có kích cỡ và tính cấp thiết giống nhau thì có thể tạo ra một hàng đợi vào trước ra trước (FIFO) đơn giản. Nếu một số tác vụ quan trọng hơn tác vụ khác (ví dụ như cần đưa ra một giải pháp một cách nhanh chóng) thì khi đó những tác vụ này sẽ được gởi đến các tiến trình tớ đầu tiên.

Tìm đường đi ngắn nhất:

Chúng ta sẽ nghiên cứu tỉ mỉ bài toán tìm khoảng cách ngắn nhất giữa hai điểm trong một đồ thị. Nó có thể phát biểu như sau:

Cho một tập các node kết nối với nhau ở đây các đường kết nối giữa các node có trọng số, tìm đường đi từ một node đặc biệt đến các node đặc biệt khác mà có đường đi với trọng số nhỏ nhất.

Phân chia công việc

Hàng đợi Các tác vụ Tiến trình chủ Các tiến trình tớ Gởi tác vụ Yêu cầu tác vụ (và có thể đưa ra các tác vụ mới)

Các node kết nối với nhau có thể được mô tả bởi một đồ thị. Trong thuật ngữ đồ thị, các node được gọi là các đỉnh và các liên kết gọi là các cạnh nếu một cạnh đi theo một hướng, đồ thị là đồ thị định hướng. Bài toán bây giờ là tìm một đường đi tốt nhất thông qua đồ thị. Đồ thị có thể được sử dụng trong các lời giải với những vấn đề khác nhau như:

Tìm đường đi ngắn nhất giữa hai thành phố hoặc hai điểm của một bản đồ, ở đây trọng số miêu tả khoảng cách.

Lộ trình nhanh nhất để đi du lịch, ở đây trọng số miêu tả thời gian.

Chi phí ít nhất để đi du lịch bằng máy bay, ở đây trọng số miêu tả giá trị của chuyến bay giữa các thành phố.

Cách tốt nhất để để leo một ngọn núi căn cứ vào một bản đồ địa hình với những đường chỉ dẫn.

v.v

Leo lên một ngọn đồi sẽ được sử dụng để minh họa. Ở đây trọng số được xác định là số lượng những nỗ lực trải qua giữa hai điểm cắm trại được kết nối với nhau. Chú ý đồ thị ở đây là đồ thị có hướng. Bởi vì nỗ lực để lên đỉnh đồi sẽ khác với nỗ lực để xuống đồi.

Hình 3.2 Leo một ngọn đồi

Hình 3.3 Đồ thị của leo đồi

1.Matrận kề, một mảng hai chiều a, trong đó a[i][j] nắm trọng số kết nối giữa đỉnh i với đỉnh j nếu tồn tại liên kết.

2. Danh sách kề, với mỗi đỉnh, một danh sách các đỉnh được kết nối trực tiếp với đỉnh bởi cạnh và có trọng số tương ứng kết hợp với cạnh.

Hai phương pháp này có thể mô tả như hình sau:

Ma trận kề (adsbygoogle = window.adsbygoogle || []).push({});

Danh sách kề

Tìm kiếm một đồ thị

Có hai thuật toán tìm đường đi ngắn nhất để xác định cách tốt nhất đến đỉnh núi:

Moore, 1957 Dijkstra, 1959

Hai thuật toán này là tương tự nhau, thuật toán Moore được chọn bởi vì nó tuân thủ thực thi song song nhiều hơn, mặc dù nó có lẽ làm nhiều việc hơn.

Thuật toán Moore: Bắt đầu từ một đỉnh nguồn, thuật toán thi hành khi đỉnh i được xem xét như sau: tìm khoảng cách từ đỉnh j đến i và so sánh với khoảng cách nhỏ nhất hiện tại đến j. Thay đổi khoảng cách nhỏ nhất hiện tại nếu khoảng cách từ i đến là ngắn hơn. Gọi di là khoảng cách nhỏ nhất hiện tại từ đỉnh nguồn đến đỉnh i, và wi,j là trọng số của cạnh từ đỉnh i đến đỉnh j, ta có:

dj= min(dj,di+wi,j)

Một hàng đợi các đỉnh với phương thức vào trước ra trước được tạo và nắm lấy danh sách các đỉnh để xem xét. Những đỉnh được xem xét chỉ khi chúng nằm trong hàng đợi. Khi khởi tạo, chỉ đỉnh nguồn nằm trong hàng đợi. Giả sử có n đỉnh, và đỉnh 0 là đỉnh nguồn. Khoảng cách ngắn nhất hiện tại từ đỉnh nguồn đến đỉnh i sẽ được lưu trong mảng dist[i] (1<=i<=n). Đầu tiên mảng dist[i] được khởi tạo là vô cùng. Giả sử w[i][j] nắm trọng số của cạnh từ đỉnh i và đỉnh j. Đoạn code như sau:

newdist_j=dist[i]+w[i][j];

if(newdist_j<dist[j]) dist[j]=newdist_j;

Khi khoảng cách nhỏ nhất được tìm thấy đến đỉnh j, đỉnh j được thêm vào hàng đợi (nếu nó không thực sự nằm trong hàng đợi).

Chi tiết của quá trình xử lý như sau ( giả sử ta sử dụng đồ thị leo đồi để minh họa) Giá trị khởi tạo cho hàng đợi và mảng trọng số lúc đầu là:

Phần tử dist[a] luôn là 0 khi đỉnh a là nguồn, nhưng có thể tổng quát lên khi a được thay thế bằng một đỉnh nguồn khác. Đầu tiên, mỗi cạnh bắt nguồn từ a là được xem

xét. Trong đồ thị của chúng ta đó sẽ là đỉnh b. Cả hàng đợi(vertex_queue[]) và mảng trọng số (dist[]) được cập nhật như sau:

Một đỉnh mới b, được đưa vào hàng đợi, nhiệm vụ tìm kiếm quanh b bắt đầu. Bây giờ chúng ta có 4 cạnh để khảo sát là đến c, d, e và f. Trong thuật toán này, chúng ta không cần thiết khảo sát những đỉnh này theo một vài cách sắp xếp đặc biệt. Thuật toán Dijkstra yêu cầu những đỉnh gần đó phải được khảo sát trước, mà chúng là phải được xử lý tuần tự. Tuy nhiên, thuật toán Moore có thể yêu cầu những đỉnh được khảo sát lại . Để giải thích chúng ta khảo sát những đỉnh theo thứ tự f, e, d, c. Khoảng cách từ đỉnh b đến đỉnh f là dist[f]=10+51=61, dist[e]=10+24=34, dist[d]=10+13=23, và dist[c]=10+8=18. Tất cả những đỉnh này là được thêm vào hàng đợi (trừ f)

Đỉnh f không cần thêm vào bởi vì nó là đỉnh đích và không cần đòi hỏi phải xử lý. Bắt đầu với đỉnh e, mà đỉnh này có một cạnh nối đến f với trọng số là 17, khoảng cách đến f qua e là dist[e]+17=34+17=51, nhỏ hơn khoảng cách hiện tại đến đỉnh f và nó thay thế khoảng cách này, dẫn đến :

Đỉnh tiếp theo d. có một cạnh đến e với trọng số là 9, cho khoảng cách đến e qua d là dist[d]+9=23+9=32, nhỏ hơn khoảng cách hiện tại đến e và nó thay thế khoảng cách này. Đỉnh e được thêm vào hàng đợi như sau:

Đỉnh tiếp theo c. Chúng ta có một cạnh từ d với trọng số 14. Do đó khoảng cách thông qua c đến d là dist[c]+14=18+14=32. Nó lớn hơn khoảng cách hiện tại đến d là 23, do đó khoảng cách này là không được thay thế.

Đỉnh tiếp theo e (lặp lại) có một cạnh đến f với trọng số là 17, khoảng cách đến f qua e có dist[e]+17=32+17=49, khoảng cách này nhỏ hơn khoảng cách hiện tại đến f và thay thế khoảng cách này, như sau:

Đến đây không còn đỉnh nào để xem xét. Chúng ta có khoảng cách nhỏ nhất từ đỉnh a đến các đỉnh khác, bao gồm đỉnh đích f. Đường đi trong trường hợp của chúng ta là: a->b->d->e->f

Mã tuần tự : chi tiết hàng đợi các đỉnh là được bỏ qua. Lấy next_vertex() trả về đỉnh tiếp theo từ trong hàng đợi các đỉnh hoặc num_vertex nếu bằng 0. Chúng ta giả định sử dụng ma trận kề w[][] được truy cập tuần tự để tìm cạnh tiếp theo.

while((i=next_vertex())!=num_vertex) for(j=0;j<n;j++) if(w[i][j]!=infinity) { newdist_j=dist[i]+w[i][j]; if(newdist_j<dist[j]) { dist[j]=newdist_j; append_queue(j); } }

Thực thi song song: ở đây chúng ta xem xét cân bằng tải động tập trung cho giải pháp phân chia công việc (work pool).

Phân chia công việc tập trung: Tiến trình chủ (master) quản lý hàng đợi gồm tập các đỉnh trong vertex-queue[], mỗi đỉnh trong đó được xem như những nhiệm vụ. Mỗi tiến trình tớ (slave) lấy đỉnh từ vertex-queue[] và trả về tập các đỉnh mới. Tiến trình slave phải xác định các cạnh và tính toán khoảng cách, do đó chúng cần truy cập vào mảng trọng số, vì vậy mỗi tiến trình slave phải có một ma trận trọng số chứa khoảng cách các đường đi của đồ thị, chúng còn phải nắm mảng khoảng cách

ngắn nhất hiện tại dist[] mà nó được quản lý bởi master, những thông điệp cần phải được gởi đến master để truy cập thông tin này. dist[] thường xuyên được cập nhật bởi master khi có những khoảng cách ngắn hơn được gởi từ slave. Cuối cùng sau khi hàng đợi là không còn đỉnh cần phải xử lý, master phải gởi thông điệp yêu cầu chấm dứt công việc của các slave. Sau đây là giả mã của chương trình:

Master

while(vertex_queue()!=empty) {

Recv(PANY,SOURCE=Pi); //nhận yêu cầu từ slave v=get_vertex_queue(); //lấy từ hàng đợi một đỉnh Send(&v,Pi); //gởi tới slave yêu cầu Send(dist,n,Pi); //gởi dist[] tới slave Recv(dist[j],j,PANY,source=Pi);//nhận kết quả từ slave

append_queue(j,dist[j]); //bổ sung bào queue }

dem=0; (adsbygoogle = window.adsbygoogle || []).push({});

while(dem<size-1) {

Recv(PANY,request_tag); //nhận yêu cầu khi đã hết nhiệm vụ Send(Pi,termination_tag); //gởi thông điệp kết thúc công việc dem++; } } Slave while(1) {

Send(Pmaster,request_tag); //gởi thông điệp yêu cầu công việc Recv(&v, Pmaster,tag); //nhận nhiệm vụ từ master

if(tag!=termination_tag) //chưa phải là kết thúc công việc {

Recv(dist,n,Pmaster); //nhận mảng khoảng cách ngắn nhất for(j=0;j<n;j++)

if(w[v][j]!=infinity) //nếu có cạnh nối {

newdist_j=dist[v]+w[v][j];

if(newdist_j<dist[j]) //tìm được khoảng cách nhỏ hơn {

dist[j]=newdist_j;

Send(j,dist[j],Pmaster);//gởi kết quả về master }

} }

Else//kết thúc công việc {

return; }

Một phần của tài liệu Kỹ Thuật Đường Ống Và Cân Bằng Tải Trong Lập Trình Song Song MPI (Trang 62 - 70)