openacc fortran pointers

I am trying to understand how fortran pointers work in OpenACC.

I know OpenACC keeps separte device and host pointers, but I do not understand how explicitly declared fortran pointers are handled. The following code fails if I comment out the declar present(p) in the subroutine, but it works if I don’t. In the code I am working on, always pointers to structures (“type(data), pointer:: p”) is passed to subroutines, and it would be a lot more convenient if i can just declare present that in the beginning.

I also tried to declare create(p) and then update device(p) during the assignment =>, that also doesn’t work.

program test
    integer, target :: x
!$acc declare create(x)
    integer, pointer :: p
!$acc declare create(p)  

    x = 2 
    p => x
!$acc kernels
    p => x
!$acc end kernels
    call pr(p)

contains

    subroutine pr(p)
        integer,  pointer :: p
!$acc declare present(p)

!$acc kernels
        p = 10
!$acc end kernels

        print*, p
    end subroutine

end program test

Hi dshawul,

This is a tough issue that I don’t have a great solution for you. The problem here is that when accessing a pointer, Fortran semantics state that the code accesses the target. Hence, if you put “p” in an OpenACC data region, the target is actually created on the device, not the pointer. However, the pointer needs to exists in order to access it on the device. In C/C++, we could create the device pointer and then “attach” it to the device target, but there isn’t a good way to do this in Fortran.

I not sure this will work for what you need in the real program, but as a work around you pass the pointer target instead of the pointer itself, i.e. change “integer, pointer :: p” in “pr” to “integer :: p”.

% cat ptr.f90
program test
     integer, target :: x
     integer, pointer :: p
!$acc declare create(x)
     x = 2
     p => x
     call pr(p)

contains
     subroutine pr(p)
         integer :: p
!$acc serial present(p)
         p = 10
!$acc end serial
!$acc update self(p)
         print*, p
     end subroutine
end program test
% pgf90 ptr.f90 -Minfo=accel -V18.1 -ta=tesla:cc70; a.out
test:
      4, Generating create(x)
pr:
     12, Generating present(p)
         Accelerator serial kernel generated
         Generating Tesla code
     15, Generating update self(p)
           10

Hope this helps,
Mat

Hello Matt,

Thank you as always for your reply.

Unfortunately I want to keep the declaration as integer,pointer::p

So what I am doing is to NOT do a declare present on p at the beginning of the subroutine. I will not bother about keeping the pointer to the device association.

Daniel

Five years since the initial post, now it looks like there are a little bit improvement, but still some confusing part. In the following code, it works correctly. but if I comment out acc update host(p), the two printed p will be same with the value of 2. This means p will not be automatically copied to host after the end of the kernel. However, if I comment out both acc declare present(p) and acc update host(p), I got the correct answer, which means the p will be automatically copied back to host.

   program test
       integer, target :: x
   !$acc declare create(x)
       integer, pointer :: p
   !$acc declare create(p)  

       x = 2
       p => x
       call pr(p)

   contains

   subroutine pr(p)
           integer,  pointer :: p

   ! If I use declare present (p), I have to use acc update host(p),
   ! otheriwse the two prints will be identical, this is hard to
   ! understand

   !$acc declare present(p)

           print*, p
   !$acc kernels
           p = 10
   !$acc end kernels

   !$acc update host(p)

           print*, p
   end subroutine

   end program

~

Yes, things have improved in the last five year with regards to using Fortran pointers on the device. Here, I’d suggest you use the “attach” clause so “p” will point to the same device memory as “x”.

% cat test.f90
   program test
       integer, target :: x
   !$acc declare create(x)
       integer, pointer :: p
       x = 2
       print *, "X Before=",x
       p => x
   !$acc enter data attach(p)
       call pr(p)
       print *, "X After=",x
   contains

   subroutine pr(p)
           integer,  pointer :: p
           print*, "P Before=",p
   !$acc kernels present(p)
           p = 10
   !$acc end kernels
   !$acc update host(p)
           print*, "P After=",p
   end subroutine

   end program
% nvfortran -acc test.f90 ; a.out
 X Before=            2
 P Before=            2
 P After=           10
 X After=           10

Hope this helps,
Mat

Thank you Mat.
I just tried the following code also works fine:
program test
integer, target :: x
!$acc declare create(x)
integer, pointer :: p
x = 2
print *, “X Before=”,x
p => x
call pr(p)
print *, “X After=”,x
contains

      subroutine pr(p)
              integer,  pointer :: p
              print*, "P Before=",p
      !$acc kernels
              p = 10
      !$acc end kernels
      !!$acc update host(p)
              print*, "P After=",p
      end subroutine

  end program

nvfortran -acc -o attach *attach.f90
(rapids) root@nwzvenmy9r:/notebooks/ParallelProgrammingWithOpenACC/Chapter13/example_openacc11/TP# ./attach
X Before= 2
P Before= 2
P After= 10
X After= 10

However, if I uncomment the acc update host(p), the result is not correct:
./attach
X Before= 2
P Before= 2
P After= 0
X After= 0

This comparison indicates that:

  1. As long as the target variable x is declare created, it will be also created in the device, and the pointer to it will also be automatically created in the device and point to the x in the device. This is very convenient if I am correct about this function.
  2. The update host(p) line seems to update host p with the device p, and since the device address of x pointed by device p does not exist in the host, so the result is zero (or undetermined) ? This is a little bit confusing, thanks.

The reason why this code works is because “p” is getting implicitly copied at the kernel directive, but this memory is a separate copy not associated with “x”. This is why adding the “update” directive causes incorrect answers.

While the attach is recommended but optional here, you do need the “present(p)” clause to make this association.

% cat test2.f90
   program test
       integer, target :: x
   !$acc declare create(x)
       integer, pointer :: p
       x = 2
       print *, "X Before=",x
       p => x
       call pr(p)
       print *, "X After=",x
   contains

   subroutine pr(p)
           integer,  pointer :: p
           print*, "P Before=",p
   !$acc kernels present(p)
           p = 10
   !$acc end kernels
   !$acc update self(p)
           print*, "P After=",p
   end subroutine

   end program
% nvfortran -acc test2.f90 -Minfo=accel; a.out
test:
      3, Generating create(x) [if not already present]
pr:
     15, Generating present(p)
         Accelerator serial kernel generated
         Generating NVIDIA GPU code
     18, Generating update self(p)
 X Before=            2
 P Before=            2
 P After=           10
 X After=           10

Thank you again Mat.

Can I explain it in this way? :

  1. without present (p) in my code, p as a pointer is implicitly copied to the device, say d_p, and the kernel allocates another variable in the device different from the device x (d_x) which was created after x was declared on the host side, say d_x’, and associates d_p to d_x’ (not d_x).

  2. In the kernel, p=10 leads to d_x’ = 10.

  3. when exiting the kernel, the value of d_x’, which is 10 now, is automatically assigned to the host x (and p since p points to x). However, if I use update host(p), the system will just give the default value of integer (which is zero) to the host x (and p) since d_p is not associated with d_x but d_x’.

Thanks.