我使用MPI编写一个小程序来并行化矩阵 - 矩阵乘法。问题是:在我的计算机上运行程序时,完成大约需要10秒钟,但群集大约需要75秒。我想我有一些同步问题,但我无法弄清楚(还)。
这是我的源代码:
/*matrix.c
mpicc -o out matrix.c
mpirun -np 11 out
*/
#include <mpi.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 1000
#define DATA_TAG 10
#define B_SENT_TAG 20
#define FINISH_TAG 30
int master(int);
int worker(int, int);
int main(int argc, char **argv) {
int myrank, p;
double s_time, f_time;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
MPI_Comm_size(MPI_COMM_WORLD, &p);
if (myrank == 0) {
s_time = MPI_Wtime();
master(p);
f_time = MPI_Wtime();
printf("Complete in %1.2f seconds\n", f_time - s_time);
fflush(stdout);
}
else {
worker(myrank, p);
}
MPI_Finalize();
return 0;
}
int *read_matrix_row();
int *read_matrix_col();
int send_row(int *, int);
int recv_row(int *, int, MPI_Status *);
int send_tag(int, int);
int write_matrix(int *);
int master(int p) {
MPI_Status status;
int *a; int *b;
int *c = (int *)malloc(N * sizeof(int));
int i, j; int num_of_finish_row = 0;
while (1) {
for (i = 1; i < p; i++) {
a = read_matrix_row();
b = read_matrix_col();
send_row(a, i);
send_row(b, i);
//printf("Master - Send data to worker %d\n", i);fflush(stdout);
}
wait();
for (i = 1; i < N / (p - 1); i++) {
for (j = 1; j < p; j++) {
//printf("Master - Send next row to worker[%d]\n", j);fflush(stdout);
b = read_matrix_col();
send_row(b, j);
}
}
for (i = 1; i < p; i++) {
//printf("Master - Announce all row of B sent to worker[%d]\n", i);fflush(stdout);
send_tag(i, B_SENT_TAG);
}
//MPI_Barrier(MPI_COMM_WORLD);
for (i = 1; i < p; i++) {
recv_row(c, MPI_ANY_SOURCE, &status);
//printf("Master - Receive result\n");fflush(stdout);
num_of_finish_row++;
}
//printf("Master - Finish %d rows\n", num_of_finish_row);fflush(stdout);
if (num_of_finish_row >= N)
break;
}
//printf("Master - Finish multiply two matrix\n");fflush(stdout);
for (i = 1; i < p; i++) {
send_tag(i, FINISH_TAG);
}
//write_matrix(c);
return 0;
}
int worker(int myrank, int p) {
int *a = (int *)malloc(N * sizeof(int));
int *b = (int *)malloc(N * sizeof(int));
int *c = (int *)malloc(N * sizeof(int));
int i;
for (i = 0; i < N; i++) {
c[i] = 0;
}
MPI_Status status;
int next = (myrank == (p - 1)) ? 1 : myrank + 1;
int prev = (myrank == 1) ? p - 1 : myrank - 1;
while (1) {
recv_row(a, 0, &status);
if (status.MPI_TAG == FINISH_TAG)
break;
recv_row(b, 0, &status);
wait();
//printf("Worker[%d] - Receive data from master\n", myrank);fflush(stdout);
while (1) {
for (i = 1; i < p; i++) {
//printf("Worker[%d] - Start calculation\n", myrank);fflush(stdout);
calc(c, a, b);
//printf("Worker[%d] - Exchange data with %d, %d\n", myrank, next, prev);fflush(stdout);
exchange(b, next, prev);
}
//printf("Worker %d- Request for more B's row\n", myrank);fflush(stdout);
recv_row(b, 0, &status);
//printf("Worker %d - Receive tag %d\n", myrank, status.MPI_TAG);fflush(stdout);
if (status.MPI_TAG == B_SENT_TAG) {
break;
//printf("Worker[%d] - Finish calc one row\n", myrank);fflush(stdout);
}
}
//wait();
//printf("Worker %d - Send result\n", myrank);fflush(stdout);
send_row(c, 0);
for (i = 0; i < N; i++) {
c[i] = 0;
}
}
return 0;
}
int *read_matrix_row() {
int *row = (int *)malloc(N * sizeof(int));
int i;
for (i = 0; i < N; i++) {
row[i] = 1;
}
return row;
}
int *read_matrix_col() {
int *col = (int *)malloc(N * sizeof(int));
int i;
for (i = 0; i < N; i++) {
col[i] = 1;
}
return col;
}
int send_row(int *row, int dest) {
MPI_Send(row, N, MPI_INT, dest, DATA_TAG, MPI_COMM_WORLD);
return 0;
}
int recv_row(int *row, int src, MPI_Status *status) {
MPI_Recv(row, N, MPI_INT, src, MPI_ANY_TAG, MPI_COMM_WORLD, status);
return 0;
}
int wait() {
MPI_Barrier(MPI_COMM_WORLD);
return 0;
}
int calc(int *c_row, int *a_row, int *b_row) {
int i;
for (i = 0; i < N; i++) {
c_row[i] = c_row[i] + a_row[i] * b_row[i];
//printf("%d ", c_row[i]);
}
//printf("\n");fflush(stdout);
return 0;
}
int exchange(int *row, int next, int prev) {
MPI_Request request; MPI_Status status;
MPI_Isend(row, N, MPI_INT, next, DATA_TAG, MPI_COMM_WORLD, &request);
MPI_Irecv(row, N, MPI_INT, prev, MPI_ANY_TAG, MPI_COMM_WORLD, &request);
MPI_Wait(&request, &status);
return 0;
}
int send_tag(int worker, int tag) {
MPI_Send(0, 0, MPI_INT, worker, tag, MPI_COMM_WORLD);
return 0;
}
int write_matrix(int *matrix) {
int i;
for (i = 0; i < N; i++) {
printf("%d ", matrix[i]);
}
printf("\n");
fflush(stdout);
return 0;
}
答案 0 :(得分:3)
嗯,你有一个相当小的矩阵(N = 1000),其次你在行/列的基础上分配算法而不是阻塞。
对于使用更好算法的更现实的版本,您可能希望获得优化的BLAS库(例如GOTO是免费的),使用该算法测试单线程性能,然后获取PBLAS并将其链接到优化的BLAS,并进行比较使用PBLAS版本的MPI并行性能。
答案 1 :(得分:2)
我在你的程序中看到了一些错误:
首先,为什么要调用wait函数,因为它的实现只是调用MPI_Barrier
。 MPI_Barrier
是一种原始同步,它阻止所有线程,直到它们通过调用MPI_Barrier
到达“障碍”。我的问题是:你想让主人与工人同步吗?在这种情况下,这不是最佳的,因为工人不需要等待主人开始计算。
其次,有两个不必要的循环。
for (i = 1; i < N / (p - 1); i++) {
for (j = 1; j < p; j++) {
b = read_matrix_col();
send_row(b, j);
}
}
for (i = 1; i < p; i++) {
send_tag(i, B_SENT_TAG);
}
在第一个i循环中,不要在语句中使用该变量。由于j循环和第二个i循环是相同的,你可以这样做:
for (i = 0; i < p; i++) {
b = read_matrix_col();
send_row(b, j);
send_tag(i, B_SENT_TAG);
}
在数据传输方面,您的程序未进行优化,因为您要为每次数据传输发送1000个数据整数数组。应该有一种更好的方法来优化数据传输,但我会让你看看它。所以,我告诉你,并告诉我们你的新表现是什么。
正如@janneb所说,你可以使用BLAS库来获得更好的矩阵乘法性能。祝你好运!
答案 2 :(得分:1)
我没有查看您的代码,但我可以提供一些提示,说明为什么您的结果可能不会出乎意料:
如前所述,N = 1000可能太小。您应该进行更多测试以查看程序的可伸缩性(尝试设置N = 100,500,1000,5000,10000等)并比较系统和群集上的结果。
比较您的系统(我假设的一个处理器)和群集上的单个处理器之间的结果。通常在服务器或集群等生产环境中,单个处理器的功能不如桌面使用的最佳处理器,但它们提供稳定性,可靠性和其他功能,适用于全天24小时运行的环境。
如果您的处理器有多个核心,则可以同时运行多个MPI进程,并且与群集中节点之间的同步相比,它们之间的同步可以忽略不计。
群集中的节点是否已静态分配给您?也许其他用户的程序可以在您同时运行的节点上进行安排。
阅读有关群集架构的文档。某些架构可能更适合特定类型的问题。
评估群集网络的延迟。从每个节点到另一个节点进行多次ping操作并计算平均值可能会给出粗略估计。
最后但也许最重要的是,您的算法可能不是最佳的。阅读有关矩阵乘法的书籍(我可以推荐“矩阵计算”,Golub和Van Loan)。