【MPI】使用非阻塞Send/Recv实现并行折叠整数和
1. 实验要求
本次实验需采用非阻塞式编程实现Master和Worker之间的通信。要求如下:
⑴.Master进程被动等待Worker进程发来数据量请求和上次计算结果。
⑵. Master进程将结果叠加至Result中,并根据数据请求量进行数据分段:
a.当有数据可分时且数据量满足Worker请求时,将请求量大小的数据段分给该请求进程Worker;
b. 当有数据可分但数据量不足够大不满足Worker请求,Master向该Worker发送退出信号;
c.当无数据可分,向该Worker发送退出信号;
⑶.Worker主动向Master请求数据,随机生成一个满足要求的随机数(例如大小在2~20之间)作为下次向Master请求的数据量,并将其与上次计算结果发送给Master;接受到Master发送来的数据进行叠加和或者退出。
2. 算法设计及代码实现
⑴.a.程序流程图
b.实验代码框架图
① 随机生成数RANNOM,生成满足区间(a,b)的整数值;
② buf_read()函数接受动态从键盘输入数据;
③ master()函数负责主进程Master的与Worker进程的通信,接受Worker的数据请求、任务分配和答案总和;
④ worker()函数负责进程Worker与主进程Master的通信,子任务的完成;
⑤ sum()函数为一次叠加和,将buf中长度为len的数据段进行叠加和。
⑵.实验主要代码
从键盘接受输入数据函数buf_read()和一次叠加和代码函数sum()与实验一代码一样,此处不再列出
①.当进程只有一个时,为串行计算,代码如下:
- if(numprocs==1)
- {
- result = sum(buffer,totalnum);
- etime = MPI_Wtime();
- printf("The result is %d.\n",result);
- printf("The sequential collapse takes %f seconds.\n",etime-stime);
- }
②.当进程数大于2时,主进程Master被动接受Worker的发送请求量和上次数据的叠加和等,处理过程如下:
- MPI_Request* reqs = (MPI_Request*)calloc(numprocs - 1, sizeof(MPI_Request));
- MPI_Status* stats = (MPI_Status*)calloc(numprocs - 1, sizeof(MPI_Status));
- int* indices = (int*)calloc((numprocs - 1), sizeof(int));
- int** lsum = (int **)calloc(numprocs - 1, sizeof(int *));
- int sendlen =0,hassend=0;
- for(i = 0; i < numprocs - 1; i++)
- {//初始化
- lsum[i] = (int *)calloc(2, sizeof(int));
- }
- count = numprocs - 1;
- allocatedJobs = numprocs - 1;
- for(i = 0; i < count; i++)
- {//初始化
- indices[i] = i;
- }
- flag = 1;
- for(i = 0; i < count; i++){//初始化
- MPI_Irecv(lsum[indices[i]], 2, MPI_INT, indices[i] + 1, 100, MPI_COMM_WORLD, reqs + indices[i]);
- }
- while(allocatedJobs||flag)
- {
- MPI_Waitsome(numprocs - 1,reqs, &count, indices, stats);
- allocatedJobs -=count;
- for(i = 0; i < count; i++)
- {
- sendlen = (lsum[indices[i]])[1];
- temp = (lsum[indices[i]])[0];
- result = (result + temp-1)%9+1;
- printf("Master--worker %d require %d datas and answer %d.\n",indices[i] + 1,sendlen,temp);
- if(read>=sendlen)
- {
- printf("Master--allocate %d datas to worker %d.\n",sendlen,indices[i] + 1);
- MPI_Isend(buffer+hassend,sendlen, MPI_CHAR, indices[i] + 1, 99, MPI_COMM_WORLD, &request);
- hassend += sendlen;
- read -=sendlen;
- MPI_Wait(&request, &status);
- MPI_Irecv(lsum[indices[i]], 2, MPI_INT, indices[i] + 1, 100, MPI_COMM_WORLD, reqs + indices[i]);
- allocatedJobs++;
- }
- else
- {//向后来的Worker发送退出信号
- MPI_Isend(buffer,0, MPI_CHAR, indices[i] + 1, 99, MPI_COMM_WORLD, &request);
- printf("Master--kill worker %d.\n",0,indices[i] + 1);
- MPI_Wait(&request, &status);
- flag = 0;
- }
- }
- }
- if(read!=0)
- {//剩余数据量不足以分配时,则由Master计算
- temp = sum(buffer+hassend,read);
- result = (result+temp-1)%9+1;
- }
③.Worker进程随机生成请求量的大小,主动向Master请求任务,主要代码实现 :
//初始化变量
- //初始化变量
- char buf[Block_SIZE];
- int result[2],recvlen,myid;
- MPI_Status status;
- MPI_Request request;
- MPI_Comm_rank(MPI_COMM_WORLD, &myid);
- result[0] = 0;//第一次计算结果设置为0
- result[1] = RANDOM(2,Block_SIZE); //生成随机请求量
- MPI_Isend(result, 2, MPI_INT, Master_Id, 100, MPI_COMM_WORLD, &request);
- MPI_Wait(&request, &status);
- while(1)
- {
- memset(buf, '\0', Block_SIZE);
- MPI_Irecv(buf, result[1], MPI_CHAR, 0, 99, MPI_COMM_WORLD, &request);
- MPI_Wait(&request, &status);
- MPI_Get_count(&status, MPI_CHAR, &recvlen);
- if(recvlen<=0){ //退出信号
- break;
- }
- printf("worker--%d receive %s from master.\n",myid,buf);
- result[0] = sum(buf,recvlen);
- result[1] = RANDOM(2,Block_SIZE); //生成随机请求量
- MPI_Isend(result, 2, MPI_INT,Master_Id, 100, MPI_COMM_WORLD, &request);
- MPI_Wait(&request, &status);
- }
3. 实验测试结果
①.当只有一个进程时,为串行计算,使用测试数据集100.tst和999.tst结果如下:
100.tst
999.tst
②. 设置最大请求量MaxSize=20bit:
a.当有两个进程时,一个为Master,一个为Worker,即只有一个计算进程,从结果中与①对比可以看出通信量给总体任务执行完的影响,使用测试数据集100.tst和999.tst结果如下:
100.tst
999.tst
b.输入进程数为5,查看Worker进程数量对计算时间的影响
100.tst
999.tst
③设置MaxSize = 50bit时,设置进程数为5时结果如下:
100.tst
999.tst
4. 性能分析
当设定每次Master向Worker发送的最大数据量为20bit时,改变进程数结果对比如下:
进程数 测设数据 |
1 |
2 |
5 |
10 |
20 |
100.tst |
0.000002 s |
0.004907 s |
0.009161s |
0.004244 s |
0.046982 s |
999.tst |
0.000015 s |
0.038625 s |
0.036455 s |
0.027891 s |
0.025322 s |
表格 1 进程数对性能的影响
固定总进程数为5时,Worker请求发送的最大数据量MaxSize对实验的影响,实验数据为执行时间为秒(s):
MaxSize 测设数据 |
10 bit |
20 bit |
50 bit |
100 bit |
100.tst |
0.004295 s |
0.009161s |
0.001424 s |
0.000965 s |
999.tst |
0. 044655s |
0.036455 s |
0.017649 s |
0.017052 s |
表格 2 请求量阈值的影响
实验分析:从表1中非阻塞式编程对试验数据进行测试时,当有1个进程时为串行计算,有2个进程时一个为Master,一个为Worker,但串行执行反而更快,执行时间更短,分析源于并行执行的通信时间过大,且每次Worker不是一次性请求数据,分为多次请求、多次通信,时间大部分都浪费在通信上。当为并行执行时,增加进程数并不一定会减少执行时间,表中对测试数据100.tst,从进程10增加到进程数为20个时,执行时间反而增大,100.tst数据量小,对于一次开启20个进程,即19个Worker时,都请求数据时,虽然请求数据量不同,但进程过多,将导致有部分Worker连一次数据都没有请求到,而Master却要为没有数据可分的Worker维护通信,故使执行时间增大。对于大数据量测试集999.tst,增加进程数从5到20,执行时间一直呈下降趋势,进程数多,并发度就越高,总体执行时间更小,故效率更高。对于表2,固定进程数,增大Worker每次能向Master请求数据量的上限,虽然是随机请求数据量,但时间一直呈下降趋势。故增大最大请求量阈值可提高计算性能。总体来看需要权衡需要开启的进程数,对于一定的数据处理量,恰当设置Worker每次请求的数据量的最大阈值对于并行度影响也比较大,最大程度的利用每个Worker且尽量增大每次Master向Worker发送的数据量可尽可能的利用最大资源,减少通信浪费的时间,总体提高效率。
转载于:https://blog.51cto.com/yang19890314/1118994