You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenACC操作派生类型成员时内核非法地址错误及子例程问题排查

OpenACC移植Fortran派生类型代码的问题解析

测试代码

模块定义

module test
implicit none
!
type test_type
  integer                            :: n1,n2,n3
  integer, pointer, dimension(:,:,:) :: dat
end type test_type
!
contains
  subroutine copydat(datain,dataout,indi,inde)
    !$ACC ROUTINE VECTOR
    implicit none
    integer, intent(in)          :: indi, inde
    type(test_type), intent(in)  :: datain
    type(test_type), intent(out) :: dataout
    !
    dataout%dat(:,indi:inde,:) = datain%dat(:,indi:inde,:)
  end subroutine copydat
  !
  subroutine get_indices(n2,i_chunk,n_chunks,indi,inde)
    !$ACC ROUTINE SEQ
    implicit none
    integer, intent(in)  :: n2, i_chunk, n_chunks
    integer, intent(out) :: indi, inde
    !
    indi = 1 + (i_chunk - 1)*n2/n_chunks
    inde = i_chunk*n2/n_chunks
  end subroutine get_indices
end module test

主程序

program main
use openacc
use test
implicit none
integer         :: n_chunks, i_chunk, n1, n2, n3, i1, i2, i3, indi, inde
type(test_type) :: data1, data2

open(10,file='num.txt')
read(10,*) n1
read(10,*) n2
read(10,*) n3
read(10,*) n_chunks
close(10)

write(*,'(4(A,I3),A)') 'Data of size ', n1 , ', ', n2, ', ', n3, ', splitted in ', n_chunks, ' chunks along dim2'

data1%n1 = n1
data1%n2 = n2
data1%n3 = n3
data2%n1 = n1
data2%n2 = n2
data2%n3 = n3
allocate(data1%dat(n1,n2,n3),data2%dat(n1,n2,n3))
do i3 = 1,n3
  do i2 = 1,n2
    do i1 = 1,n1
      data1%dat(i1,i2,i3) = i1 + (i2-1)*n1 + (i3-1)*n1*n2
    end do
  end do
end do

!$ACC ENTER DATA COPYIN(data1,data2)
!$ACC ENTER DATA CREATE(data1%dat,data2%dat)
!$ACC UPDATE DEVICE(data1%dat)

!$ACC PARALLEL DEFAULT(present) PRIVATE(indi,inde)
!$ACC LOOP GANG INDEPENDENT PRIVATE(indi,inde)
do i_chunk=1,n_chunks
  ! --- Current chunk indices ---
  call get_indices(n2,i_chunk,n_chunks,indi,inde)
  print *, "#", __pgi_gangidx(), ": ", indi, inde
  data2%dat(:,indi:inde,:) = data1%dat(:,indi:inde,:)
  !call copydat(data1,data2,indi,inde)
end do
!$ACC END LOOP
!$ACC END PARALLEL

!$ACC UPDATE SELF(data2%dat)
!$ACC EXIT DATA DELETE(data1%dat,data2%dat)
!$ACC EXIT DATA DELETE(data1,data2)

print *, maxval(abs(data1%dat-data2%dat))

deallocate(data1%dat,data2%dat)

end program main

测试环境

  • num.txt内容:128、256、32、64(每个gang处理128×4×32的数据块)
  • 硬件:Tesla P100
  • 编译命令:nvfortran -acc -ta=tesla:cc60 -fast -Minfo=accel -o main.exe test.F90

问题与解答

1. PRIVATE子句组合崩溃的原因

问题现象

四种PRIVATE子句组合中仅Try D崩溃(报错:call to cuStreamSynchronize returned error 700: Illegal address during kernel execution):

  • Try A:!$ACC PARALLEL DEFAULT(present) + !$ACC LOOP GANG INDEPENDENT(正常)
  • Try B:!$ACC PARALLEL DEFAULT(present) PRIVATE(indi,inde) + !$ACC LOOP GANG INDEPENDENT PRIVATE(indi,inde)(正常)
  • Try C:!$ACC PARALLEL DEFAULT(present) PRIVATE(indi,inde) + !$ACC LOOP GANG INDEPENDENT(正常)
  • Try D:!$ACC PARALLEL DEFAULT(present) + !$ACC LOOP GANG INDEPENDENT PRIVATE(indi,inde)(崩溃)

解答

这是nvfortran对OpenACC私有变量作用域的处理逻辑导致的:

  • Try A:无显式PRIVATE子句,indi/inde默认是共享变量,但每个gang独立执行循环迭代时,编译器会隐式为每个gang创建私有副本,因此无冲突。
  • Try B/C:在PARALLEL或LOOP层级显式声明PRIVATE,indi/inde为gang层级私有变量,每个gang拥有独立副本,访问合法。
  • Try D:仅在LOOP GANG层级声明PRIVATE,PARALLEL层级未声明。此时indi/inde本质是PARALLEL区域的共享变量,内层强行PRIVATE会导致存储混淆,触发非法地址访问。

OpenACC标准中,LOOP子句的PRIVATE仅作用于循环迭代的gang,但变量存储生命周期由外层PARALLEL区域决定,外层未声明PRIVATE时,内层PRIVATE会引发存储冲突。


2. 调用copydat子例程的异常行为

问题现象

  • 直接执行数组赋值:正常运行,vector_length=128。
  • 调用copydat:vector_length变为32且崩溃;n2改为128时不崩溃但vector_length仍为32。

解答

  1. vector_length变化原因:
    copydat被声明为!$ACC ROUTINE VECTOR,表示由向量线程执行,nvfortran会为向量例程默认分配较小的vector_length(如32);而直接数组赋值时,编译器会自动适配硬件设置vector_length(P100默认128)。

  2. 崩溃原因:
    n2=256时,每个gang处理的是非连续数组切片,copydat中的数组赋值由向量线程并行执行,但编译器未正确处理派生类型指针的设备地址映射,导致非法内存访问。n2=128时切片偏移量变小,映射错误未触发,但属于未定义行为。

    修复方案:将copydat的!$ACC ROUTINE VECTOR改为!$ACC ROUTINE SEQ(该子例程为每个gang独立执行,无需向量并行);或在子例程内部添加显式OpenACC循环指令明确并行区域。


3. 调用deldat子例程释放内存崩溃

问题现象

将主程序显式deallocate替换为调用deldat子例程时崩溃,编译输出存在差异。

解答

这是nvfortran对派生类型+OpenACC的支持不足导致的,并非预期行为:

  • 显式deallocate时,编译器会自动关联OpenACC数据区域的释放逻辑(设备端数据已通过EXIT DATA DELETE清理,主机端释放合法)。
  • 调用deldat时,编译器未识别到该子例程释放的是已注册到OpenACC数据区域的指针,设备端映射信息未正确清理,导致主机端释放时地址错误。

修复方案:

  • 在deldat中添加OpenACC指令显式同步设备数据:
    subroutine deldat(data0)
      implicit none
      type(test_type), intent(inout)  :: data0
      !$ACC ENTER DATA PRESENT(data0)
      deallocate(data0%dat)
      !$ACC EXIT DATA DELETE(data0%dat)
    end subroutine deldat
    
  • 或确保在调用deldat前,设备端数据已通过EXIT DATA完全释放。

内容的提问来源于stack exchange,提问作者Fabio Riva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 02:35:01