【上海大学计算机系统结构实验报告】多机环境下MPI并行编程 ...

打印 上一主题 下一主题

主题 1863|帖子 1863|积分 5589

实验目标


  • 学习体例多进程并行步伐实现如下功能:

    • 创建多进程,输出进程号和进程数。
    • 运行多进程并行例子步伐。
    • 编程实现大规模矩阵的并行计算。

实验过程及效果分析

实验环境



  • 操纵系统:Ubuntu 20.04
  • 开发工具:GCC 9.3.0、OpenMPI 4.0.3
实验步骤

多主机无密码登录配置


  • 在任意一台主机上生成RSA密钥对:
    1. ssh-keygen -t rsa -C "Kevin"
    复制代码
​ 该命令将在用户主目次下的 ~/.ssh/ 目次中生成id_rsa和id_rsa.pub两个文件。

  • 将生成的公钥内容追加至同目次下的 authorized_keys 文件中,授权本主机信托该密钥登录:
    1. cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
    复制代码
    此时,将该 authorized_keys 文件拷贝到其他主机,以建立互信。
  • 为便于多主机通讯与配置,使用以下命令将三台主机分别命名为 master、slave1 和 slave2:
    1. sudo vim /etc/hostname
    复制代码
  • 编辑 /etc/hosts 文件,添加各主机的IP与主机名映射,比方:
    1. 192.168.1.100 master
    2. 192.168.1.101 slave1
    3. 192.168.1.102 slave2
    复制代码
  • 重启三台主机,而后使用ssh [username]命令测试主机间的无密码登录是否见效。
安装MPI环境


  • 在三台主机上分别执行以下命令安装MPI环境:
    1. sudo apt install openmpi-bin libopenmpi-dev
    复制代码
  • 验证安装是否成功:
    1. mpicc --version
    2. mpirun --version
    复制代码
图 1 安装成功后会显示版本信息
配置NFS共享目次

​ 在多主机运行MPI步伐时,全部节点需要访问相同的可执行文件和输入输出路径。假如每台主机都独立保存一份代码和数据,会导致维护成本较高且容易堕落。因此,此次实验使用NFS(网络文件系统)在master节点上创建共享目次,并将其挂载到全部计算节点,从而确保各节点读取到的是同一份步伐和数据。
​ 具体步骤如下:

  • 在master节点上配置NFS服务:

    • 安装NFS服务端:
      1. sudo apt install nfs-kernel-server
      复制代码
    • 创建共享目次并设置权限:
      1. sudo mkdir -p /home/ubuntu/shared
      2. sudo chown -R ubuntu:ubuntu /home/ubuntu/shared
      复制代码
    • 在/etc/exports文件中添加:
      1. /home/ubuntu/shared *(rw,sync,no_subtree_check)
      复制代码
    • 重启NFS服务使配置见效:
      1. sudo exportfs -a
      2. sudo systemctl restart nfs-kernel-server
      复制代码

  • 在slave1和slave2节点上挂载共享目次:

    • 安装NFS客户端:
      1. sudo apt install nfs-common
      复制代码
    • 创建本地挂载点:
      1. sudo mkdir -p /home/ubuntu/shared
      复制代码
    • 挂载共享目次:
      1. sudo mount master:/home/ubuntu/shared /home/ubuntu/shared
      复制代码

​ 挂载完成后,从节点可直接访问/home/ubuntu/shared目次,并与master节点保持实时同步。
MPI步伐测试

单主机多进程测试

​ 在master节点上编译运行下面的MPI步伐,验证并行本领,假如主机的核心数不敷,可添加 --oversubscribe 参数从而答应多个进程共享核心。
  1. #include <mpi.h>
  2. #include <stdio.h>
  3. int main(int argc, char **argv)
  4. {
  5.     MPI_Init(&argc, &argv);
  6.     int world_rank;
  7.     MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
  8.     int world_size;
  9.     MPI_Comm_size(MPI_COMM_WORLD, &world_size);
  10.     printf("Hello from rank %d out of %d processors\n", world_rank, world_size);
  11.     MPI_Finalize();
  12.     return 0;
  13. }
复制代码
​ 运行效果:
图 2 单主机多进程测试效果
多主机多进程测试


  • 在master节点创建主机清单文件,在此中设置每个节点的slots数:
  1.         master slots=2
  2.         slave1 slots=2
  3.         slave2 slots=2
复制代码

  • 使用--hostfile参数指定三台主机运行:
    1. mpirun --hostfile hosts -np 6 ./mpi_hello
    复制代码
    运行效果:
图 3 多主机多进程测试效果
大规模矩阵并行计算测试

​ 在共享目次中编译并运行下面的矩阵乘法步伐 :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <mpi.h>
  4. #include <string.h>
  5. #include <time.h>
  6. #define MASTER 0
  7. void MatrixGenerate(double *mat, int size) {
  8.     for (int i = 0; i < size * size; ++i)
  9.         mat[i] = (double)rand() / RAND_MAX;
  10. }
  11. void LocalMatrixMultiply(double *a_local, double *b, double *c_local, int local_rows, int size) {
  12.     for (int i = 0; i < local_rows; ++i)
  13.         for (int j = 0; j < size; ++j) {
  14.             double sum = 0.0;
  15.             for (int k = 0; k < size; ++k)
  16.                 sum += a_local[i * size + k] * b[k * size + j];
  17.             c_local[i * size + j] = sum;
  18.         }
  19. }
  20. int main(int argc, char *argv[]) {
  21.     int rank, size;
  22.     MPI_Init(&argc, &argv);
  23.     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  24.     MPI_Comm_size(MPI_COMM_WORLD, &size);
  25.     int test_sizes[] = {1000, 2000, 3000};
  26.     int num_tests = sizeof(test_sizes) / sizeof(int);
  27.     for (int test = 0; test < num_tests; ++test) {
  28.         int matrix_size = test_sizes[test];
  29.         int rows_per_proc = matrix_size / size;
  30.         int remaining = matrix_size % size;
  31.         int local_rows = rows_per_proc + (rank < remaining ? 1 : 0);
  32.         int offset = rank * rows_per_proc + (rank < remaining ? rank : remaining);
  33.         double *A = NULL, *B = NULL, *C = NULL;
  34.         double *A_local = (double *)malloc(local_rows * matrix_size * sizeof(double));
  35.         double *C_local = (double *)malloc(local_rows * matrix_size * sizeof(double));
  36.         B = (double *)malloc(matrix_size * matrix_size * sizeof(double));
  37.         if (rank == MASTER) {
  38.             A = (double *)malloc(matrix_size * matrix_size * sizeof(double));
  39.             C = (double *)malloc(matrix_size * matrix_size * sizeof(double));
  40.             srand(time(NULL) + test);  // 避免相同种子
  41.             MatrixGenerate(A, matrix_size);
  42.             MatrixGenerate(B, matrix_size);
  43.         }
  44.         // 广播 B 矩阵
  45.         MPI_Bcast(B, matrix_size * matrix_size, MPI_DOUBLE, MASTER, MPI_COMM_WORLD);
  46.         // 发送 A 子矩阵
  47.         if (rank == MASTER) {
  48.             int pos = 0;
  49.             for (int i = 0; i < size; ++i) {
  50.                 int send_rows = rows_per_proc + (i < remaining ? 1 : 0);
  51.                 if (i == MASTER) {
  52.                     memcpy(A_local, A + pos * matrix_size, send_rows * matrix_size * sizeof(double));
  53.                 } else {
  54.                     MPI_Send(A + pos * matrix_size, send_rows * matrix_size, MPI_DOUBLE, i, 0, MPI_COMM_WORLD);
  55.                 }
  56.                 pos += send_rows;
  57.             }
  58.         } else {
  59.             MPI_Recv(A_local, local_rows * matrix_size, MPI_DOUBLE, MASTER, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
  60.         }
  61.         // 开始计时并进行乘法
  62.         double start = MPI_Wtime();
  63.         LocalMatrixMultiply(A_local, B, C_local, local_rows, matrix_size);
  64.         double end = MPI_Wtime();
  65.         // 收集结果
  66.         if (rank == MASTER) {
  67.             int pos = 0;
  68.             memcpy(C + pos * matrix_size, C_local, local_rows * matrix_size * sizeof(double));
  69.             pos += local_rows;
  70.             for (int i = 1; i < size; ++i) {
  71.                 int recv_rows = rows_per_proc + (i < remaining ? 1 : 0);
  72.                 MPI_Recv(C + pos * matrix_size, recv_rows * matrix_size, MPI_DOUBLE, i, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
  73.                 pos += recv_rows;
  74.             }
  75.             printf("Matrix Size %dx%d, Time = %.3f seconds\n", matrix_size, matrix_size, end - start);
  76.         } else {
  77.             MPI_Send(C_local, local_rows * matrix_size, MPI_DOUBLE, MASTER, 1, MPI_COMM_WORLD);
  78.         }
  79.         // 清理内存
  80.         free(A_local); free(C_local); free(B);
  81.         if (rank == MASTER) {
  82.             free(A); free(C);
  83.         }
  84.     }
复制代码
​ 单主机运行效果:
图 4 单主机矩阵运算效果
​ 多主机运行效果:
图 5 多主机矩阵运算效果
​ 可以看到,随着任务规模扩大,多主机MPI并行计算在性能上显现出显着上风。
标题分析

​ 初次运行步伐时,可能出现通讯错误:
  1. ubuntu@master:~/shared$ make runs
  2. mpirun --hostfile hosts -np 6 ./mpi_matrix
  3. [slave1][[55832,1],2][btl_tcp_endpoint.c:625:mca_btl_tcp_endpoint_recv_connect_ack] received unexpected process identifier [[55832,1],3]
复制代码
​ 经排查发现,原因在于通过OpenMPI举行多主机运行时,OpenMPI会探求主机之前的全部IP接口,但是步伐现实上不会用到全部的IP接口,从而发生运行时阻塞或连接被拒绝得标题。以是此时需要通过--mca btl_tcp_if_include 参数来限制网络接口。
  1. mpirun --hostfile hosts --mca btl_tcp_if_include eth0 -np 6 ./mpi_matrix
复制代码
​ 通过显式指定接口为eth0后,标题得以办理。
总结

​ 通过本次实验,我系统把握了分布式MPI环境的搭建流程,也进一步巩固了SSH无密码登录以及NFS共享配置的方法。实验中通过多主机MPI步伐运行验证了并行计算在大规模数据处理中的高效性。这让我联想到在数据库系统中也有通过部署大规模集群来实现高并发访问和海量数据处理的本领,不过我并不确定这两者之间的原理是否相同,盼望以后有时机能进一步学习此中的原理。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莱莱

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表