使用Intel Fortran编译器赋值操作中的内存泄漏问题及解决问询
Fortran自定义类型元素赋值与运算符重载导致内存泄漏的原因及通用规避方法
问题背景
以下是最小复现代码:
module lib type FG_t real,allocatable::g(:) contains procedure,private::FG_Assign generic::assignment(=)=>FG_Assign end type interface operator(-) procedure FG_Sub end interface contains elemental subroutine FG_Assign(this,that) class(FG_t),intent(inout)::this class(FG_t),intent(in)::that this%g=that%g end elemental type(FG_t) function FG_Sub(this,that) class(FG_t),intent(in)::this real,intent(in)::that FG_Sub=FG_t(this%g-that) end end program prog use lib type(FG_t)::arr(1000),arr_(SIZE(arr)) do i=1,SIZE(arr) allocate(arr(i)%g(10)) end do do i=1,100000 arr_=arr-1. end do end
使用ifx(2022.2.1)、ifort(2021.7.1)、nvfortran(22.9)或nagfor(7.1)编译运行时,内存会快速占用(迭代次数过高可能导致系统崩溃),内存随时间变化曲线如下:
使用gfortran(11.1.0)或将FG_assign前的elemental关键字替换为pure,可解决Intel编译器版本下的问题,但对Nvidia和NAG编译器无效。Intel VTune性能分析器显示,内存主要在arr_=arr-1.调用FG_Sub后,于this%g=that%g行分配。
原因分析
问题核心在于elemental赋值子例程对可分配组件的内存管理逻辑:
- Elemental过程要求以标量方式逐个处理数组元素,当执行数组赋值
arr_=arr-1.时,编译器会为每个元素单独调用FG_Assign。 - 对于
this%g=that%g这行代码,Fortran标准允许编译器在this%g已分配且大小匹配时直接复用内存,但部分编译器(ifx/ifort/nvfortran/nagfor)在elemental场景下未正确实现这一优化:每次调用赋值子例程时,都为this%g重新分配内存,却未释放原已分配的内存,导致内存泄漏。 - 将
elemental改为pure后,Intel编译器能优化数组级别的内存复用,但Nvidia和NAG编译器对pure过程的内存管理逻辑仍存在差异,因此问题未解决。
通用规避方法
1. 显式管理可分配组件的内存
在赋值子例程中主动检查组件的分配状态,复用已匹配的内存,避免重复分配:
elemental subroutine FG_Assign(this,that) class(FG_t),intent(inout)::this class(FG_t),intent(in)::that if (allocated(this%g)) then ! 若已分配且大小匹配,直接赋值 if (size(this%g) == size(that%g)) then this%g = that%g return end if ! 大小不匹配则释放原有内存 deallocate(this%g) end if ! 未分配或已释放,重新分配并赋值 allocate(this%g(size(that%g))) this%g = that%g end subroutine FG_Assign
这种方式强制显式管理内存,不受编译器优化逻辑影响,可在所有测试编译器中解决内存泄漏问题。
2. 改用数组级别的赋值实现
若业务场景允许,可放弃elemental赋值,转而定义处理数组的pure子例程,直接对整个数组的可分配组件进行批量内存管理,减少编译器逐元素处理的不确定性。
内容的提问来源于stack exchange,提问作者V T
相关产品推荐
相关产品推荐

