Array Shaping
6.8. Array Shaping#
A pointer variable can be shaped to a multi-dimensional array to facilitate data access. This is achieved by a shape-operator casted in front of a pointer (lvalue expression):
([ s1][ s2]…[ sn]) pointer
where each si is an integral-type expression of positive value. The shape-operator can appear in either the motion-clause of the target update directive or the depend clause.
The following example shows the use of the shape-operator in the target update directive. The shape-operator ([nx][ny+2]) casts pointer variable a to a 2-dimentional array of size nx x (ny+2) . The resulting array is then accessed as array sections (such as [0:nx][1] and [0:nx][ny]) in the from or to clause for transferring two columns of noncontiguous boundary data from or to the device. Note the use of additional parentheses around the shape-operator and a to ensure the correct precedence over array-section operations.
//%compiler: clang
//%cflags: -fopenmp
/*
* name: array_shaping.1
* type: C
* version: omp_5.1
*/
#pragma omp begin declare target
int do_work(double *a, int nx, int ny);
int other_work(double *a, int nx, int ny);
#pragma omp end declare target
void exch_data(double *a, int nx, int ny);
void array_shaping(double *a, int nx, int ny)
{
// map data to device and do work
#pragma omp target data map(a[0:nx*(ny+2)])
{
// do work on the device
#pragma omp target // map(a[0:nx*(ny+2)]) is optional here
do_work(a, nx, ny);
// update boundary points (two columns of 2D array) on the host
// pointer is shaped to 2D array using the shape-operator
#pragma omp target update from( (([nx][ny+2])a)[0:nx][1], \
(([nx][ny+2])a)[0:nx][ny] )
// exchange ghost points with neighbors
exch_data(a, nx, ny);
// update ghost points (two columns of 2D array) on the device
// pointer is shaped to 2D array using the shape-operator
#pragma omp target update to( (([nx][ny+2])a)[0:nx][0], \
(([nx][ny+2])a)[0:nx][ny+1] )
// perform other work on the device
#pragma omp target // map(a[0:nx*(ny+2)]) is optional here
other_work(a, nx, ny);
}
}
The shape operator is not defined for Fortran. Explicit array shaping of procedure arguments can be used instead to achieve a similar goal. Below is the Fortran-equivalent of the above example that illustrates the support of transferring two rows of noncontiguous boundary data in the target update directive.
!!%compiler: gfortran
!!%cflags: -fopenmp
! name: array_shaping.1
! type: F-free
! version: omp_5.2
module m
interface
subroutine do_work(a, nx, ny)
!$omp declare target enter(do_work)
integer, intent(in) :: nx, ny
double precision a(0:nx+1,ny)
end subroutine do_work
subroutine other_work(a, nx, ny)
!$omp declare target enter(other_work)
integer, intent(in) :: nx, ny
double precision a(0:nx+1,ny)
end subroutine other_work
subroutine exch_data(a, nx, ny)
integer, intent(in) :: nx, ny
double precision a(0:nx+1,ny)
end subroutine exch_data
end interface
end module m
subroutine array_shaping(a, nx, ny)
use m
implicit none
integer, intent(in) :: nx, ny
double precision a(0:nx+1,ny)
! map data to device and do work
!$omp target data map(a)
! do work on the device
!$omp target ! map(a) is optional here
call do_work(a, nx, ny)
!$omp end target
! update boundary points (two rows of 2D array) on the host.
! data transferred are noncontiguous
!$omp target update from( a(1,1:ny), a(nx,1:ny) )
! exchange ghost points with neighbors
call exch_data(a, nx, ny)
! update ghost points (two rows of 2D array) on the device.
! data transferred are noncontiguous
!$omp target update to( a(0,1:ny), a(nx+1,1:ny) )
! perform other work on the device
!$omp target ! map(a) is optional here
call other_work(a, nx, ny)
!$omp end target
!$omp end target data
end subroutine