OpenMP并行代码在64核节点出现Segmentation Fault问题排查求助
OpenMP并行段引发段错误的问题分析
问题背景
串行代码运行正常,但在64核、230G内存的节点上运行OpenMP并行版本时,出现Segmentation fault/longjmp causes uninitialized stack frame错误,已在提交脚本中设置OMP_STACKSIZE=30G(从10G提升至30G)。并行段代码如下:
!$omp parallel do shared (SEE) private (SKE,m,n,i,j,delta,inv_tau_delta,E, ek,eqv) REDUCTION(+:tot_delta,tot_inv_tau_delta) DO m=0,3100 DO n=m,3100 tot_delta=zero tot_inv_tau_delta=zero E=n*DeltaE DO i=1,Nk_BZ DO j=1,nbnd delta = w0gauss((E-ek(eqv(i),j))/degaussw)/degaussw inv_tau_delta=SKE(eqv(i),j,m)*delta tot_delta=tot_delta+delta tot_inv_tau_delta=tot_inv_tau_delta+inv_tau_delta ENDDO ! i ENDDO ! j SEE(n,m)=tot_inv_tau_delta/tot_delta SEE(m,n)=SEE(n,m)*exp((real(m-n))/kT) ENDDO ENDDO
其中数组SKE维度为SKE(44226,4,3100),函数w0gauss定义在代码末尾。
并行段的核心问题
1. 大数组被错误声明为私有变量
代码中private列表包含了SKE、ek、eqv这三个大数组:
SKE是规模为44226×4×3100的数组,每个线程若在栈上复制一份,内存开销会极其巨大(单份SKE的内存就远超30G)。64个线程同时复制这类大数组,直接会耗尽栈空间,触发段错误。- 这些数组仅被线程读取,无需修改,完全应该设为
shared(OpenMP默认变量作用域就是shared,无需显式声明,只要从private列表中移除即可)。
2. 其他潜在影响因素
- Reduction变量初始化冗余:
tot_delta和tot_inv_tau_delta是reduction变量,OpenMP会自动为每个线程初始化私有副本,循环内的tot_delta=zero和tot_inv_tau_delta=zero属于冗余操作,但不会直接引发段错误。 - 并行粒度不均衡:仅并行最外层
m循环,不同m对应的n循环迭代次数差异较大(n从m到3100),会导致线程负载不均,但这是性能问题,不是段错误的直接原因。 w0gauss函数的栈使用:若w0gauss内部定义了大的局部数组,也可能引发栈溢出,需检查该函数的实现,必要时将局部大数组改为静态分配或动态分配。
修正建议
- 修正
parallel do的变量作用域声明,仅将真正需要私有循环变量和临时变量放入private列表:
!$omp parallel do shared (SEE, SKE, ek, eqv) private (m,n,i,j,delta,inv_tau_delta,E) REDUCTION(+:tot_delta,tot_inv_tau_delta)
(注:SKE、ek、eqv若默认是shared,也可省略显式的shared声明,只需从private中移除)
检查
w0gauss函数内部是否存在大局部数组,若有则调整分配方式。若负载不均衡问题明显,可考虑重构循环,将
m和n的组合作为并行单元,提升并行效率。
内容的提问来源于stack exchange,提问作者Vahid Askarpour
相关产品推荐
相关产品推荐

