并行系统学习之路(二) ---- MPI热身

本篇将介绍一个很简单的MPI程序。程序的要求是:

  • 程序将统计一个信息经过一个由处理器组成的环所需要的时间(比如说,讲一个消息从一个处理器节点发送至下一个处理器节点,直到最后一个节点发送返回第一个节点)
  • 统计不同大小的消息的发送时间(包括32B, 64B, 128B, …, 2M)
  • 统计不同处理器数量所需的时间(2个,4个,8个)
  • 作图表示不同消息大小对应的往返时间以及标准差

程序的核心代码如下:

for(msg_size = MIN_SIZE; msg_size <= MAX_SIZE; msg_size *= 2) {
// Construct send and data
send_data = malloc(msg_size * sizeof(*send_data));
recv_data = malloc(msg_size * sizeof(*recv_data));
for(int j = 0; j < ITERATION_TIMES + EXTRA_ITERATION; j++) {
if(rank == MASTER) {
// master node send data and receive data from last node to finish a loop
loop_times[j] = -MPI_Wtime();
MPI_Send(send_data, msg_size, MPI_CHAR, 1,
0, MPI_COMM_WORLD);
MPI_Recv(recv_data, msg_size, MPI_CHAR, numproc - 1,
0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
loop_times[j] += MPI_Wtime();
}
else {
// other node receive data from the left node and send data to its right node
MPI_Recv(recv_data, msg_size, MPI_CHAR, (rank+numproc - 1) % numproc,
0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
MPI_Send(send_data, msg_size, MPI_CHAR, (rank+1)%numproc,
0, MPI_COMM_WORLD);
}
}
}

本程序使用的消息收发方法为blocking的MPISend和MPIRecv,基本逻辑为:

  • 生成发送消息
  • 如果节点为MASTER节点,则记录起始时间并向下一个节点发送消息,待接受到来自最后一个节点的消息时记录结束时间
  • 其他节点将在接收来自前一个节点的消息之后向下一节点发送消息

主逻辑结束后我们用python的matplotlib库画出统计图如下:

我们从图中可以得出以下四点结论:

  • RTT随消息大小的增加而增加,因为当消息大小变大时,发送和接收消息所需要的时间会增加。
  • 当处理器数量增加时RTT会增加,因为处理器数量的增加意味着传输环的长度也增加了,RTT时间也会因此增加。
  • 对于相同大小的消息,所有的节点对传输消息的时间并不相同,而这种差异将会随着消息大小的增加变的可以忽略,以下为当我们取节点6和节点7时点对点消息发送时间的测试数据:
    Size: 32 Average: 1.101494e-05 Stddev: 1.412710e-05
    Size: 64 Average: 6.222725e-06 Stddev: 8.691657e-07
    Size: 128 Average: 6.628036e-06 Stddev: 4.744470e-07
    Size: 256 Average: 8.368492e-06 Stddev: 6.348384e-07
    Size: 512 Average: 9.393692e-06 Stddev: 3.234067e-07
    Size: 1024 Average: 1.118183e-05 Stddev: 7.497876e-07
    Size: 2048 Average: 1.463890e-05 Stddev: 8.272816e-07
    Size: 4096 Average: 1.914501e-05 Stddev: 1.182758e-06
    Size: 8192 Average: 2.329350e-05 Stddev: 1.077114e-06
    Size: 16384 Average: 3.447533e-05 Stddev: 3.075877e-06
    Size: 32768 Average: 4.868507e-05 Stddev: 2.696977e-06
    Size: 65536 Average: 8.447170e-05 Stddev: 2.323939e-06
    Size: 131072 Average: 1.497269e-04 Stddev: 2.056490e-06
    Size: 262144 Average: 2.811909e-04 Stddev: 6.059022e-06
    Size: 524288 Average: 5.399466e-04 Stddev: 2.198240e-06
    Size: 1048576 Average: 1.061463e-03 Stddev: 3.065232e-06
    Size: 2097152 Average: 2.106738e-03 Stddev: 1.004478e-05

而当我们取节点6和75时,测试数据如下:

Size: 32 Average: 1.018047e-05 Stddev: 6.808134e-06
Size: 64 Average: 5.602837e-06 Stddev: 6.419617e-07
Size: 128 Average: 9.679794e-06 Stddev: 1.045579e-05
Size: 256 Average: 1.194477e-05 Stddev: 1.145573e-05
Size: 512 Average: 1.001358e-05 Stddev: 3.917617e-06
Size: 1024 Average: 1.132488e-05 Stddev: 2.264977e-06
Size: 2048 Average: 1.511574e-05 Stddev: 2.845484e-06
Size: 4096 Average: 1.816750e-05 Stddev: 2.139931e-06
Size: 8192 Average: 2.992153e-05 Stddev: 2.092794e-06
Size: 16384 Average: 3.972054e-05 Stddev: 3.302248e-06
Size: 32768 Average: 5.364418e-05 Stddev: 2.438406e-06
Size: 65536 Average: 9.126663e-05 Stddev: 4.580864e-06
Size: 131072 Average: 1.563311e-04 Stddev: 4.737336e-06
Size: 262144 Average: 2.840281e-04 Stddev: 3.253431e-06
Size: 524288 Average: 5.425692e-04 Stddev: 2.291921e-06
Size: 1048576 Average: 1.065373e-03 Stddev: 4.348444e-06
Size: 2097152 Average: 2.111387e-03 Stddev: 9.281682e-06

很显然当消息大小比较小的时候节点6到75所需时间会大于6到7的时间,这也说明节点6与节点7的物理距离可能比节点6和75的距离要远。

  • 大小为32B的消息的RTT时间反而比大小为64B要长,这很可能是由于32B是第一个消息,而由于初始化或者其他原因,这个消息的发送时间相对会变长。测试发现当在正式计算之前发送10次模拟消息而不加入统计数据时,此差异将被消除。