OpenCAEporo运行与优化

赛题克隆与优化

一、赛题克隆

1. 克隆赛题项目

我将其克隆到家目录下,为了减少输入修改了目录名字(不过后面又记为变量了,感觉没什么太大用处)。然后在环境变量里新增该目录为变量$QUEST1

git clone /home/tangzehao
mv OpenCAEPoro_ASC2024 OpenCAEPoro
vim ~/.bashrc
# 键入i编辑,追加以下指令
export QUEST1=/home/tangzehao/OpenCAEPoro
# 按Esc退出编辑,然后输入:wq保存退出

2. 解压缩源文件

进入项目文件夹,接着解压所有的源文件。

cd /home/tangzehao/OpenCAEPoro
tar zxf lapck*
tar zxf parmetis*
tar zxf hypre*
tar zxf petsc-3.19.3*
tar zxf petsc_solver*
tar zxf OpenCAEPoro*

二、编译与测试

进入对应包的文件夹,按以下顺序编译

1. 编译lapck

make blaslib
make cblaslib
make lapacklib
make lapackelib

2. 编译parmetis

按照官方文档编译这个包的时候一直报错,找不到mpiicc,于是我把参数改成cc=gcc, cxx=g++,又在#include <mpi.h>报错,搜索发现是没有安装mpich¹,由于本机未安装且没有管理员权限,需要手动安装到家目录,于是我又手动安装mpich。在跑通baseline后才知道这里可以用icc,但是已经编译完了,我就懒得再改回去了(笑)。

这里手动安装mpich²

# 下载和解压
wget https://www.mpich.org/static/downloads/4.1/mpich-4.1.tar.gz
tar zxf mpich-4.1.tar.gz
cd mpich-4.1/
#接着编辑编译选项, prefix为安装路径, cc\cxx\cf分别为c\c++\fortran的编译器
./configure --prefix=/home/tangzehao/MPI/install cc=gcc cxx=g++ fc=gfortran
# 开始编译和安装
make -j4
make install
# 安装完成后将mpich写入环境变量
vim ~/.bashrc
# 键入i,追加以下指令
export PATH=/home/tangzehao/MPI/install/bin:$PATH
# 按Esc退出,输入:wq保存退出,然后更新
source ~/.bashrc

安装完mpich再继续编译parmetis,进入该文件夹,找到文件夹里的build-parmetis.sh脚本,编辑它的一些预设,然后再运行之。

vim build-parmetis.sh
## 键入I 进入插入模式,修改以下指令
make config cc=mpiicc prefix=/es01/paratera/sce0588/zl/ASC/parmetis-4.0.3/parmetis-install
## 修改cc=gcc, 追加cxx=g++, 将prefix修改为文件所在路径下的安装目录,我将其修改为以下
make config cc=mpicc prefix=$QUEST1/parmetis-4.0.3/parmetis-install
## 接着按Esc键退出编辑,键入:wq保存并退出,然后运行之
sh build-parmetis.sh

3. 编译hypre

进入文件夹,找到build-hypre.sh脚本,编辑然后运行之。

vim build-hypre.sh

## 键入I 进入插入模式,修改以下指令
./configure --prefix="/es01/paratera/sce0588/zl/ASC/hypre-2.28.0/install" --with-MPI --enable-shared
## 将prefix修改为文件所在路径下的安装目录,我将其修改为以下
./configure --prefix="$QUEST1/hypre-2.28.0/install" --with-MPI --enable-shared

## 接着按Esc键退出编辑,键入:wq保存并退出,然后运行之
sh build-hypre.sh

4. 编译petsc

这里一开始我总是无法通过编译,后来结合其他文章做了一些修改³

进入文件夹,找到build-petsc.sh脚本,对应的指令进行修改,然后运行之。

vim build-petsc.sh
## 键入I 进入插入模式

#!/bin/bash
## 这里将以下两条注释掉(虽然不知道是做什么的,但最终通过编译了(笑))
#source /es01/paratera/parasoft/module.sh  
#source /es01/paratera/parasoft/oneAPI/2022.1/setvars.sh
module load cmake/3.17.1 gcc/7.3.0-para 

# 这两条更改了路径到本文件夹,这两个变量后面还会再用到,应该与这里保持一致
export PETSC_DIR=$QUEST1/petsc-3.19.3
export PETSC_ARCH=petsc_install

./configure --with-mpi-dir=/home/tangzehao/MPI/install \  # 这里删掉了cc和cxx参数,改为使用mpich
    --with-fortran-bindings=0 \
    --with-hypre-dir=$QUEST1/hypre-2.28.0/install \       # 这里改为更改hypre所在目录
    --with-debugging=0 \
    COPTFLAGS="-O3" \
    CXXOPTFLAGS="-O3" \

make -j 20 PETSC_DIR=$QUEST1/petsc-3.19.3 PETSC_ARCH=petsc_install all # 这两个变量和上面保存一致
make all check

## 接着按Esc键退出编辑,键入:wq保存并退出,然后运行之
sh build-petsc.sh

5. 编译petsc_solver

进入文件夹,找到build-petscsolver.sh脚本,对应的指令进行修改,然后运行之。

vim build-petscsolver.sh
## 键入I 进入插入模式

export CC=mpicc  # 这两个改为mpi的编译器
export CXX=mpicxx

## 这两个lapack前缀改为自己所在的目录
export CPATH=$QUEST1/lapack-3.11/CBLAS/include:$QUEST1/lapack-3.11/LAPACKE/include:$CPATH
export LD_LIBRARY_PATH=$QUEST1/lapack-3.11:$LD_LIBRARY_PATH

## 接着按Esc键退出编辑,键入:wq保存并退出,然后运行之
sh build-petscsolver.sh

6. 编译OpenCAEPoro

进入文件夹,同样修改mpi-build-petsc.sh脚本,这里修改的地方比较多,都是把路径前缀改为自己的文件夹所在目录

vim mpi-build-petsc.sh
## 键入I 进入插入模式

export CC=mpicc  # 这两个改为mpi的编译器
export CXX=mpicxx

## 路径前缀改为源文件所在路径,PETSC_DIR和PET_ARCH和前面保持一致
# users specific directory paths
export PARMETIS_DIR=$QUEST1/parmetis-4.0.3
export PARMETIS_BUILD_DIR=$QUEST1/parmetis-4.0.3/build/Linux-x86_64
export METIS_DIR=$QUEST1/parmetis-4.0.3/metis
export METIS_BUILD_DIR=$QUEST1/parmetis-4.0.3/build/Linux-x86_64
export PETSC_DIR=$QUEST1/petsc-3.19.3
export PETSC_ARCH=petsc_install
export PETSCSOLVER_DIR=$QUEST1/petsc_solver


export CPATH=$QUEST1/petsc-3.19.3/include/:$CPATH
export CPATH=$QUEST1/petsc-3.19.3/petsc_install/include/:$QUEST1/parmetis-4.0.3/metis/include:$QUEST1/parmetis-4.0.3/include:$CPATH
export CPATH=$QUEST1/lapack-3.11/CBLAS/include/:$CPATH

## 接着按Esc键退出编辑,键入:wq保存并退出,然后运行之
sh mpi-build-petsc.sh

7. 测试运行OpenCAEPoro

进入OpenCAEPoro文件夹,修改参数p为进程数量,执行以下指令

mpirun -n p ./testOpenCAEPoro ./data/test/test.data

如果安装成功,测试样例将会在终端打印出运行时间等信息,在文件夹./data/test下也会生成新文件:SUMMARY.out和FastReview.out,如果进程数大于1还会生成statistics.out文件。

三、样例运行与优化

在OpenCAEPoro文件夹运行baseline,指定进程数量p,查看样例的运行时间

mpirun -np p ./testOpenCAEPoro ./data/case1/case1.data verbose=1

1.改变进程数量

由于官方文档说明进程数不超过10,所以一开始我设置的进程数是10,最终Wall Time为1778秒左右,Object Time为228.992秒,花费了比较长的时间。

后面尝试了一下提高进程数,还是可行的,由于服务器处理器为32核64线程,这里进程数改为64再跑一遍,最终Wall Time为682秒左右,Object Time为52.403秒,增幅336%,提升很大。

2.多节点并发运行

原理也是提高进程数量,只不过这里在多台设备上同时运行,主打一个力大砖飞(乐)。这里涉及到MPI的多节点运行和ssh免密登录,参考了一些资料

  • 首先需要设置节点间ssh免密登录,hosts文件已经配置好gpu01\gpu02\gpu03\gpu04的ip地址和主机名,可以直接使用主机名登录,在登录的主机生成公钥,由于服务器已经把文件都同步,每台主机同个用户上的文件都一样,因此不用再互相发送公钥,生成公钥后即可互相免密登录,同样也不需要每个可执行文件都拷贝到各主机上。
cd ~/.ssh
ssh-keygen -t rsa   # 一路回车
cat ~/.ssh/id_rsa.pun >> ~/.ssh/authorized_keys
  • 在用户目录新建一个配置文件hostfile,每个主机对应希望运行的进程数量,这里都设置为64
cd ~
vim hostfile

## 写入以下内容 ##
gpu01:64
gpu02:64
gpu03:64
gpu04:64
##
  • 接着开始执行,这里把执行指令写成文件用bash指令运行,方便运行和调整
vim run

## 指令内容
NODE=4          # 结点数量
P=64            # 每个结点的进程数量,当然也可以每个结点都不一样,具体在hostfile中调整
NP=$[$NODE*$P]  # 进程总数量,应该与hotsfile中所有结点加起来的进程数量一致

HOSTFILE=~/hostfile 

cd /home/tangzehao/OpenCAEPoro/OpenCAEPoro/
mpirun -n $NP -machinefile $HOSTFILE  ./testOpenCAEPoro ./data/case1/case1.data verbose=1   # 开始执行
##

bash run        # 执行文件中的指令

在用2台主机各64进程测试时,发现运行时间非但没有减少,反而增加了许多,Wall Time来到了2600秒,而Object Time为76秒,甚至于我在中途利用gpu04跑64进程都比这个测试先结束(悲)。

我不是很理解为什么多台主机一起计算,速度下降了不少,不过考虑到主机间的通信和数据交换也需要时间,可能是各进程太多导致的,毕竟每个核心都吃满的话,数据交换也会变慢,也许是如此吧,于是我又用4台主机,每台主机60进程再跑了一遍,果然快了不少,最终Wall Time为783秒,而Object Time也降低到了29.382秒,相较于单机增幅78%,效果拔群啊(喜)。

请添加图片描述

按照这样的逻辑再思考一下,在gpu01发起的并发,会不会需要它用更多的时间收集通信,而其他主机需要的时间更少呢,那么可以让其他三台主机的进程数多些,但也不全吃满,64个进程分63个来计算应该就够了,而1号机保持60个进程,于是我把主机配置设置为如下

gpu01:60
gpu02:63
gpu03:63
gpu04:63

按照这样的配置运行,在计算到第500个左右(总共3600个)就花了将近1600秒,所以我知道是不会更快了,因此提前结束了计算。

这样的结果也促进了我进一步思考,在查看cpu占用率时,我发现在每台主机60个进程的情况下,4台主机的cpu占用率都在93%左右(当前用户),也就是说主机间的数据交换其实并不会占用太多运算量,1号主机并不需要更多的空闲算力来收集数据。而gpu03由于有其他用户在进行计算,cpu总占用率达到了96%,因此这里猜测是gpu03在使用63个进程时,算力达到瓶颈,才拖慢了总体进程。

不过感觉再多几个线程提升也不会很大了,没有太多时间等4台主机都空闲,这里就先这样吧。

3.修改一些编译选项

  • 之前在编译parmetis时出过一些问题,在搜索时发现这个库可以根据系统设置编译选项¹,在parmetis/metis/include/metis.h 文件中,可以将IDXTYPEWIDTH和REALTYPEWIDTH从32修改为64,前者是int型变量的大小,后者决定浮点数使用单精度(32)还是双精度(64)。
#define IDXTYPEWIDTH 64
#define REALTYPEWIDTH 64

修改之后按照之前的步骤重新编译parmetis和OpenCAEPoro,在单机64进程的情况下,Wall Time为817秒左右,Object Time为55.211秒,相较于修改之前单机降幅5%,影响不大吧。

  • 之前编译时,所有的编译器都是mpi,并没有使用intel oneAPI的编译,而官方文档提示使用该库会更好,因此可以将编译器都修改为oneAPI的编译器,c编译使用icx,c++编译使用icpx
## 这里只给出修改的地方
source opt/intel/oneapi/setvars.sh  # 这里参考了https://blog.csdn.net/qq_41443388/article/details/124505277,也许本来已经可以用的,这里再手动更新一次。

# 这一条来自parmetis中的build-parmetis.sh脚本,将mpicc改为icx
make config cc=icx  prefix=$QUEST1/parmetis-4.0.3/parmetis-install

# 这一条来自petsc中的build-petsc.sh脚本,将--with-mpi-dir修改为cc和cxx,指定编译器
./configure cc=icx cxx=icpx \
    --with-fortran-bindings=0 \
    --with-hypre-dir=$QUEST1/hypre-2.28.0/install \
    --with-debugging=0 \
    COPTFLAGS="-O3" \
    CXXOPTFLAGS="-O3" \

# 这两条来自petsc_solver中的build-petscsolver.sh脚本,修改了编译器
# 另外,OpenCAEPoro中的mpi-build-petsc.sh也这样修改
export CC=icx
export CXX=icpx

完成上述修改后按照顺序重新编译各源文件,再重新编译OpenCAEPoro,运行样例,在单机64进程的情况下,Wall Time为539秒左右,Object Time为53.515秒,相较于之前的编译器,增幅3%,影响不大。多机4*60进程的情况下,Wall Time为352秒左右,Object Time为20.407秒,相较于编译选项修改之前,增幅43%,提升较大,也许oneAPI的编译器对多进程的优化更好。

请添加图片描述

4. 修改源代码

在修改代码前我想着换数学库的,但是这个东西的具体算法我也不了解,可能不同库的api不一样,最多就给一些库更新一些版本,所以还是看看代码有哪些地方能改的。

这里先用Vtune看一下各个函数在每个线程花费的时间,优化这些函数就行了,占用时间少的也没什么优化空间,不过这部分能修改的东西还是比较少,因为我也不知道这些函数具体实现什么功能,只能抠一抠细节了(难绷)。

运行一次用vtune查看一个进程的运行状态

请添加图片描述

在236秒捕获中,可修改的部分实际消耗的时间还是太短了,消耗最高的可修改部分是OCPFlux01::AssembleMatFIM,耗时1.587秒,占比不到0.7%,OCPFlux01::CalFlux耗时0.897秒,改一下这部分试试吧。

这部分代码变量太多了不好改,干脆把循环的i++都改成++i吧,后面的写法会快一点,但只有一点点。

另外,这里的if-else改成case也许会好点

## 原来的
        if ((exbegin) && (exend)) {
            rho[j] = (bvs.rho[bId_np_j] + bvs.rho[eId_np_j]) / 2;
        }
        else if (exbegin && (!exend)) {
            rho[j] = bvs.rho[bId_np_j];
        }
        else if ((!exbegin) && (exend)) {
            rho[j] = bvs.rho[eId_np_j];
        }
        else {
            upblock[j] = bId;
            rho[j]     = 0;
            flux_vj[j] = 0;
            continue;
        }

## 修改后
        int sigma = exbegin + 2 * exend;
        switch (sigma)
        {
        case 1:
            rho[j] = bvs.rho[bId_np_j];
            break;
        case 2:
            rho[j] = bvs.rho[eId_np_j];
            break;
        case 3:
            rho[j] = (bvs.rho[bId_np_j] + bvs.rho[eId_np_j]) / 2;
            break;
        default:
            upblock[j] = bId;
            rho[j] = 0;
            flux_vj[j] = 0;
            continue;
        }

重新编译后打算再跑一遍试试的,不过由于临近ddl,大家都在用服务器测试,没有空闲机器测试了,就没有再跑,先这样吧(叹气)

https://www.cnblogs.com/Orien/p/5919292.html

https://blog.csdn.net/x1131230123/article/details/129296417

https://blog.csdn.net/jiacong_wang/article/details/106723345

https://blog.csdn.net/liu_feng_zi_/article/details/108403321

https://blog.csdn.net/qq_41653433/article/details/135110603

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值