allocator.f90 Source File

! backends/omp/target/allocator.f90

Implements an allocator specialised to OMP target offloading


This file depends on

sourcefile~~allocator.f90~2~~EfferentGraph sourcefile~allocator.f90~2 allocator.f90 sourcefile~allocator.f90 allocator.f90 sourcefile~allocator.f90~2->sourcefile~allocator.f90 sourcefile~common.f90 common.f90 sourcefile~allocator.f90~2->sourcefile~common.f90 sourcefile~field.f90 field.f90 sourcefile~allocator.f90~2->sourcefile~field.f90 sourcefile~mesh.f90 mesh.f90 sourcefile~allocator.f90~2->sourcefile~mesh.f90 sourcefile~allocator.f90->sourcefile~common.f90 sourcefile~allocator.f90->sourcefile~field.f90 sourcefile~mpi.f90 mpi.f90 sourcefile~common.f90->sourcefile~mpi.f90 sourcefile~field.f90->sourcefile~common.f90 sourcefile~mesh.f90->sourcefile~common.f90 sourcefile~mesh.f90->sourcefile~field.f90 sourcefile~decomp_dummy.f90 decomp_dummy.f90 sourcefile~mesh.f90->sourcefile~decomp_dummy.f90 sourcefile~mesh_content.f90 mesh_content.f90 sourcefile~mesh.f90->sourcefile~mesh_content.f90 sourcefile~mesh.f90->sourcefile~mpi.f90 sourcefile~decomp_dummy.f90->sourcefile~mesh_content.f90 sourcefile~mesh_content.f90->sourcefile~common.f90

Files dependent on this one

sourcefile~~allocator.f90~2~~AfferentGraph sourcefile~allocator.f90~2 allocator.f90 sourcefile~backend.f90 backend.f90 sourcefile~backend.f90->sourcefile~allocator.f90~2

Source Code

!!! backends/omp/target/allocator.f90
!!
!! Implements an allocator specialised to OMP target offloading

module m_omptgt_allocator

  use iso_c_binding, only: c_ptr, c_f_pointer, &
                           c_sizeof, c_associated, c_null_ptr
  use omp_lib, only: omp_target_alloc, omp_target_free, &
                     omp_get_default_device, omp_get_num_devices, &
                     omp_get_initial_device

  use m_common, only: dp

  use m_allocator, only: allocator_t
  use m_mesh, only: mesh_t
  use m_field, only: field_t

  implicit none

  private
  public :: omptgt_allocator_t
  public :: omptgt_field_t

  type, extends(allocator_t) :: omptgt_allocator_t
  contains
    procedure :: create_block => create_block_omptgt
  end type omptgt_allocator_t

  interface omptgt_allocator_t
    module procedure omptgt_allocator_init
  end interface omptgt_allocator_t

  type, extends(field_t) :: omptgt_field_t
    !! A device-resident field.
    !!
    integer, private :: n
    integer, dimension(3), private :: dims
    integer, private :: dev_id
    type(c_ptr), private :: dev_ptr = c_null_ptr
  contains
    procedure :: fill => fill_omptgt
    procedure :: get_shape => get_shape_omptgt
    procedure :: set_shape => set_shape_omptgt
    procedure :: get_dev_ptr
    final :: omptgt_field_destroy
  end type omptgt_field_t

contains

  type(omptgt_allocator_t) function omptgt_allocator_init(dims, sz) result(a)
    !! Constructs the allocator, leaving the sizing of the memory blocks it
    !! hands out to the base allocator.
    integer, intent(in) :: dims(3)
    integer, intent(in) :: sz

    a%allocator_t = allocator_t(dims, sz)
  end function omptgt_allocator_init

  function create_block_omptgt(self, next) result(ptr)
    !! Creates a new device-resident block and links it in front of `next` in
    !! the allocator's list of blocks.
    class(omptgt_allocator_t), intent(inout) :: self
    class(field_t), pointer, intent(in) :: next
    class(field_t), pointer :: ptr

    self%next_id = self%next_id + 1
    allocate (omptgt_field_t :: ptr)
    select type (ptr)
    type is (omptgt_field_t)
      call omptgt_field_init(self%ngrid, next, id=self%next_id, f=ptr)
    class default
      error stop "Allocation failed to set type"
    end select

  end function create_block_omptgt

  subroutine omptgt_field_init(ngrid, next, id, f)
    !! Initialises a field and allocates its `ngrid` points of storage on the
    !! default target device. No device means offloading was not enabled at
    !! build time, so this errors out rather than silently running on the host.
    integer, intent(in) :: ngrid
    class(field_t), pointer, intent(in) :: next
    integer, intent(in) :: id
    type(omptgt_field_t) :: f

    f%refcount = 0
    f%next => next
    f%id = id

    if (omp_get_num_devices() < 1) then
      error stop "No OpenMP target device available, was offloading enabled?"
    end if
    f%dev_id = omp_get_default_device()
    if (f%dev_id == omp_get_initial_device()) then
      error stop "Device ID is HOST"
    end if

    f%n = ngrid
    f%dims = -1
    f%dev_ptr = omp_target_alloc(f%n*c_sizeof(0.0_dp), f%dev_id)
    if (.not. c_associated(f%dev_ptr)) then
      error stop "omp_target_alloc failed"
    end if

  end subroutine omptgt_field_init

  subroutine omptgt_field_destroy(self)
    !! Frees the device allocation when the field is finalised.
    type(omptgt_field_t) :: self

    if (c_associated(self%dev_ptr)) then
      call omp_target_free(self%dev_ptr, self%dev_id)
      self%dev_ptr = c_null_ptr
    end if
  end subroutine

  subroutine fill_omptgt(self, c)
    !! Sets every point of the field, padding included, to the constant `c`.
    class(omptgt_field_t) :: self
    real(dp), intent(in) :: c

    call fill_omptgt_(self%dev_ptr, c, self%n)

  end subroutine fill_omptgt

  subroutine fill_omptgt_(dev_ptr, c, n)
    !! Offloaded kernel behind `fill_omptgt`. Takes the device pointer rather
    !! than the field so the target region needs no mapping.
    type(c_ptr), intent(in) :: dev_ptr
    real(dp), intent(in) :: c
    integer, intent(in) :: n

    real(dp), dimension(:), pointer :: p_data_tgt
    integer :: i

    call c_f_pointer(dev_ptr, p_data_tgt, shape=[n])
    !$omp target is_device_ptr(dev_ptr)
    !$omp teams loop
    do i = 1, n
      p_data_tgt(i) = c
    end do
    !$omp end teams loop
    !$omp end target

  end subroutine

  function get_shape_omptgt(self) result(dims)
    !! Returns the shape the field is currently viewed with, or -1s if
    !! `set_shape` has not been called yet.
    class(omptgt_field_t) :: self
    integer :: dims(3)

    dims = self%dims
  end function

  subroutine set_shape_omptgt(self, dims)
    !! Sets the shape the field is viewed with. Unlike the host and CUDA
    !! fields there is no array pointer to reshape here, so this only records
    !! the extents later used to cast the device pointer into an array.
    class(omptgt_field_t) :: self
    integer, intent(in) :: dims(3)

    if (product(dims) > self%n) then
      error stop "Trying to set shape of field greater than its capacity"
    end if
    self%dims = dims

  end subroutine

  type(c_ptr) function get_dev_ptr(self) result(ptr)
    !! Returns the device pointer to the field's storage, for use in the
    !! `is_device_ptr` clause of an offloaded region.
    class(omptgt_field_t) :: self

    ptr = self%dev_ptr
  end function

end module m_omptgt_allocator