目录
1.将赛题克隆至家目录之后对压缩包进行解压缩后即可按照readme中给出的步骤在对应的目录下进行配置。
2.尝试跑多机(这步在尝试时实际在后面,后来想了一下其实一开始先跑多机时间降得会更快)
一·背景介绍
1.OpenCAEPoro是一种数值模拟器,专为多孔介质中的多相和多组分流而设计,特别强调石油储层问题。它使用 C++ 构建,并结合了 MPI 并行性以增强其性能和可扩展性。其过程主要涉及利用高性能计算研究流体动力学相关的问题和多物理场的模拟。
2.PETSC是一个专门为大规模科学计算提供的高效数值求解工具的开源软件库,主要是处理线性和非线性方程组,以及求解偏微分方程。
二编译及安装
1.将赛题克隆至家目录之后对压缩包进行解压缩后即可按照readme中给出的步骤在对应的目录下进行配置。
克隆:
git clone https://github.com/OpenCAEPlus/OpenCAEPoro_ASC2024
2.过程中遇到的报错以及解决
(1)build parmetis:
build parmetis:
S1. cd parmetis-4.0.3
S2. modify the path in script "build-parmetis.sh", such as "make config
cc=mpiicc prefix=ROOT_DIR/parmetis-4.0.3/parmetis-install"
S3. sh build-parmetis.sh
ROOT_DIR is the root directory of the repository, which is set by the user
according to their own directory (similarly hereinafter).
在这一步骤当中,按照要求修改改了文件的内容之后运行sh build-parmetis.sh遇到了如下报错
查了之后发现这个报错的意思是找不到mpicc
which mpicc
发现该命令没有得到返回值,我一开始以为要自己安装,但是发现没有root权限(笑,后来得知其实已经安装好了只需要直接设置环境变量
对该修改进行更新后再次编译就能顺利通过了。
(2)build petsc
build petsc
S1. cd petsc-3.19.3
S2. modify the path in script "build-petsc.sh", such as
export PETSC_DIR=ROOT_DIR/petsc-3.19.3
export PETSC_ARCH=petsc_install
./configure CC=mpiicc CXX=mpiicpc \
--with-fortran-bindings=0 \
--with-hypre-dir=ROOT_DIR/hypre-2.28.0/install \
--with-debugging=0 \
COPTFLAGS="-O3" \
CXXOPTFLAGS="-O3" \
make -j 20 PETSC_DIR=ROOT_DIR/petsc-3.19.3 PETSC_ARCH=petsc_install all
S3. sh build-petsc.sh
这一步遇到的报错挺麻烦的
最主要的是一开始没明白在报什么错,最明显的一行就是在说什么fortran error,在modify的时候额外添加一个参数--with-fc=0 即可 ,该参数表示对fortran编译器进行禁用(不启用调用模式),这样一来就不会尝试去链接它进而就不会出现could not be located.对于前三行内容前面我们已经修改过对应的环境变量那么前三行就可以直接注释掉使其不生效
p.s.当时这一步卡了我好久,进行了一些不必要的配置和修改,导致当时的路径中存在一些隐藏的问题,因此即使这步的编译通过之后我在进行下一步编译时仍然遇到了很奇怪的问题,在我又卡了近一天之后我终于决定把之前的文件全部删除再从第一步克隆重新开始,在有了之前的经验之后我这一次的配置非常之丝滑,如果我执着于debug而没有敢于从头开始不知道又要卡多久。。。。
3.修改脚本路径,编译可执行程序
这一步还是按照readme将路径修改为我们之前的安装路径。
三.测试运行
cd OpenCAEPoro/
mpirun -np <core_num> ./testOpenCAEPoro ./data/case1.data verbose=1
这里也有说明,指定进程数应<=10,那么就必须小于十吗(doge,查了一下原来是出于对于资源消耗以及便于监控管理诸如此类的考虑。那先浅试一下小数4和5之类.情况基本是下面这样
ok运行时间非常之长(长到我以为是前面过程有问题。。。),那么看来这个object time对应的394.914是个大数,我们接下来优化的目标也是围绕着如何减小这个 OBJECT TIME。
四.优化
(1)首先要明确影响object time的主要因素是网络质量,时间步长(在动态模拟中,时间步长的选择会影响每个时间步的计算量。较小的时间步长通常会导致更高的计算精度,但也会增加计算时间),模型的复杂性(基本改不了),选择的求解器类型和设置(如迭代次数、收敛标准等),硬件之类就不说了。
(2)这段是后面感觉有必要补充:对于object time下降一秒两秒这种优化成都怎么衡量呢?答案是要结合时间降低的百分比,也就是说如果基线时间较长,那么两秒就是一个小的提升,如果基线时间很短,那么2s也有可能是百分之几百的进步。
参考文档
1.增加进程数
前面已经提到是出于对于资源消耗以及便于监控管理诸如此类的考虑才对进程数有如此限制,因此直接尝试10后结果是231.663秒,相对于开始的394.914已经有了提升,那么是否可以更大呢?浅浅尝试一下45哈
我勒个去直接变58.159了
那么进程数最大可以是多少呢?运行以下代码
nproc
lscpu
这里说明目前是每个cpu有32个核心,两个cpu共64个核心,逻辑核心数决定我们可同时有效运行的线程数。建议的并行进程数通常不超过核心数的2到4倍,以确保每个进程都有足够的资源,按照这个道理去递增进程数,发现随着数值接近64,并不完全是递增的,其原因包含以下几点:
-
资源竞争:
当进程数超过系统的核心数时,进程之间会争夺处理器的时间片,这会导致上下文切换的增加,导致性能下降。每个进程都会被系统调度,过多的进程竞争有限的资源。
-
内存带宽瓶颈:
随着并行进程数增加,尤其是在需要大量内存操作的情况下,内存带宽(带宽是指能够同时传输的数据量)可能成为瓶颈。如果多个进程同时访问内存,会导致带宽争用,进而影响性能。
-
缓存效应:
多进程并行运行会导致缓存共享和缓存伪共享的问题。不同进程可能会访问相同的缓存行,导致缓存命中率下降,提高了内存访问的延迟。
-
通信开销:
进程之间的通信(特别是使用 MPI 等通信框架时),随着进程数的增加,通信开销也会增加,可能超出了并行计算所带来的性能收益。
-
负载不均衡:
过多的进程可能导致负载不均衡,部分进程工作量重而导致阻塞,而其他进程处于空闲状态,结果导致整体性能下降。
-
调度开销:
当进程数量过多,操作系统需要进行更多的上下文切换和调度,这会增加 CPU 的调度开销,从而降低实际计算效率。
-
超量并行:
大多数计算任务会有一个最佳的并行程度,实际上运行的进程数远远超过这个最佳水平,会导致性能边际收益递减,即每增加一个进程所带来的性能提升越来越少。
2.尝试跑多机(这步在尝试时实际在后面,后来想了一下其实一开始先跑多机时间降得会更快)
编写hostfile以及运行脚本
再次运行
??发现时间基本没有变化,说明我这步操作没有效果,怀疑根本没有成功跑多机。后来发现是没有实现多机之间的免密通信,应该生成密钥再发送给其他机器
ssh-keygen -t rsa
3.vtune性能分析
其功能主要包括检测程序性能瓶颈,提供cpu以及gpu的使用情况并且支持针对多线程和并行应用程序的分析,显示线程之间的交互及其对性能的影响。
vtune -collect hotspots -result-dir result1 mpirun -np n ./testOpenCAEPoro ./data/test/test.data verbose=1
得到如下结果
结果分析:
Function | Module | CPU Time | % of CPU Time |
---|---|---|---|
PMPI_Allreduce | libmpi.so.12 | 2.363s | 7.4% |
PMPI_Waitall | libmpi.so.12 | 2.103s | 6.6% |
PMPI_Isend | libmpi.so.12 | 1.195s | 3.7% |
__strcasecmp_l_avx | libc.so.6 | 1.020s | 3.2% |
ucp_worker_progress | libucp.so.0 | 0.845s | 2.6% |
[Others] | N/A | 24.554s | 76.5% |
从这个结果当中可以看出来,三个MPI函数PMPI_Allreduce
, PMPI_Waitall
, PMPI_Isend在计算中
占据了相当大的时间,因此我们可以考虑使用更加高效的并行策略减少进程间的通信带来的资源占据。
4.修改编译选项
以petsc的build-petsc.sh为例
这里可以看出现在选用的是目前使用的是-03优化标志,已经处于较高的级别。 make -j 20表示使用 20 个线程进行并行编译。实际上我们有更多的 CPU 核心,可以增加这个数字到前面提到的逻辑 CPU 核心数量(callback!!!),以加快编译速度。直接上64.
此修改降低了三点几秒(啊啊啊啊啊这个尝试没有严格控制变量,在把这个进程拉满的同时我稍微减小了一下运行的进程数,怕它卡太久)
在其他场景当中我们选用更加优化的编译器其实也能达到一定的目的。
5.更换程序运行的数学库
对于这一步来讲可以考虑(1)能否把现有的库换成更新的版本(2)本项目用到的几个数学库都是可以进行单独替换的。集成其他的库如(Intel) MKL或oneMKL可以来替代LAPACK 实现,注意要确保在配置期间声明。这时考虑确保安装后将库的路径添加到环境变量中可以修改对应的CMakelists.txt或将用到的库在编译配置中指向Intel MKL。
以petsc为例 ,在配置时,可以直接通过 --with-mkl
选项启用 MKL 支持。例如:
./configure --with-mkl=PATH_TO_INTEL_MKL --with-cxx --with-openmp --with-mpi
跑一下看看
可以看到object time确实变小了。
多机是后面跑的这里插上
6.修改源代码
其实在最开始想着优化的时候,我是上去就想莽源代码,,,后来发现有点天书(对我来说)费了老半天劲改了一点for循环,且在最初阶段,对应的object time已不可考(代码方面功力确实不够啊啊啊)下一步也打算强化这个方面。
总结:
整个优化过程当中,时间减少最明显的两个阶段分别是设置的进程数接近逻辑内核的时候和跑多机的时候,基本都是减少了50%的时间
其他的步骤成功实践的part也确实取得了提高但是按照之前提到的评价标准,不够显著,差不多能符合预期吧。另外三个MPI函数其实可以做进一步的修改和优化,这三个占了大头的函数实际上主要与通信有关,可以考虑优化数据传输逻辑。例如,减少传输的数据量,使用更高效的归约方法如分布式归约算法(第一个函数的作用),或者采取更合理的进程同步策略,避免不必要的调用。也可使用 MPI_Isend
和 MPI_Irecv
替代 MPI_Send
和 MPI_Recv
。非阻塞通信可以让进程在等待消息时继续执行其他计算,从而减少通信开销。