parallel-processing - LSF 中的混合 MPI/OpenMP

标签 parallel-processing mpi openmp

我正在将一个由 OpenMP 并行化的程序移动到集群。该集群使用 Lava 1.0 作为调度程序,每个节点有 8 个核心。我在作业脚本中使用了 MPI 包装器来进行多主机并行。

这是作业脚本:

#BSUB -q queue_name
#BSUB -x

#BSUB -R "span[ptile=1]"
#BSUB -n 1

#BSUB -J n1p1o8
##BSUB -o outfile.email
#BSUB -e err

export OMP_NUM_THREADS=8

date
/home/apps/bin/lava.openmpi.wrapper -bynode -x OMP_NUM_THREADS \
    ~/my_program ~/input.dat ~/output.out 
date

我专门在 ONE 主机上做了一些实验。但是,我不知道如何解释一些结果。

1.

-nOMP_NUM_THREADStime
1      4      21:12
2      4      20:12

这是否意味着 MPI 在这里不做任何并行?我认为在第二种情况下,每个 MPI 进程都会有 4 个 OMP 线程,因此它应该使用 800% 的 CPU 使用率,这应该比第一个更快。

另一个证明它的结果是
-nOMP_NUM_THREADStime
2      2      31:42
4      2      30:47

它们的运行时间也非常接近。

2.
在这种情况下,如果我想通过简单的方式在这个集群中以合理的优化速度并行这个程序,在每个主机中放置1个MPI进程(告诉LFG我使用一个核心)是否合理,设置OMP_NUM_THREADS = 8,然后专门运行它?因此 MPI 仅适用于跨节点作业,而 OpenMP 适用于内部节点作业。 (-n = 主机数;ptile = 1;OMP_NUM_THREADS = 每个主机的最大内核数)

更新:
程序由 gfortran -fopenmp 编译,不带 mpicc。 MPI 仅用于分发可执行文件。

3 月 3 日更新:
程序内存使用监视器

本地环境:Mac 10.8/2.9 Ghz i7/8GB 内存

没有 OpenMP
  • 实际内存大小:8.4 MB
  • 虚拟内存大小:2.37 GB
  • 共享内存大小:212 KB
  • 私有(private)内存大小:7.8 Mb
  • 虚拟专用内存:63.2 MB

  • 使用 OpenMP(4 线程)
  • 实际内存大小:31.5 MB
  • 虚拟内存大小:2.52 GB
  • 共享内存大小:212 KB
  • 私有(private)内存大小:27.1 Mb
  • 虚拟专用内存:210.2 MB

  • 集群硬件简要信息

    每个主机都包含双四核芯片,每个节点有 8 个内核和 8GB 内存。此集群中的主机通过 infiniband 连接。

    最佳答案

    考虑到您在评论中指定的信息,您最好的选择是:

  • 使用 -x 请求独占节点访问权限(你已经这样做了);
  • 使用 -n 1 为每个节点请求一个插槽(你已经这样做了);
  • 设置 OMP_NUM_THREADS每个节点的核心数(您已经这样做了);
  • 启用 OpenMP 线程的绑定(bind);
  • 直接启动可执行文件。

  • 您的作业脚本应如下所示:
    #BSUB -q queue_name
    #BSUB -x
    #BSUB -n 1
    
    #BSUB -J n1p1o8
    ##BSUB -o outfile.email
    #BSUB -e err
    
    export OMP_NUM_THREADS=8
    export OMP_PROC_BIND=true
    
    date
    ~/my_program ~/input.dat ~/output.out
    date
    
    OMP_PROC_BIND是 OpenMP 3.1 规范的一部分。如果使用符合旧版本标准的编译器,则应使用特定于供应商的设置,例如GOMP_CPU_AFFINITY对于 GCC 和 KMP_AFFINITY用于英特尔编译器。将线程绑定(bind)到内核可防止操作系统在不同处理器内核之间移动线程,从而加快执行速度,尤其是在数据局部性非常重要的 NUMA 系统上(例如,具有多个 CPU 插槽和每个插槽中的单独内存 Controller 的机器) .

    如果您想在不同的输入文件上运行程序的多个副本,请提交数组作业。使用 LSF(我猜也使用 Lava)这是通过更改作业脚本来完成的:
    #BSUB -q queue_name
    #BSUB -x
    #BSUB -n 1
    
    #BSUB -J n1p1o8[1-20]
    ##BSUB -o outfile.email
    #BSUB -e err_%I
    
    export OMP_NUM_THREADS=8
    export OMP_PROC_BIND=true
    
    date
    ~/my_program ~/input_${LSF_JOBINDEX}.dat ~/output_${LSF_JOBINDEX}.out
    date
    

    这会提交一个包含 20 个子作业 (-J n1p1o8[1-20]) 的数组作业。 %I-e替换为工作编号,因此您将获得一个单独的 err每个作业的文件。 LSF_JOBINDEX环境变量设置为当前作业索引,即 1在第一份工作中,2在第二个等等。

    我关于程序内存使用的问题不在于它消耗了多少内存。这是关于在单个 OpenMP 循环中处理的典型数据集有多大。如果数据集不够小,无法放入 CPU 的最后一级缓存,则需要考虑内存带宽。如果您的代码对每个数据项进行繁重的本地处理,那么它可能会随着线程数的增加而扩展。如果另一方面它进行简单而轻量的处理,那么即使是单个线程,内存总线也可能会饱和,尤其是在代码被正确矢量化的情况下。通常这是通过以 FLOPS/字节为单位的所谓操作强度来衡量的。它给出了在从内存中获取下一个数据元素之前发生的数据处理量。高操作强度意味着 CPU 中发生了大量的数字运算,并且数据很少传输到内存或从内存传输。无论内存带宽是多少,此类程序几乎与线程数成线性关系。另一方面,操作强度非常低的代码受内存限制,导致 CPU 未得到充分利用。

    严重受内存限制的程序不会随线程数而扩展,而是随可用内存带宽扩展。例如,在较新的 Intel 或 AMD 系统上,每个 CPU 插槽都有自己的内存 Controller 和内存数据路径。在这样的系统上,内存带宽是单个套接字带宽的倍数,例如具有两个插槽的系统提供的内存带宽是单插槽系统的两倍。在这种情况下,无论何时使用两个套接字,您都可能会看到代码运行时间的改进,例如如果你设置 OMP_NUM_THREADS等于内核总数,或者如果您设置 OMP_NUM_THREADS等于2并告诉运行时将两个线程放在不同的套接字上(当线程正在执行向量化代码并且单个线程能够使本地内存总线饱和时,这是一个合理的场景)。

    关于parallel-processing - LSF 中的混合 MPI/OpenMP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15177395/

    相关文章:

    python - 将 joblib 与导入函数一起使用时出错

    c#-4.0 - Parallel For 对 ushort 数组求和(大数组 18M)

    c - MPI 主进程不等待其他进程计算

    c++ - 使用 g++ -c 和 g++ -fopenmp 生成文件

    parallel-processing - 未使用所需数量的处理器

    python - 多处理器环境下的 scikit-learn 网格搜索

    c - MPI 错误 : expected expression before ‘,’ token

    openmp - OpenMP 中的原子性和关键性有什么区别?

    c - 如何确保动态分配的数组在 openmp 中是私有(private)的

    multithreading - Parallel::ForkManager 使子例程慢 1000 倍