1

在使用 OpenACC 加速的 Fortran 程序上,我需要在 GPU 上复制一个数组。复制的数组只会在 GPU 上使用,永远不会在主机上复制。我知道创建它的唯一方法是在主机上声明和分配它,然后acc data create它:

program test
    implicit none
    integer, parameter :: n = 1000
    real :: total
    real, allocatable :: array(:)
    real, allocatable :: array_d(:)

    allocate(array(n))
    allocate(array_d(n))

    array(:) = 1e0

    !$acc data copy(array) create(array_d) copyout(total)

    !$acc kernels
    array_d(:) = array(:)
    !$acc end kernels

    !$acc kernels
    total = sum(array_d)
    !$acc end kernels

    !$acc end data

    print *, sum(array)
    print *, total

    deallocate(array)
    deallocate(array_d)
end program

这是一个插图代码,因为所讨论的程序要复杂得多。

这个解决方案的问题是我必须allocate在主机上复制数组,即使我在这里不使用它。一些主机内存会被浪费,特别是对于大型阵列(即使我知道在主机内存用完之前我会用完设备内存)。在 CUDA Fortran 上,我知道我可以声明一个仅设备数组,但我不知道这是否可以通过 OpenACC 实现。

有没有更好的方法来执行此操作?

4

1 回答 1

2

OpenACC 规范有“acc declare device_resident”,它分配一个您将使用的仅设备数组,而不是“数据创建”。就像是:

    implicit none
    integer, parameter :: n = 1000
    real :: total
    real, allocatable :: array(:)
    real, allocatable :: array_d(:)
    !$acc declare device_resident(array_d)
    allocate(array(n))
    allocate(array_d(n))

    array(:) = 1e0

    !$acc data copy(array) copyout(total)

    !$acc kernels
    array_d(:) = array(:)
    !$acc end kernels

    !$acc kernels
    total = sum(array_d)
    !$acc end kernels

    !$acc end data

    print *, sum(array)
    print *, total

    deallocate(array)
    deallocate(array_d)
end program

尽管由于实现的复杂性和缺乏令人信服的用例,我们的编译器(NVHPC aka PGI)将 device_resident 视为创建,即仍然分配主机数组。因此,如果您使用的是 NVHPC 并且确实需要一个仅设备阵列,那么您需要在阵列上使用 CUDA Fortran“设备”属性。CUDA Fortran 和 OpenACC 是可互操作的,因此可以混合使用。

但是,对于绝大多数代码来说,浪费一点主机内存并不是问题,而且由于没有复制数据,因此不会影响性能。因此,如果您按原样保留代码,那应该不是问题。

于 2021-09-17T15:50:45.183 回答