关于MPI语义的简单测试

前一段时间实现了一个简单的MPI,现在通过对MPICH的功能进行测试,来验证我的实现中是否存在问题。

现对MPI标准中感觉叙述不是特别清楚的地方(估计是我没有仔细阅读造成的)进行一些简单的测试。

首先来看MPI_Comm_size与MPI_Comm_rank,在MPI_COMM_WORLD情况下结果比较明确。源码如下:

void MPI_test_worldranksize()
{
	int rank,size;
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    	MPI_Comm_size(MPI_COMM_WORLD, &size);
    	printf( "Hello world from process %d of %d\n", rank, size );
}

结果如下,没有什么问题

Hello world from process 0 of 4
Hello world from process 2 of 4
Hello world from process 1 of 4
Hello world from process 3 of 4

那么在通信子分割的情况下会出现什么问题。这个通信子确实挺讨厌的,它一分割会造成许多不太明确的地方。

一个通信子由两部分进行标记,一个是color值,一个是key值,也就是当前进程在某个通信子中的rank值,首先来看key值唯一的情况(也就是key与key之间不相同)

void MPI_comm_ranksize()
{
	int oldrank,oldsize;
	int newrank,newsize;
	MPI_Comm newcomm;

	MPI_Comm_rank(MPI_COMM_WORLD, &oldrank);
    	MPI_Comm_size(MPI_COMM_WORLD, &oldsize);

	MPI_Comm_split(MPI_COMM_WORLD,oldrank%2,oldrank,&newcomm);

	MPI_Comm_rank(newcomm, &newrank);
    	MPI_Comm_size(newcomm, &newsize);

	printf( "old rank = %d size = %d, color = %d , new rank = %d size = %d\n", oldrank,oldsize,oldrank%2,newrank,newsize);
}

以上程序是将全局通信子根据rank值的单、双分割成两个comm的测试程序,这里要注意的一点是,即使rank值是唯一的,在新的通信子中rank也会从0开始重新编号

程序运行结果如下:

old rank = 0 size = 4, color = 0 , new rank = 0 size = 2
old rank = 2 size = 4, color = 0 , new rank = 1 size = 2 new rank不是2
old rank = 1 size = 4, color = 1 , new rank = 0 size = 2
old rank = 3 size = 4, color = 1 , new rank = 1 size = 2
具有相同的value值,运行结果相同。

再来看看发送与接收,正常情况的咱们先不讨论了,先来看看通信子分割的情况,现在发现通信子分割的麻烦之处了吧。

首先将通信子分割成不同的key值,然后使用这个key值进行发送或接收,看看程序是否能够正常运行,程序源码如下:

void MPI_same_sendrecv()
{
	char greeting[MAX_STRING];
	
	int oldrank,oldsize;
	int newrank,newsize;
	MPI_Comm newcomm;

	MPI_Comm_rank(MPI_COMM_WORLD, &oldrank);
    	MPI_Comm_size(MPI_COMM_WORLD, &oldsize);

	MPI_Comm_split(MPI_COMM_WORLD,oldrank%2,oldrank,&newcomm);

	MPI_Comm_rank(newcomm, &newrank);
    	MPI_Comm_size(newcomm, &newsize);

	if(0==oldrank){
		sprintf( greeting , "old rank = %d old size = %d,new rank = %d size = %d\n" , oldrank , oldsize , newrank , newsize);
		MPI_Send(greeting,strlen(greeting)+1,MPI_CHAR,2,0,newcomm);
	}else if(1==oldrank){
		sprintf( greeting , "old rank = %d old size = %d,new rank = %d size = %d\n" , oldrank , oldsize , newrank , newsize);
		MPI_Send(greeting,strlen(greeting)+1,MPI_CHAR,3,0,newcomm);
	}else if(2==oldrank){
		MPI_Recv(greeting,MAX_STRING,MPI_CHAR,0,0,newcomm,MPI_STATUS_IGNORE);
		printf("%s\n",greeting);
	}else{
		MPI_Recv(greeting,MAX_STRING,MPI_CHAR,0,0,newcomm,MPI_STATUS_IGNORE);
		printf("%s\n",greeting);
	}

	MPI_Comm_free(&newcomm);
}

运行结果:程序直接就返回了,由于source进程不存在,因此程序直接返回了。这里可以得到一个比较明确的结论就是:如果source进程不存在,则recv函数直接返回。

将以上程序稍改,目标进程都改为“1”,则程序可以正常运行,结果如下:

old rank = 0 old size = 4,new rank = 0 size = 2
old rank = 1 old size = 4,new rank = 0 size = 2

若设为相同的key值,结果相同。

这里要总结的一点是:无论key值为何,在当前comm中rank值都是从“0”开始编号的。old rank只是起到一个表明前后相对顺序的作用。

再来看看send、recv函数,主要就是特殊情况。

首先是自己发送、自己接收,源码如下:

void MPI_sendrecv_self()
{
	char greeting[MAX_STRING];

	int oldrank,oldsize;
	MPI_Comm_rank(MPI_COMM_WORLD, &oldrank);
	MPI_Comm_size(MPI_COMM_WORLD, &oldsize);


	if(oldrank==0){
		sprintf( greeting , "old rank = %d old size = %d\n" , oldrank , oldsize);
		MPI_Send(greeting,strlen(greeting)+1,MPI_CHAR,0,0,MPI_COMM_WORLD);
		printf("%s\n",greeting);
		MPI_Recv(greeting,MAX_STRING,MPI_CHAR,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
	}
}

程序运行结果:printf函数都没有被调用,看来MPI_Send函数都没有返回,原因为何,没分析过源代码,也没有什么发言权。改为MPI_COMM_SELF情况相同,看来在HPCC中应该不存在自己给自己发的情况。使用Ssend,在MPI_COMM_SELF与MPI_COMM_WORLD的情况下,程序都是无限阻塞。

改为ISend,源码如下:

void MPI_Isendrecv_self()
{
	char greeting[MAX_STRING];
	MPI_Request req;

	int oldrank,oldsize;
	MPI_Comm_rank(MPI_COMM_WORLD, &oldrank);
	MPI_Comm_size(MPI_COMM_WORLD, &oldsize);


	if(oldrank==0){
		sprintf( greeting , "old rank = %d old size = %d\n" , oldrank , oldsize);
		MPI_Isend(greeting,strlen(greeting)+1,MPI_CHAR,0,0,MPI_COMM_WORLD,&req);
		printf("send has done\n");
		MPI_Recv(greeting,MAX_STRING,MPI_CHAR,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
		printf("%s\n",greeting);
	}
}

运行结果如下:

send has done
Assertion failed in file /home/andywang/mpich-3.2/src/mpid/ch3/src/ch3u_buffer.c at line 77: FALSE
memcpy argument memory ranges overlap, dst_=0x7ffee5701c20 src_=0x7ffee5701c20 len_=27

internal ABORT - process 0

发生memcpy错误的原因可能是被发送的内容还没有到达接收缓冲区,所以加上一个wait试试,程序源码如下:

void MPI_Isendrecv_self()
{
	char greeting[MAX_STRING];
	MPI_Request req;

	int oldrank,oldsize;
	MPI_Comm_rank(MPI_COMM_WORLD, &oldrank);
	MPI_Comm_size(MPI_COMM_WORLD, &oldsize);


	if(oldrank==0){
		sprintf( greeting , "old rank = %d old size = %d\n" , oldrank , oldsize);
		MPI_Isend(greeting,strlen(greeting)+1,MPI_CHAR,0,0,MPI_COMM_WORLD,&req);
		printf("send has done\n");
		MPI_Wait(&req,MPI_STATUS_IGNORE);
		printf("wait has done\n");
		MPI_Recv(greeting,MAX_STRING,MPI_CHAR,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
		printf("%s\n",greeting);
	}
}

先在send后加上一个wait,目标是保证send能够被发出去。但程序的运行结果与预期不同,send返回后,wait函数并没有返回,程序再次进入阻塞状态。MPI_COMM_SELF是同样的结果。

针对上述发生memcpy的程序,进行简单的修改,使用两个不同的缓冲区,程序源码如下:

void MPI_Isendrecv_diffbuf_self()
{
	char greeting[MAX_STRING];
	char recvbuf[MAX_STRING];
	MPI_Request req;

	int oldrank,oldsize;
	MPI_Comm_rank(MPI_COMM_WORLD, &oldrank);
	MPI_Comm_size(MPI_COMM_WORLD, &oldsize);


	if(oldrank==0){
		sprintf( greeting , "old rank = %d old size = %d\n" , oldrank , oldsize);
		MPI_Isend(greeting,strlen(greeting)+1,MPI_CHAR,0,0,MPI_COMM_WORLD,&req);
		printf("send has done\n");
		MPI_Recv(recvbuf,MAX_STRING,MPI_CHAR,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
		printf("%s\n",recvbuf);
	}
}

结果就可以正常的输出了:

send has done
old rank = 0 old size = 4


顺着这个思路,在wait后面也使用不同的缓冲区,结果程序仍然会阻塞。

对MPI_Send与MPI_Recv的发送程序使用不同的缓冲区,MPI_Send函数都无法返回。

好了,send这一方面做了一些简单的测试,再来看看recv的变化能给程序带来什么变化。

首先是MPI_Send 与MPI_Irecv 配合使用,使用相同的缓冲区先尝尝咸淡,源码如下:

void MPI_sendIrecv_self()
{
	char greeting[MAX_STRING];

	int oldrank,oldsize;
	MPI_Comm_rank(MPI_COMM_WORLD, &oldrank);
	MPI_Comm_size(MPI_COMM_WORLD, &oldsize);


	if(oldrank==0){
		sprintf( greeting , "old rank = %d old size = %d\n" , oldrank , oldsize);
		MPI_Send(greeting,strlen(greeting)+1,MPI_CHAR,0,0,MPI_COMM_WORLD);
		printf("%s",greeting);
		MPI_Irecv(greeting,MAX_STRING,MPI_CHAR,0,0,MPI_COMM_WORLD,NULL);
		printf("%s",greeting);
	}
}

程序的运行结果还是一样的,MPI_Send函数都没有返回。

这里我打算从send的语义出发解释这一现象,但没有找到什么可以说服自己的解释,不过有一点比较明确,send函数没有返回,说明greeting还无法被使用,至于为什么greeting无法被使用,我还没有想到问题的答案。

再来实验一下使用不同缓冲区的情况,程序运行结果:还是阻塞,无法继续运行。

不测试在recv后添加wait的情况了,因为send都无法返回。

好了,可以试试Isend与Irecv配合的情况了,先来最简单的情况,程序源码如下:

void MPI_IsendIrecv_self()
{
	char greeting[MAX_STRING];

	int oldrank,oldsize;
	MPI_Comm_rank(MPI_COMM_WORLD, &oldrank);
	MPI_Comm_size(MPI_COMM_WORLD, &oldsize);


	if(oldrank==0){
		sprintf( greeting , "old rank = %d old size = %d\n" , oldrank , oldsize);
		MPI_Isend(greeting,strlen(greeting)+1,MPI_CHAR,0,0,MPI_COMM_WORLD,NULL);
		printf("%s",greeting);
		MPI_Irecv(greeting,MAX_STRING,MPI_CHAR,0,0,MPI_COMM_WORLD,NULL);
		printf("%s",greeting);
	}
}
首先设置request为NULL,结果出现如下错误:

Fatal error in PMPI_Isend: Invalid argument, error stack:
PMPI_Isend(149): MPI_Isend(buf=0x7ffe74377800, count=27, MPI_CHAR, dest=0, tag=0, MPI_COMM_WORLD, request=(nil)) failed
PMPI_Isend(99).: Null pointer in parameter request
好像request不能为空,先把Isend改了试试,结果又出现了相同的错误,不过是在MPI_Recv 函数中,所以都加上,程序运行结果如下:

old rank = 0 old size = 4
Assertion failed in file /home/andywang/mpich-3.2/src/mpid/ch3/src/ch3u_buffer.c at line 77: FALSE
memcpy argument memory ranges overlap, dst_=0x7fffce3d8bd0 src_=0x7fffce3d8bd0 len_=27

internal ABORT - process 0

感觉有点门了,换成不同的缓冲区试试:好了,程序可以正常运行了,结果如下:

old rank = 0 old size = 4
old rank = 0 old size = 4

此处并没有使用wait函数。

这里要总结一下自己给自己发送并能成功接收的两种情况是

  1. MPI_Isend与MPI_recv配合
  2. MPI_Isend与MPI_Irecv配合

注意接收缓冲区与发送缓冲区需要是不同的缓冲区。

好了最后来看看,MPI_Sendrecv函数,测试源码如下:

void MPI_sendrecv_function()
{
	char send[MAX_STRING];
	char recv[MAX_STRING];
	int comm_sz;
	int my_rank;

	MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
	MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);

	sprintf(send,"Greeting from process %d of %d",my_rank,comm_sz);
	printf("send : %s\n",send);

	
	if(my_rank==0)
		MPI_Sendrecv(send,strlen(send)+1,MPI_CHAR,0,0,recv,MAX_STRING,MPI_CHAR,0,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);


	printf("%s\n",recv);

}

程序可以正常运行,通过实验可以发现,sendrecv可以给自己发送数据。在来点极端的情况,使用一个缓冲区,程序运行结果如下:

send : Greeting from process 0 of 1
Assertion failed in file /home/andywang/mpich-3.2/src/mpid/ch3/src/ch3u_buffer.c at line 77: FALSE
memcpy argument memory ranges overlap, dst_=0x7ffd5e31aeb0 src_=0x7ffd5e31aeb0 len_=29

internal ABORT - process 0

同一个缓冲区不行,好,针对上述问题,再补充一点,sendrecv使用不同的缓冲区同样可以给自己发送。





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值