Fortran OpenACC 使用函数指针调用设备上的函数

Fortran OpenACC invoking a function on device using a function pointer

如何通过函数指针访问设备上的函数?

在下面的代码中,我尝试使用函数指针 init 访问 init0init1。如果在编译期间未启用 OpenACC,则代码会按预期工作。但是,当使用 OpenACC 编译时它会失败。下面的代码保存为 Whosebug2.f95:

module modTest2

  use openacc
  implicit none

  
  type :: Container
    sequence
    integer :: n
    integer, allocatable :: arr(:)
  end type Container


  interface Container
    procedure :: new_Container
  end interface 


  abstract interface
    integer function function_template (i)
      integer, intent (in) :: i
    end function function_template
  end interface


  contains
  
    type(Container) function new_Container(n)
      integer, intent(in) :: n

      allocate(new_Container%arr(n))
    end function new_Container    
end module modTest2

program test2

  use modTest2
  implicit none


  integer :: n, x, i
  type(Container) :: c
  procedure(function_template), pointer :: init


  print *, "Enter array size: "
  read *, n
  print *, "Allocating..."
  c = Container(n)
  print *, "Allocation complete!"
  
  
  print *, "Enter initialization type (x): "
  read *, x
  print *, "Initializing..."
  select case (x)
    case (0)
      init => init0
    case default
      init => init1
  end select
  !$acc data copyin(c) copyout(c%arr)
  !$acc parallel loop present(c)
  do i = 1, n
    c%arr(i) = init(i)
  end do
  !$acc end data
  print *, "Initialization complete..."


  do i = 1, n
    print *, i, c%arr(i)
  end do
  
  
  contains
  
    integer function init0(i)
      !$acc routine
      integer, intent(in) :: i
      init0 = 10*i
    end function init0

    
    integer function init1(i)
      !$acc routine
      integer, intent(in) :: i
      init1 = 20*i
    end function init1
end program test2

在没有 OpenACC 的情况下看到正确的输出:

$ gfortran -c Whosebug2.f95
$ gfortran Whosebug2.o -o a.out
$ ./a.out
 Enter array size: 
3
 Allocating...
 Allocation complete!
 Enter initialization type (x): 
0
 Initializing...
 Initialization complete...
           1          10
           2          20
           3          30

下面是 OpenACC 的错误输出(注意这里使用的是 NVIDIA 编译器):

$ /opt/nvidia/hpc_sdk/Linux_x86_64/22.1/compilers/bin/nvfortran Whosebug2.f95 -acc; ./a.out
 Enter array size: 
3
 Allocating...
 Allocation complete!
 Enter initialization type (x): 
0
 Initializing...
 Initialization complete...
            1            0
            2            0
            3            0

抱歉,设备尚不支持函数指针(以及 C++ 虚函数)。添加编译器反馈标志 (-Minfo=accel),您将看到以下消息:

% nvfortran -acc -Minfo=accel test.f90
test2:
     62, Generating copyout(c%arr(:)) [if not already present]
         Generating copyin(c) [if not already present]
     65, Accelerator restriction: Indirect function/procedure calls are not supported

问题在于间接函数需要设备跳转 table 和当前不可用的运行时动态链接。虽然我没有时间表,但我们正在探索未来如何提供此支持的选项。

在下面使用 gfortran-11 就可以了:

module modTest2

  use openacc
  implicit none

  
  type :: Container
    sequence
    integer :: n
    integer, allocatable :: arr(:)
  end type Container


  interface Container
    procedure :: new_Container
  end interface 


  abstract interface
    integer function function_template (i)
      integer, intent (in) :: i
    end function function_template
  end interface


  contains
  
    type(Container) function new_Container(n)
      integer, intent(in) :: n

      allocate(new_Container%arr(n))
    end function new_Container    
end module modTest2

program test2

  use modTest2
  implicit none


  integer :: n, x, i
  type(Container) :: c
  procedure(function_template), pointer :: init


  print *, "Enter array size: "
  read *, n
  print *, "Allocating..."
  c = Container(n)
  print *, "Allocation complete!"
  
  
  print *, "Enter initialization type (x): "
  read *, x
  print *, "Initializing..."
  select case (x)
    case (0)
      init => init0
    case default
      init => init1
  end select
  !$acc enter data copyin(c)
  !$acc enter data create(c%arr)
  !$acc parallel loop present(c)
  do i = 1, n
    c%arr(i) = init(i)
  end do
  !$acc exit data copyout(c%arr)
  !$acc exit data delete(c)
  print *, "Initialization complete..."


  do i = 1, n
    print *, i, c%arr(i)
  end do
  
  
  contains
  
    integer function init0(i)
      !$acc routine
      integer, intent(in) :: i
      init0 = 10*i
    end function init0

    
    integer function init1(i)
      !$acc routine
      integer, intent(in) :: i
      init1 = 20*i
    end function init1
end program test2

这是输出:

$ gfortran-11 -fopenacc Whosebug2.f95
$ gfortran-11 -fopenacc Whosebug2.o -o Whosebug2
$ ./Whosebug2 
 Enter array size: 
4
 Allocating...
 Allocation complete!
 Enter initialization type (x): 
0
 Initializing...
 Initialization complete...
           1          10
           2          20
           3          30
           4          40
$ ./Whosebug2 
 Enter array size: 
4
 Allocating...
 Allocation complete!
 Enter initialization type (x): 
9
 Initializing...
 Initialization complete...
           1          20
           2          40
           3          60
           4          80