学习来源
MPI安装
打开官网下载源代码
然后将这个mpich-4.0.2.tar.gz
上传到服务器(一般服务器上是可以直接下载的,但是我用的是百度的aistudio,不支持apt-get,只能手工下载了)
解压它
tar -xzvf ./mpich-4.0.2.tar.gz -C /home/aistudio/external-libraries/mpich3
进入解压缩文件夹
cd ./mpich3/mpich-4.0.2
配置(这个过程很漫长)
./configure --prefix=/home/aistudio/external-libraries/mpich3
编译
make
安装
make install
添加mpi的bin文件:一般是
vim ~/.bashrc
末尾添加
export PATH=/home/mpich3/bin:$PATH
保存文件后,刷新
source ~/.bashrc
但我是在aistudio下操作的,貌似官网就说了怎么添加环境变量,我就大胆尝试了一下
%env PATH=/home/aistudio/external-libraries/mpich3/bin:$PATH
没想到输完之后,mpi命令能用了,但是原来的linux指令都不能用了,后来我又改回去了,没想到改回来之后mpi的命令还是能用,这波搞不懂了,不知道有没有懂的兄弟提点一下。
%env PATH=/bin:/usr/bin:$PATH
demo
编译:mpic++ main.cpp -o main
执行:mpiexec -np 4 ./main
#include <stdio.h>
#include <string.h>
#include "mpi.h"
void main(int argc, char* argv[])
{
int numprocs, myid, source;
MPI_Status status;
char message[100];
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
if (myid != 0) { //非0号进程发送消息
strcpy(message, "Hello World!");
MPI_Send(message, strlen(message) + 1, MPI_CHAR, 0, 99,
MPI_COMM_WORLD);
}
else { // myid == 0,即0号进程接收消息
for (source = 1; source < numprocs; source++) {
MPI_Recv(message, 100, MPI_CHAR, source, 99,
MPI_COMM_WORLD, &status);
printf("接收到第%d号进程发送的消息:%s\n", source, message);
}
}
MPI_Finalize();
}
实验
实验内容
:分别使用不同矩阵规模、不同的进程数、不同的线程数进行矩阵乘法实验。
实验结果
:分别展示了不同矩阵规模下加速比与进程数的关系(如图1所示,线程数设置为1)、不同矩阵规模下加速比与线程数的关系(如图2所示,进程数设置为8)。
实验分析
:
(1)我们可以发现,随着进程数的增加,加速比不是线性增长的,反而在某几个较小的进程数上,可以得到较大的加速比,说明进程的创建是有很大开销的,如果这个开销被多个进程带来的并行计算能力抵消,则加速效果显著,反之,若增加的计算能力没有抵消这部分开销,加速效果反而不佳,也就是说合理的使用MPI技术,能够得到一个较为不错的效果。
(2)在一定的进程数下,增加线程数,加速比呈现上升趋势,说明线程的开销并没有进程那么大,合理的增加线程数,可以带来较大的增益,也就是说MPI与OpenMP的结合可以得到更好的效果。
c++源代码
:mpic++ main.cpp -o main && mpiexec -np 8 ./main
#include <stdio.h>
#include <string.h>
#include <chrono>
#include "mpi.h"
using namespace std;
using namespace std::chrono;
const int N=1024; //矩阵的维度
const int ThreadNumber=32; //线程数
int a[N*N],b[N*N],c[N*N],buffer[N*N],ans[N*N];
int main(int argc, char* argv[])
{
int rank, numprocs, line;
// MPI 初始化
MPI_Init(NULL, NULL);
MPI_Comm_rank(MPI_COMM_WORLD, &rank); //当前进程的索引
MPI_Comm_size(MPI_COMM_WORLD, &numprocs); // 进程个数
line = N / numprocs; // 从进程分到 a 矩阵的行数
for (int i = 0; i < N*N; i++) //矩阵初始化
a[i] = b[i] = i;
if(rank==0) //主进程
{
auto start = system_clock::now();
for (int i = 1; i < numprocs; i++)
{
//把b矩阵发给每个从进程
MPI_Send(b, N * N, MPI_INT, i, 0, MPI_COMM_WORLD);
//把a矩阵的对应行发给每个从线程
MPI_Send(a + (i - 1) * line * N, N * line, MPI_INT, i, 1,MPI_COMM_WORLD);
}
for (int k = 1; k < numprocs; k++)
{
MPI_Recv(ans, line * N, MPI_INT, k, 3, MPI_COMM_WORLD,MPI_STATUS_IGNORE);
for (int i = 0; i < line; i++)
{
for (int j = 0; j < N; j++)
{
c[((k - 1) * line + i) * N + j] = ans[i * N + j];
}
}
}
auto end = system_clock::now();
auto duration = duration_cast<microseconds>(end - start);
auto paraller_compute_time=double(duration.count()) * microseconds::period::num / microseconds::period::den;
start = system_clock::now();
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
int temp = 0;
for (int k = 0; k < N; k++) {
temp += a[i*N+k] * b[i*N+k];
}
c[i*N+j] = temp;
}
}
end = system_clock::now();
duration = duration_cast<microseconds>(end - start);
auto serial_compute_time=double(duration.count()) * microseconds::period::num / microseconds::period::den;
printf("加速比:%.1lf \n",serial_compute_time/paraller_compute_time);
}
else{
MPI_Recv(b, N * N, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
MPI_Recv(buffer, N * line, MPI_INT, 0, 1, MPI_COMM_WORLD,MPI_STATUS_IGNORE);
#pragma omp parallel for num_threads(ThreadNumber)
for (int i = 0; i < line; i++)
{
for (int j = 0; j < N; j++)
{
int temp = 0;
for (int k = 0; k < N; k++)
temp += buffer[i * N + k] * b[k * N + j];
ans[i * N + j] = temp;
}
}
MPI_Send(ans, line * N, MPI_INT, 0, 3, MPI_COMM_WORLD);
}
MPI_Finalize();
return 0;
}
python绘图代码
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
import matplotlib
# aistudio上设置plt中文正常显示
matplotlib.rcParams['font.sans-serif'] = ['FZSongYi-Z13S'] # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False # 正确显示连字符
# 参数设置
process_nums=[4,8,16,32] # 进程数
thread_nums=[4,8,16,32] # 进程数
matrix_scales=[64,128,256,512,1024] # 矩阵规模
# problem1_dic[i]表示某一个矩阵规模下的 各个进程数对应的加速比 实验数据
problem1_dic=[
[2.7,2.1,0.8,0.4],
[2.8,3.7,3.3,0.9],
[4.6,5.7,0.5,1.1],
[1.7,1.2,1.1,0.8],
[1.0,0.7,0.5,0.7]
]
# problem2_dic[i]表示某一个矩阵规模、8个进程下 各个线程数对应的加速比 实验数据
problem2_dic=[
[1.3,1.7,1.6,1.4],
[3.9,3.8,4.0,4.2],
[4.6,5.2,5.0,5.8],
[1.3,1.4,1.3,1.3],
[0.6,0.8,0.9,1.7]
]
# 第一张图
x=process_nums
fig=plt.figure()
axes=fig.add_axes([0.1,0.1,0.8,0.8])
axes.set_title("不同矩阵规模下加速比与进程数的关系")
colors=["green","black","blue","cyan","magenta","red"]
labels=[str(matrix_scale)+" * "+str(matrix_scale) for matrix_scale in matrix_scales]
for idx in range(len(matrix_scales)):
y=problem1_dic[idx]
axes.plot(x,y,c=colors[idx],label=labels[idx])
axes.axis([4,32,0,6])
axes.xaxis.set_major_locator(MultipleLocator(4))
axes.set_xlabel("进程数")
axes.set_ylabel("加速比")
axes.legend()
fig.savefig("3.1.png",dpi=300)
# 第二张图
x=thread_nums
fig=plt.figure()
axes=fig.add_axes([0.1,0.1,0.8,0.8])
axes.set_title("不同矩阵规模下加速比与线程数的关系")
colors=["green","black","blue","cyan","magenta","red"]
labels=[str(matrix_scale)+" * "+str(matrix_scale) for matrix_scale in matrix_scales]
for idx in range(len(matrix_scales)):
y=problem2_dic[idx]
axes.plot(x,y,c=colors[idx],label=labels[idx])
axes.axis([4,32,0,6])
axes.xaxis.set_major_locator(MultipleLocator(4))
axes.set_xlabel("线程数")
axes.set_ylabel("加速比")
axes.legend()
fig.savefig("3.2.png",dpi=300)