我正在将一个由OpenMP并行化的程序移动到Cluster。群集使用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。
-n OMP_NUM_THREADS 时间
1 4 21:12
2 4 20:12
这是否意味着MPI在这里没有做任何并行?我认为在第二种情况下,每个MPI进程都有4个OMP线程,所以它应该使用800%的CPU使用率,这应该比第一个快。
证明这是另一个结果
-n OMP_NUM_THREADS 时间
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
使用OpenMP(4个线程)
群集硬件简要信息
每个主机包含双四芯片,每个节点8个内核和8GB内存。此群集中的主机由infiniband连接。
答案 0 :(得分:5)
考虑到您在评论中指定的信息,您最好的选择是:
-x
进行独占节点访问(您已经这样做了); -n 1
一个插槽(您已经这样做了); OMP_NUM_THREADS
设置为每个节点的核心数(您已经这样做了); 您的工作脚本应如下所示:
#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规范的一部分。如果使用符合旧版标准的编译器,则应使用特定于供应商的设置,例如GCC为GOMP_CPU_AFFINITY
,英特尔编译器为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
这提交了一个包含20个子作业(-J n1p1o8[1-20]
)的数组作业。 %I
中的-e
被作业编号替换,因此您将从每个作业中获得单独的err
文件。 LSF_JOBINDEX
环境变量设置为当前作业索引,即第一个作业中为1
,第二个作业中为2
,依此类推。
关于程序内存使用情况的问题不在于它消耗了多少内存。它是关于在单个OpenMP循环中处理的典型数据集有多大。如果数据集不够小,无法容纳CPU的最后一级缓存,则会考虑内存带宽。如果您的代码对每个数据项执行大量本地处理,那么它可能会根据线程数进行扩展。如果在另一侧它执行简单和轻量级处理,那么即使是单个线程,内存总线也可能会饱和,特别是如果代码被正确地矢量化。通常这是通过FLOPS /字节中的所谓操作强度来测量的。它给出了从内存中提取下一个数据元素之前发生的数据处理量。高操作强度意味着在CPU中发生大量数字运算,并且数据很少转移到存储器或从存储器传输。无论内存带宽是多少,这些程序几乎都与线程数成线性关系。另一方面,操作强度非常低的代码受内存限制,导致CPU未得到充分利用。
严重受内存限制的程序不会使用数字线程进行扩展,但会使用可用的内存带宽。例如,在较新的Intel或AMD系统上,每个CPU插槽都有自己的内存控制器和内存数据路径。在这样的系统上,存储器带宽是单个插座的带宽的倍数,例如,具有两个插槽的系统提供两倍于单插槽系统的内存带宽。在这种情况下,每当使用两个套接字时,您可能会看到代码运行时的改进,例如,如果将OMP_NUM_THREADS
设置为等于核心总数,或者将OMP_NUM_THREADS
设置为等于2
并告诉运行时将两个线程放在不同的套接字上(这是一个当线程执行矢量化代码并且单个线程能够使本地存储器总线饱和时,这是合理的场景。)