VII. Sự phát triển của điện toán đám mây trong tương lai
5. Hiện trạng ứng dụng điện toán đám mây công cộng ở Việt Nam
2.5 Giao tiếp point-to-point
- Thực hiện để đọc ma trận từ tập tin - Thực thi bởi process
- Đọc một nhóm các dòng bên cạnh nhau trong ma trận
- Gửi trực tiếp một tin chứa những dòng tới process chịu trách nhiệm quản lý chúng.
- Thực hiện in ma trận
Mỗi process gửi tới những dòng trong nhóm của ma trận tới process 0. Process 0 nhận những tin này và in những dòng theo chuẩn xuất.
Giao tiếp liên quan tới các cặp process:
- Một process gửi một tin - Process khác nhận tin
Hình 25. Sự giao tiếp liên quan giữa một cặp của các tiến trình
Process không liên quan tới sự giao tiếp
Cả hai tiến trình gửi và nhận đều được ngăn chặn
Cả hai tiến trình gửi và nhận cần phải có điều kiện thực hiện bởi quá trình phân cấp.
...
if ( id == i ) {
...
/* Send message to process j */ ...
}
else if ( id == j ) {
...
/* Receive message from process i */ ...
}
Các lần gọi giao tiếp phải được trong mã thực thi có điều kiện.
Hàm MPI_Send
Thể hiện một giới hạn gửi
int MPI_Send ( void * buffer, int count, MPI_Datatype datatype, int dest,int tag, MPI_Comm comm );
buffer địa chỉ bắt đầu của mảng dữ liệu gửi
count số lượng dữ liệu trong mảng (số nguyên không âm)
datatype loại của mỗi mục dữ liệu (uniform since it is an array); định nghĩa bởi một MPI constant
dest Rank của điểm đích (integer)
tag thẻ tin, hoặc nhãn nguyên; cho phép xác định mục đích của tin
comm Communicator; nhóm của các tiến trình tham gia vào chức năng giao tiếp này.
Các hàm ngăn chặn cho tới khi vùng đệm của tập tin có sẵn
Vùng đệm của tin trống khi: tin đã sao chép tới vùng đệm của hệ thống hoặc tin đã chuyển.
Hàm MPI_Recv
Ngăn chặn việc nhận tin
int MPI_Recv ( void * buffer, int count, MPI_Datatype datatype, int src,int tag, MPI_Comm comm, MPI_Status * status );
buffer Địa chỉ bắt đầu của vùng đệm nhận tin
count số maximum của mục dữ liệu trong bộ đệm nhận tin
datatype Dữ liệu của mỗi mục (uniform since it is an array); định nghĩa bởi một MPI constant
src Rank of source (integer)
Có thể chỉ rõ bởi một MPI_ANY_SOURCE để nhận tin từ bất cứ nguồn truyền tin nào.
Process rank trong trường hợp này có thể xác định thông qua statustag giá trị thẻ mong muốn(integer)
Có thể đặc tả bằng MPI_ANY_TAG
Thẻ được nhận có thể xác định thông qua status comm Communicator; nhóm của các tiến trình tham gia vào chức năng giao tiếp này.
Đối tượng tình trạng phải được chỉ định trước khi gọi tới MPI_Recv
Chặn cho tới khi tin được nhận, hoặc tới khi có lỗi điều kiện khiến hàm trả về count chứa độ dài lớn nhất của tin. Độ dài thực tế của việc nhận tin có thể được xác định với MPI_Get_count
Status chứa thông tin về hàm vừa hoàn thành
status->MPI source Rank of the process sending message status->MPI tag Message’s tag value
Deadlock
Process bị chặn trên một điều kiện mà sẽ chẳng bao giờ hiện thực Dễ dàng viết code gửi/nhận mà nó bế tắc.
Cả hai nhận được trước khi gửi float a[2], avg;
int me; // Process rank MPI_Status status; ...
int other = 1 - me;
MPI_Recv ( a + other, 1, MPI_FLOAT, other, 0, MPI_COMM_WORLD, &status );
MPI_Send ( a + me, 1, MPI_FLOAT, other, 0, MPI_COMM_WORLD ); avg = ( a[0] + a[1] ) / 2.0;
Process 0 chặn bên trong MPI_Recv đợi tin từ process 1 tới Process 1 chặn bên trong MPI_Recv đợi tin từ process 0 tới Cả hai đều bế tắc
float a[2], avg;
int me; // Process rank MPI_Status status; ...
int other = 1 - me;
MPI_Isend ( a + me, 1, MPI_FLOAT, other, me, MPI_COMM_WORLD, &request );
MPI_Recv ( a + other, 1, MPI_FLOAT, other, me, MPI_COMM_WORLD, &status );
avg = ( a[0] + a[1] ) / 2.0;
Các process bị chặn trong MPI_Recv bởi thẻ không đúng
Các vấn đề khác có thẻ sai đích trong khi gửi hoặc sai nguồn trong khi nhận.
III. Phân tích và so sánh độ phức tạp
1.Thuật toán tuần tự
Khi sử dụng thuật toán tuần tự, bài toán có độ phức tạp là: O(
2.Thuật toán song song
Phân tích: Độ phức tạp của bài toán khi sử dụng thuật toán song song:
Vòng lặp trong cùng có độ phức tạp là O
Vòng lặp ở giữa thực thi tối đa lần dựa trên phương pháp phân rã row-wise block triped
Tổng độ phức tạp của thuật toán là
Độ phức tạp của việc giao tiếp
Không có vòng lặp nào bên trong Không có vòng lặp giữa
Truyền ở vòng lặp bên ngoài
Thông qua một tin nhắn đơn có độ dài từ PE (Processing Element) đến PE khác có độ phức tạp O .
- Việc truyền tin tới PEs đòi hỏi bước chuyển tin .
Độ phức tạp của việc truyền tin Vòng lặp ngoài cùng
Với mỗi vòng lặp ngoài cùng, thuật toán song song phải tính toán gốc PE Những bản sao đúng trên của một dòng gốc PE của A tới mảng tmp, mất
khoảng O(n) lần
Vòng lặp thực thi n lần
Tổng độ phức tạp
Tính speedup và Efficiency của thuật toán
Ta có công thức tính speed up
Như vậy: Thuật giải tuần tự có độ phức tạp theo thời gian là Ts(n).[2] Thuật giải song song trên P bộ xử lý có độ phức tạp là Tp(n).[2]
Tóm lại:
Trong môi trường đã xây dựng như đề cập ở phần 1 thì với bài toán tìm kiếm một phần tử trong một mảng số thực, ta nhận thấy thuật toán tuần tự luôn cho một tốc độ thực hiện chương trình nhanh hơn khi kích thước mảng nhỏ, vì không phải mất thời gian giao tiếp giữa các process như thuật giải song song.
Tuy nhiên đối với mảng dữ liệu số thực có kích thước lớn thì thuật giải song song lại chiếm ưu thế tuyệt đối so với thuật toán tuần tự. Vì lúc này thời
gian thi hành và tính toán của thuật toán tuần tự là nhiều hơn nhiều so với khi chạy song song, các công việc được chia nhỏ ra và làm cùng lúc ở các process, chính vì thế thời gian thi hành và tính toán cũng được giảm xuống đáng kể.
Chúng ta dễ nhận thấy là độ phức tạp của xử lý song song có thể sẽ lớn hơn xử lý tuần tự rất nhiều, bởi vì cần có sự trao đổi thông tin và sự đồng bộ các tiến trình trong quá trình thực hiện xử lý bài toán. Vì thế, vấn đề chúng ta cần quan tâm ở đây chính là thời gian thực hiện chương trình. Một trong những mục đích chính
của xử lý song song là nghiên cứu, xây dựng những thuật toán thích hợp để cài đặt trên các máy tính song song, nghĩa là phát triển các thuật toán song song nhằm giải quyết các bài toán đặt ra trong thực tế.
IV. Cài đặt chương trình
Để cài đặt PVM trên hệ điều hành LINUX thì trước tiên ta phải cấu hình được hệ thống. Cấu hình trên 2 node Master và Slave.
Với cả 2 node:
Tạo một user bất kỳ dùng chung cho 2 node Master và Slave. Khởi động và tắt các dịch vụ không cần thiết của hệ thống, tắt FireWall,…bằng lệnh setup.
Đặt hostname và IP cho các node trong tập tin /etc/hosts để các node ssh vào nhau thông qua hostname của 2 máy thay cho địa chỉ IP.
Chỉnh sửa tập tin /etc/hosts.equiv để kết nối rsh vào nhau mà không yêu cầu mật khẩu (PVM yêu cầu cần phải có file này). Nếu chưa có file này thì cần phải tạo mới bằng trình vi. Nội dung của file này là địa chỉ IP của 2 máy.
Tạo tập tin ẩn .rhosts trên thư mục $HOME của user. Tập tin này cũng cho phép rsh giữa các node không cần mật khẩu và nội dung của file này bao gồm hostname của các node.
Thêm các lệnh rsh, rexec, rlogin vào cuối tập tin /etc/securetty. Đối với máy master:
Đặt lại địa chỉ IP tĩnh như trong khai báo của tập tin hosts cho node master. Đặt lại tên máy master trong tập tin /etc/sysconfig/network.
Cấu hình lại tập tin /etc/exports để tạo thư mục dùng chung cho hệ thống. Cụ thể node Master sẽ export thư mục home dùng chung, khi đó node Slave sẽ mount được các thư mục có trong /home của Master. Thêm các dòng lệnh vào cuối file /etc/exports:
/home 192.168.1.0/255.255.255.0(rw)
Tạo key và cấp quyền cho ssh trên thư mục $HOME của user: $ssh-keygen –t rsa $cd $HOME/.ssh
Đối với máy Slave:
Đặt lại địa chỉ IP và tên máy.
Cấu hình tập tin /etc/fstab để mount thư mục public trên node Master về. Cụ thể, thêm dòng lệnh vào cuối file /etc/fstab:
master:/home /home nfs rw 0 0 Cài đặt PVM
Sau khi tải PVM về cần đặt biến môi trường để PVM có thể chạy được. Biên dịch và chạy thử:
Khởi động PVM bằng lệnh $PVM, sau đó add host slave vào và chạy chương trình. Vào thư mục $HOME/pvm3/examples và biên dịch file master1.c và slave1.c:
$cd $HOME/pvm3/examples $aimk master1 slave1
Kết quả file master và worker được tạo trong thư mục $PVM_ROOT/bin/LINUX. Vào thư mục này và thực thi file master1.
$cd $PVM_ROOT/bin/LINUX
$./master1 và xuất kết quả bài toán. Giải quyết thuật toán:
- Số tiến trình của bài toán sẽ gấp 3 lần số máy tham gia hệ thống. - Máy Master có nhiệm vụ gửi dữ liệu cho các máy Slave để tính toán. - Máy Master nhận kết quả từ các máy Slave.
- In ra kết quả của từng máy và định danh của các máy đó. Song song hóa giải thuật tuần tự:
Sử dụng hệ thống gồm NPROCS bộ xử lý, ở đây NPROCS = 3 * nhost. Khi đó mỗi bộ xử lý sẽ phải tính kết quả theo công thức và trả kết quả về máy master.
Máy chủ sẽ tổng hợp và in ra màn hình các kết quả. Code chương trình:
/* File: mpi_floyd.c */
#include<stdio.h>
#include<stdlib.h>
#include<mpi.h>
constint INFINITY = 1000000;
void Read_matrix(int local_mat[], int n,int*temp_mat, int my_rank, int p, MPI_Comm comm);
void Print_matrix(int local_mat[], int n, int my_rank, int p, MPI_Comm comm); void Floyd(int local_mat[], int n, int my_rank, int p, MPI_Comm comm);
int Owner(int k, int p, int n);
void Copy_row(int local_mat[], int n, int p, int row_k[], int k); void Print_row(int local_mat[], int n, int my_rank, int i);
/*
int docmatranke(char tenfile[100],int*n,int a[]) { FILE*f; int i,j; f=fopen(tenfile,"rt"); if(f==NULL) {
printf("khong mo duoc file.\n"); return 0; } fscanf(f,"%d",n); for(i=0;i<(*n);i++) { for(j=0;j<(*n);j++) { fscanf(f,"%d",&(a[i*n+j])); } } fclose(f); return 1; } */
int main(int argc, char* argv[]) { int n,*temp_mat; int* local_mat; MPI_Comm comm; int p, my_rank; FILE*f; int i,j; MPI_Init(&argc, &argv); comm = MPI_COMM_WORLD;
MPI_Comm_size(comm, &p); MPI_Comm_rank(comm, &my_rank); if (my_rank == 0) { // docmatranke("data.txt",&n,temp_mat); f=fopen("data.txt","rt"); if(f==NULL) {
printf("khong mo duoc file.\n"); }
fscanf(f,"%d",&n);
temp_mat=(int*)malloc(n*n*sizeof(int)); for(i=0;i<n;i++) { for(j=0;j<n;j++) { fscanf(f,"%d",&(temp_mat[i*n+j])); } } fclose(f); }
MPI_Bcast(&n, 1, MPI_INT, 0, comm); local_mat = malloc(n*n/p*sizeof(int));
if (my_rank == 0) printf("Enter the local_matrix\n");
Read_matrix(local_mat, n,temp_mat, my_rank, p, comm); if (my_rank == 0) printf("We got\n");
Print_matrix(local_mat, n, my_rank, p, comm); if (my_rank == 0) printf("\n");
double start=MPI_Wtime(),end;
Floyd(local_mat, n, my_rank, p, comm); end=MPI_Wtime();
if (my_rank == 0) printf("The solution is:\n"); Print_matrix(local_mat, n, my_rank, p, comm);
printf("\n Tien trinh %d Thoi gian chay chuong trinh: %lf",my_rank,end-start); free(local_mat); MPI_Finalize(); return 0; } /* main */ /*---
* Function: Read_matrix
* Purpose: Read in the local_matrix on process 0 and scatter it using a * block row distribution among the processes.
* In args: All except local_mat * Out arg: local_mat
*/
void Read_matrix(int local_mat[], int n,int*temp_mat, int my_rank, int p, MPI_Comm comm) {
if (my_rank == 0) {
MPI_Scatter(temp_mat, n*n/p, MPI_INT, local_mat, n*n/p, MPI_INT, 0, comm); } else {
MPI_Scatter(temp_mat, n*n/p, MPI_INT, local_mat, n*n/p, MPI_INT, 0, comm); }
} /* Read_matrix */
/*--- * Function: Print_row
* Purpose: Convert a row of the matrix to a string and then print * the string. Primarily for debugging: the single string * is less likely to be corrupted when multiple processes * are attempting to print.
* In args: All */
void Print_row(int local_mat[], int n, int my_rank, int i){ char char_int[100]; char char_row[1000]; int j, offset = 0; for (j = 0; j < n; j++) { if (local_mat[i*n + j] == INFINITY) sprintf(char_int, "i "); else sprintf(char_int, "%d ", local_mat[i*n + j]); sprintf(char_row + offset, "%s", char_int); offset += strlen(char_int);
}
printf("Proc %d > row %d = %s\n", my_rank, i, char_row); } /* Print_row */
* Function: Print_matrix
* Purpose: Gather the distributed matrix onto process 0 and print it. * In args: All
*/
void Print_matrix(int local_mat[], int n, int my_rank, int p, MPI_Comm comm) {
int i, j;
int* temp_mat = NULL; if (my_rank == 0) {
temp_mat = malloc(n*n*sizeof(int)); MPI_Gather(local_mat, n*n/p, MPI_INT, temp_mat, n*n/p, MPI_INT, 0, comm); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) if (temp_mat[i*n+j] == INFINITY) printf("i "); else printf("%d ", temp_mat[i*n+j]); printf("\n"); } free(temp_mat); } else { MPI_Gather(local_mat, n*n/p, MPI_INT, temp_mat, n*n/p, MPI_INT, 0, comm); }
} /* Print_matrix */
/*--- * Function: Floyd
* Purpose: Implement a distributed version of Floyd's algorithm for * finding the shortest path between all pairs of vertices. * The adjacency matrix is distributed by block rows. * In args: All except local_mat
* In/out arg: local_mat: on input the adjacency matrix. On output * the matrix of lowests costs between all pairs of
* vertices */
void Floyd(int local_mat[], int n, int my_rank, int p, MPI_Comm comm) { int global_k, local_i, global_j, temp;
int root;
for (global_k = 0; global_k < n; global_k++) { root = Owner(global_k, p, n);
if (my_rank == root)
Copy_row(local_mat, n, p, row_k, global_k); MPI_Bcast(row_k, n, MPI_INT, root, comm); for (local_i = 0; local_i < n/p; local_i++) for (global_j = 0; global_j < n; global_j++) {
temp = local_mat[local_i*n + global_k] + row_k[global_j]; if (temp < local_mat[local_i*n+global_j])
local_mat[local_i*n + global_j] = temp; } } free(row_k); } /* Floyd */ /*--- * Function: Owner
* Purpose: Return rank of process that owns global row k * In args: All
*/
int Owner(int k, int p, int n) {
return k/(n/p);
} /* Owner */
/*--- * Function: Copy_row
* Purpose: Copy the row with *global* subscript k into row_k * In args: All except row_k
* Out arg: row_k */
void Copy_row(int local_mat[], int n, int p, int row_k[], int k) { int j;
int local_k = k % (n/p); for (j = 0; j < n; j++)
row_k[j] = local_mat[local_k*n + j]; } /* Copy_row */
Khi chạy thử nghiệm chương trình, ta có thể kết luận là tùy vào bài toán như thế nào mà việc xác định số lượng process thực hiện công việc là khác nhau để công việc có thể thực hiện tối ưu với chi phí thời gian là nhỏ nhất.
Bên cạnh đó tốc độ thực hiện chương trình còn phụ thuộc nhiều vào các yếu tố khác như cấu hình máy, đường truyền kết nối giữa các máy.
Vấn đề đặt ra là là đánh giá một thuật toán song song như thế nào được gọi là thích hợp? Trong thuật toán tuần tự thì chúng ta đánh giá dựa vào thời gian thực hiện thuật toán, không gian bộ nhớ và khả năng lập trình….
Đánh giá thuật toán song song thì phức tạp hơn nhiều, ngoài những tiêu