oneAPI GPU 优化指南 - 子句: is_device_ptr, use_device_ptr, has_device_addr, use_device_addr

本章节翻译by chenchensmail@163.com  原文:Clauses: is_device_ptr, use_device_ptr, has_device_addr,... (intel.com)

OpenMP 子句 is_device_ptruse_device_ptrhas_device_addr 和 use_device_addr 可以用来传达在 target、 target data 或 dispatch 结构中引用的变量的信息。这些子句的描述如下。

is_device_ptr

is_device_ptr 子句出现在 target 或 dispatch 指令上。它表示列表项是设备指针。因此,在结构体内部对每个列表项进行私有化,并将新的列表项初始化为原始列表项所引用的设备地址。

  • 在 C 语言中,每个列表项应为指针类型或数组类型。

  • 在 C++ 中,每个列表项应为指针类型、数组类型、指针的引用类型或数组的引用类型。

  • 在 Fortran 中,每个列表项应为 C_PTR 类型。

以下 C/C++ 示例说明了如何使用 is_device_ptr 子句。 omp_target_alloc_device 例程在设备上分配内存, 并返回该内存的设备指针,该指针保存在主机变量 arr_device 中。在 target 指令上,我们使用 is_device_ptr(arr_device) 子句 来表示 arr_device 指向设备内存。因此,在 target 结构体内部, arr_device 被私有化并初始化为 arr_device 所引用的设备地址。

 1#include <stdio.h>
 2#include <stdlib.h>
 3#include <stdint.h>
 4#include <math.h>
 5#include <omp.h>
 6
 7#define N 100
 8
 9int main(void)
10{
11  int *arr_host = NULL;
12  int *arr_device = NULL;
13
14  arr_host = (int *) malloc(N * sizeof(int));
15  arr_device = (int *) omp_target_alloc_device(N * sizeof(int),
16					       omp_get_default_device());
17
18  #pragma omp target is_device_ptr(arr_device) map(from: arr_host[0:N])
19  {
20    for (int i = 0; i < N; ++i) {
21      arr_device[i] = i;
22      arr_host[i] = arr_device[i];
23    }
24  }
25
26  printf ("%d, %d, %d \n", arr_host[0], arr_host[N/2], arr_host[N-1]);
27}

use_device_ptr

use_device_ptr 子句出现在一个 target data 指令上。它表示每个列表项都是一个指向在设备上有对应存储或可访问的对象的指针。

如果一个列表项是一个指向映射到设备的对象的指针,那么在结构中对列表项的引用将被转换为对一个设备指针的引用, 该设备指针是局部于结构体并且引用相应对象的设备地址。

如果列表项不指向映射对象,它必须包含一个有效的设备地址,列表项引用将被转换为对一个局部设备指针的引用,该指针引用这个设备地址。

每个列表项必须是一个指针,其值是在目标数据环境中具有对应存储或在目标设备上可访问的对象的地址。

在 C 语言中,每个列表项应为指针类型或数组类型。

在 C++ 中,每个列表项应为指针类型、数组类型、指针的引用类型或数组的引用类型。

在 Fortran 中,每个列表项应为 C_PTR 类型。

以下 C/C++ 示例说明了如何使用 use_device_ptr 子句。 omp_target_alloc_device 例程被调用三次以在设备上分配内存。 分配的内存的地址保存在主机上的指针变量 AB 和 C 中。我们使用 use_device_ptr(A, B,C) 子句在 target data 指令上来表示 AB 和 C 包含有效的设备地址。

 1#include <stdio.h>
 2#include <stdlib.h>
 3#include <stdint.h>
 4#include <math.h>
 5#include <omp.h>
 6
 7#define length 65536
 8
 9int main(void)
10{
11  int device_id = omp_get_default_device();
12  size_t bytes = length*sizeof(double);
13  double * __restrict A;
14  double * __restrict B;
15  double * __restrict C;
16  double scalar = 3.0;
17  double ar;
18  double br;
19  double cr;
20  double asum;
21
22  // Allocate arrays in device memory
23
24  A = (double *) omp_target_alloc_device(bytes, device_id);
25  if (A == NULL){
26     printf(" ERROR: Cannot allocate space for A using omp_target_alloc_device().\n");
27     exit(1);
28  }
29
30  B = (double *) omp_target_alloc_device(bytes, device_id);
31  if (B == NULL){
32     printf(" ERROR: Cannot allocate space for B using omp_target_alloc_device().\n");
33     exit(1);
34  }
35
36  C = (double *) omp_target_alloc_device(bytes, device_id);
37  if (C == NULL){
38     printf(" ERROR: Cannot allocate space for C using omp_target_alloc_device().\n");
39     exit(1);
40  }
41
42  #pragma omp target data use_device_ptr(A,B,C)
43  {
44      // Initialize the arrays
45
46      #pragma omp target teams distribute parallel for
47      for (size_t i=0; i<length; i++) {
48          A[i] = 2.0;
49          B[i] = 2.0;
50          C[i] = 0.0;
51      }
52
53      // Perform the computation
54
55      #pragma omp target teams distribute parallel for
56      for (size_t i=0; i<length; i++) {
57          C[i] += A[i] + scalar * B[i];
58      }
59
60      // Validate and output results
61
62      ar = 2.0;
63      br = 2.0;
64      cr = 0.0;
65      for (int i=0; i<length; i++) {
66          cr += ar + scalar * br;
67      }
68
69      asum = 0.0;
70      #pragma omp target teams distribute parallel for reduction(+:asum)
71      for (size_t i=0; i<length; i++) {
72          asum += fabs(C[i]);
73      }
74
75  } // end target data
76
77  omp_target_free(A, device_id);
78  omp_target_free(B, device_id);
79  omp_target_free(C, device_id);
80
81  double epsilon=1.e-8;
82  if (fabs(cr - asum)/asum > epsilon) {
83      printf("Failed Validation on output array\n"
84             "       Expected checksum: %lf\n"
85             "       Observed checksum: %lf\n"
86             "ERROR: solution did not validate\n", cr, asum);
87      return 1;
88  } else {
89      printf("Solution validates. Checksum = %lf\n", asum);
90  }
91
92  return 0;
93}

has_device_addr

has_device_addr 子句出现在一个 target 指令上。它表示列表项已经具有有效的设备地址,因此可以直接从设备访问。

每个列表项必须对于设备数据环境具有有效的设备地址。它可以是任何类型,包括数组部分。

特别地,在 Fortran 中, has_device_addr 子句非常有用,因为它可以用于任何类型的列表项(不仅仅是 C_PTR )来表示列表项具有设备地址。

以下的 Fortran 示例说明了如何使用 has_device_addr 子句。在示例中,三个数组 AB 和 C 在设备上分配。 当在 target 区域引用数组时,我们使用 has_device_addr(A, B, C) 子句来表示 AB 和 C 已经具有设备地址。

 1program main
 2  use iso_fortran_env
 3  use omp_lib
 4  implicit none
 5
 6  integer, parameter :: iterations=1000
 7  integer, parameter :: length=64*1024*1024
 8  real(kind=REAL64), parameter ::  epsilon=1.D-8
 9  real(kind=REAL64), allocatable ::  A(:)
10  real(kind=REAL64), allocatable ::  B(:)
11  real(kind=REAL64), allocatable ::  C(:)
12  real(kind=REAL64) :: scalar=3.0
13  real(kind=REAL64) :: ar, br, cr, asum
14  real(kind=REAL64) :: nstream_time, avgtime
15  integer :: i, iter
16
17  !
18  ! Allocate arrays in device memory
19
20  !$omp allocate allocator(omp_target_device_mem_alloc)
21  allocate(A(length))
22
23  !$omp allocate allocator(omp_target_device_mem_alloc)
24  allocate(B(length))
25
26  !$omp allocate allocator(omp_target_device_mem_alloc)
27  allocate(C(length))
28
29  !
30  ! Initialize the arrays
31
32  !$omp target teams distribute parallel do has_device_addr(A, B, C)
33  do i = 1, length
34      A(i) = 2.0
35      B(i) = 2.0
36      C(i) = 0.0
37  end do
38
39  !
40  ! Perform the computation
41
42  nstream_time = omp_get_wtime()
43  do iter = 1, iterations
44     !$omp target teams distribute parallel do has_device_addr(A, B, C)
45     do i = 1, length
46        C(i) = C(i) + A(i) + scalar * B(i)
47     end do
48  end do
49  nstream_time = omp_get_wtime() - nstream_time
50
51  !
52  ! Validate and output results
53
54  ar = 2.0
55  br = 2.0
56  cr = 0.0
57  do iter = 1, iterations
58     do i = 1, length
59        cr = cr + ar + scalar * br
60     end do
61  end do
62
63  asum = 0.0
64  !$omp target teams distribute parallel do reduction(+:asum) has_device_addr(C)
65  do i = 1, length
66     asum = asum + abs(C(i))
67  end do
68
69  if (abs(cr - asum)/asum > epsilon) then
70     print *, "Failed Validation on output array:", "Expected =", cr, "Observed =", asum
71  else
72     avgtime = nstream_time/iterations
73     print *, "Solution validates:", "Checksum =", asum, "Avg time (s) =",  avgtime
74  endif
75
76  deallocate(A)
77  deallocate(B)
78  deallocate(C)
79
80end program main

use_device_addr

use_device_addr 子句出现在一个 target data 指令上。它表示每个列表项已经在设备上有对应的存储或者可以在设备上访问。

如果一个列表项被映射,那么在结构中对列表项的引用将被转换为对相应列表项的引用。如果一个列表项没有被映射,那么假定它可以在设备上访问。

一个列表项可以是一个数组部分。

就像 has_device_addruse_device_addr 子句在 Fortran 中特别有用,因为它可以用于任何类型的列表项(不仅仅是 C_PTR )来表示列表项具有设备地址。

以下的 Fortran 示例说明了如何使用 use_device_addr 子句。在示例中, array_d 使用 alloc 映射类型映射到设备,因此为 array_d 在设备上分配存储空间, 并且不会发生主机和设备之间的数据传输。我们在 target data 指令上使用 use_device_addr(array_d) 子句来表示 array_d 在设备上有对应的存储。

 1program target_use_device_addr
 2
 3  use omp_lib
 4  use iso_fortran_env, only : real64
 5  implicit none
 6
 7  integer, parameter :: N1 = 1024
 8  real(kind=real64), parameter :: aval = real(42, real64)
 9  real(kind=real64), allocatable :: array_d(:), array_h(:)
10  integer :: i,err
11
12  ! Allocate host data
13  allocate(array_h(N1))
14
15  !$omp target data map (from:array_h(1:N1)) map(alloc:array_d(1:N1))
16  !$omp target data use_device_addr(array_d)
17  !$omp target
18      do i=1, N1
19        array_d(i) = aval
20        array_h(i) = array_d(i)
21     end do
22  !$omp end target
23  !$omp end target data
24  !$omp end target data
25
26  ! Check result
27  write (*,*) array_h(1), array_h(N1)
28  if (any(array_h /= aval)) then
29    err = 1
30  else
31    err = 0
32  end if
33
34  deallocate(array_h)
35  if (err == 1) then
36    stop 1
37  else
38    stop 0
39  end if
40
41end program target_use_device_addr

下表总结了本节描述的子句的属性。

Clause

On which directive

Type of list item

Description

is_device_ptr

target, dispatch

C/C++: Pointer, array, or reference

Fortran: C_PTR

Indicates that list item is a device pointer (has valid device address).

use_device_ptr

target data

C/C++: Pointer, array, or reference

Fortran: C_PTR

Indicates that list item is a pointer to an object that has corresponding storage on device or is accessible on device.

has_device_addr

target

Any type (may be array section)

Indicates that list item has a valid device address.

use_device_addr

target data

Any type (may be array section)

Indicates that list item has corresponding storage on device or is accessible on the device.

上一章                                    主目录​​    上级目录                                                               下一章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值