4

我正在将一个由 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
  • 私有内存大小: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 连接。

4

1 回答 1

6

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

  • 请求独占节点访问权限-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英特尔编译器。将线程绑定到内核可防止操作系统在不同处理器内核之间移动线程,从而加快执行速度,尤其是在数据局部性非常重要的 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

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


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

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

于 2013-03-03T20:02:19.943 回答