Fortran LAPACK: DSYEV使用高CPU%sys - 没有并行化 - 是否正常?

3

请看下方更新

当运行Fortran代码时,我发现系统CPU使用率很高。"用户CPU使用率"占用了大约一个核(系统是运行Linux的Intel i7,有4个核心/ 8个线程),而系统CPU却占用了大约2个核心(因此总CPU使用率约为75%)。有人能解释一下这是从哪里来的,以及这是否是"正常"行为吗?

我使用gfortran编译代码(优化关闭-O0,尽管这部分似乎并不重要),并链接BLAS、LAPACK和一些(其他)C函数。我的代码本身没有使用任何并行化,链接的代码也没有(据我所知)。至少我没有使用任何并行化的库版本。

代码本身涉及组装和求解有限元系统,并使用了大量(?)的分配和内置函数调用(matmul,dot_product),尽管总体RAM使用率非常低(约200MB)。我不知道这些信息是否足够/有用,但我希望有人知道那里出了问题。

此致敬礼, Ben

更新 我认为我已经追踪到(部分)问题,它来自于LAPACK中DSYEV的调用(计算实对称矩阵A的特征值,在我的情况下是3x3)。

program test

implicit none

integer,parameter :: ndim=3
real(8) :: tens(ndim,ndim)

integer :: mm,nn
real(8), dimension(ndim,ndim):: eigvec
real(8), dimension(ndim)   :: eigval

character, parameter    :: jobz='v'  ! Flags calculation of eigenvectors
character, parameter    :: uplo='u'  ! Flags upper triangular 
integer, parameter      :: lwork=102   ! Length of work array
real(8), dimension(lwork)  :: work      ! Work array
integer :: info   

tens(1,:) = [1.d0, 2.d0, 3.d0]
tens(2,:) = [2.d0, 5.d0, 1.d0]
tens(3,:) = [3.d0, 1.d0, 1.d0]   

do mm=1,5000000    
    eigvec=tens
   ! Call DSYEV
   call dsyev(jobz,uplo,ndim,eigvec,ndim,eigval,work,lwork,info)
enddo

write(*,*) eigvec
write(*,*) int(work(1))

endprogram test

编译和链接是通过以下方式完成的

gfortran test.f90 -o test -llapack

这个程序占用了我很高的%sys CPU使用率。有人能够验证一下吗?(显然LAPACK是运行代码所必需的)这是“正常”的情况还是我的代码/系统/库出了问题...? 更新2 受@roygvib评论的鼓舞,我在另一个系统上运行了代码。在第二个系统上,无法复现高CPU sys使用率。比较这两个系统,我似乎找不到这个问题的根源。它们都运行相同的操作系统版本(Linux Ubuntu),相同的gfortran版本(4.8),内核版本,LAPACK和BLAS。唯一的“主要”区别是:有缺陷的系统的处理器是i7-4770,而另一个系统是i7-870。在有缺陷的系统上运行测试代码给我%user 16s%sys 28s。在i7-870上,它是%user 16s%sys 0s。将代码运行四次(并行运行)使每个进程的总时间分别为在另一个系统上的约18s和在有缺陷的系统上的约44s
有什么其他措施可以寻找问题? 更新3 我认为我们正在接近: 在另一个系统上构建测试程序,并与LAPACK和BLAS库进行静态链接,
gfortran test.f90 -O0 /usr/lib/liblapack.a /usr/lib/libblas.a -Wl,--allow-multiple-definition

在有漏洞的系统上运行该代码,可以得到约为0的%sys时间(正如所期望的那样)。另一方面,在有漏洞的系统上使用静态链接到LAPACK和BLAS的测试程序并在另一个系统上运行代码会返回高CPU使用率。因此,很明显,这些库似乎存在差异,对吗? 在有漏洞的系统上构建静态版本会导致文件大小约为18MB,而在另一个系统上只有100KB。此外,我必须包含

-Wl,--allow-multiple-definition

只需在另一台系统上运行命令(否则会抱怨 xerbla 的多重定义),而在出现故障的系统上,我必须(明确地)链接到 libpthread

gfortran test.f90 -O0 /usr/lib/liblapack.a /usr/lib/libblas.a -lpthread -o test

有趣的是,
apt-cache policy liblapack*

对于这两个系统,它们返回相同的版本和repo目的地(libblas*也是如此)。还有其他的想法吗?也许有其他命令可以检查库版本,我不知道吗?


请查看此答案中嵌入的nmon开发人员的评论:https://dev59.com/V1bTa4cB1Zd3GeqP-Wvg#5738139。如果没有重现问题的方法,这个问题很可能会被视为离题(尽管它可能很有趣)。祝你好运。 - shellter
我目前正在处理的模型只有大约4000个元素。但是大小似乎并不是问题,因为我甚至可以使用更小的模型来重现这种行为。问题在于:使用基于Fortran的商业FEM代码时,仅使用了一个真正的单核心(0%系统CPU使用率)。因此,我不明白@shellter提示的评论的意义所在。 - PrinceOfMe
你的 LAPACK 是否是多线程的?你的 LAPACK 和 BLAS 实现来自哪里? - Vladimir F Героям слава
他们来自哪个代码库? - Vladimir F Героям слава
这就是为什么我5天前就问你的LAPACK和BLAS实现来自哪里了!!!OpenBLAS通常比参考netlib BLAS快得多。 - Vladimir F Героям слава
显示剩余13条评论
1个回答

2
我的解释是:
使用了一个线程化的(可能是OpenMP)LAPACK和BLAS版本。它们尝试在并行计算中启动多个线程来解决线性代数问题,这通常会加速计算。
然而,在这种情况下,可能出现以下问题:
do mm=1,5000000    
   eigvec=tens
   call dsyev(jobz,uplo,ndim,eigvec,ndim,eigval,work,lwork,info)
enddo

这是为了一个非常小的问题(一个3x3矩阵)而多次调用库。这不能有效地并行解决,因为矩阵太小。线程同步所带来的开销占据了解决时间的主导地位。同步(甚至是线程创建)被执行了5000000次!
解决方法:
1. 使用非线程化的BLAS和LAPACK。 2. 如果使用OpenMP进行并行化,请设置OMP_NUM_THREADS=1,这意味着只使用一个线程。 3. 不要使用LAPACK,因为对于特殊情况3x3,有专门的算法可用。 https://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices

非常感谢您提供答案。虽然我设置了OPENBLAS_NUM_THREADS=1,但我仍然坚持使用openblas。我提供的测试程序只是一个小例子,除了解决3x3矩阵的特征值之外,还有更多LAPACK/BLAS的调用。尽管如此,我已经将这些小系统的调用从LAPACK更改为更快的求解器。感谢您的提示! - PrinceOfMe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接