LSF 中的混合 MPI/OpenMP

Posted

技术标签:

【中文标题】LSF 中的混合 MPI/OpenMP【英文标题】:Hybrid MPI/OpenMP in LSF 【发布时间】:2013-02-17 02:45:22 【问题描述】:

我正在将 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_THREADS时间1      4       21:12      2      4      20:12      

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

另一个证明它的结果是 -nOMP_NUM_THREADStime2      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 私有内存大小:7.8 Mb 虚拟专用内存:63.2 MB

使用 OpenMP(4 线程)

实际内存大小:31.5 MB 虚拟内存大小:2.52 GB 共享内存大小:212 KB 私有内存大小:27.1 Mb 虚拟专用内存:210.2 MB

集群硬件简介

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

【问题讨论】:

这里的大多数人都不是通灵者。您可以从描述您的计算算法、其内存要求和计算强度(最好以 FLOPS/字节为单位)开始。然后告诉我们每个集群节点在 CPU 类型、套接字数量、内存带宽等方面都有哪些硬件。 @HristoIliev 谢谢你的回复!我的程序使用 OpenMP 来并行化 fortran 中的循环。循环中还有一些分配/释放内存。你能告诉我如何获得内存需求和计算强度吗?我正在向集群管理器询问硬件信息。再次感谢。 如何编辑您的答案并提供一些示例内核循环?无需粘贴整个程序 - 只需粘贴多少代码即可了解正在处理的信息量。我的观点是,如果您的应用程序受内存限制,那么可用内存带宽就是限制加速的因素。 @HristoIliev 代码实际上非常复杂且冗长。在这里选择“重要”部分进行解析非常困难,因为我不确定哪些部分可能是“重要的”。相反,我在本地环境中做了一个内存监视器并更新了集群中的内存信息。如果您需要更多信息,请告诉我。非常感谢! 所以你只是多次运行同一个可执行文件。那么,当他们不合作和分享他们的工作时,你如何期望它在更多的进程下运行得更快? MPI 是一个通信库,您必须明确将 MPI 调用放入应用程序中,并让不同的进程处理问题的不同部分,以缩短处理时间。简单地多次启动你的可执行文件不会神奇地让它运行得更快。 【参考方案1】:

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

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

您的作业脚本应如下所示:

#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 用于 Intel 编译器。将线程绑定到内核可防止操作系统在不同处理器内核之间移动线程,从而加快执行速度,尤其是在数据局部性非常重要的 NUMA 系统(例如,具有多个 CPU 插槽和每个插槽中的单独内存控制器的机器)上.

如果您想在不同的输入文件上运行程序的多个副本,请提交数组作业。使用 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]) 的数组作业。 -e 中的 %I 被作业编号替换,因此您将从每个作业中获得一个单独的 err 文件。 LSF_JOBINDEX 环境变量设置为当前作业索引,即第一个作业为1,第二个作业为2,依此类推。


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

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

【讨论】:

非常感谢您的回答。对我现在的问题和未来的方向真的很有帮助!

以上是关于LSF 中的混合 MPI/OpenMP的主要内容,如果未能解决你的问题,请参考以下文章

OpenMP 和 MPI 混合程序

确保混合 MPI / OpenMP 在不同的内核上运行每个 OpenMP 线程

基于 MPI/OpenMP 混合编程的大规模多体(N-Body)问题仿真实验

OpenMP 和 MPI 交互

使用 MPI/OpenMP 的具有派生数据类型(嵌套类对象)容器的 C++ 程序

大数据系列9:mpi4py高性能计算