OpenACC Example and Exercise
A great deal of information about programming in OpenACC can be found in the OpenACC Programming and Best Practices Guide.
Based on this we can return to our familiar Jacobi iteration example to implement it on a GPU.
C
Contents of oacc_laplace.c
/*
* Copyright 2012 NVIDIA Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <math.h>
#include <string.h>
#include <openacc.h>
#include <stdio.h>
#include "timer.h"
#define NN 4096
#define NM 4096
double A[NN][NM];
double Anew[NN][NM];
int main(int argc, char** argv)
{
const int n = NN;
const int m = NM;
const int iter_max = 1000;
const double tol = 1.0e-6;
double error = 1.0;
memset(A, 0, n * m * sizeof(double));
memset(Anew, 0, n * m * sizeof(double));
for (int j = 0; j < n; j++)
{
A[j][0] = 1.0;
Anew[j][0] = 1.0;
}
printf("Jacobi relaxation Calculation: %d x %d mesh\n", n, m);
StartTimer();
int iter = 0;
#pragma acc data copy(A[:n][:m]) create(Anew[:n][:m])
while ( error > tol && iter < iter_max )
{
error = 0.0;
#pragma acc parallel loop reduction(max:error)
for( int j = 1; j < n-1; j++)
{
for( int i = 1; i < m-1; i++ )
{
Anew[j][i] = 0.25 * ( A[j][i+1] + A[j][i-1]
+ A[j-1][i] + A[j+1][i]);
error = fmax( error, fabs(Anew[j][i] - A[j][i]));
}
}
#pragma acc parallel loop
for( int j = 1; j < n-1; j++)
{
for( int i = 1; i < m-1; i++ )
{
A[j][i] = Anew[j][i];
}
}
if(iter % 100 == 0) printf("%5d, %0.6f\n", iter, error);
iter++;
}
double runtime = GetTimer();
printf(" total: %f s\n", runtime / 1000);
return 0;
}
Download oacc_laplace.c file
Contents of timer.h
/*
* Copyright 2012 NVIDIA Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TIMER_H
#define TIMER_H
#include <stdlib.h>
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <sys/time.h>
#endif
#ifdef WIN32
double PCFreq = 0.0;
__int64 timerStart = 0;
#else
struct timeval timerStart;
#endif
void StartTimer()
{
#ifdef WIN32
LARGE_INTEGER li;
if(!QueryPerformanceFrequency(&li))
printf("QueryPerformanceFrequency failed!\n");
PCFreq = (double)li.QuadPart/1000.0;
QueryPerformanceCounter(&li);
timerStart = li.QuadPart;
#else
gettimeofday(&timerStart, NULL);
#endif
}
// time elapsed in ms
double GetTimer()
{
#ifdef WIN32
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return (double)(li.QuadPart-timerStart)/PCFreq;
#else
struct timeval timerStop, timerElapsed;
gettimeofday(&timerStop, NULL);
timersub(&timerStop, &timerStart, &timerElapsed);
return timerElapsed.tv_sec*1000.0+timerElapsed.tv_usec/1000.0;
#endif
}
#endif // TIMER_H
Download timer.h file
Fortran
Contents of oacc_laplace.f90
!
! Copyright 2012 NVIDIA Corporation
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!
program laplace
implicit none
integer, parameter :: fp_kind=kind(1.0d0)
integer, parameter :: n=4096, m=4096, iter_max=1000
integer :: i, j, iter
real(fp_kind), dimension (:,:), allocatable :: A, Anew
real(fp_kind) :: tol=1.0e-6_fp_kind, error=1.0_fp_kind
real(fp_kind) :: start_time, stop_time
allocate ( A(0:n-1,0:m-1), Anew(0:n-1,0:m-1) )
A = 0.0_fp_kind
Anew = 0.0_fp_kind
! Set B.C.
A(0,:) = 1.0_fp_kind
Anew(0,:) = 1.0_fp_kind
write(*,'(a,i5,a,i5,a)') 'Jacobi relaxation Calculation:', n, ' x', m, ' mesh'
call cpu_time(start_time)
iter=0
!$acc data copy(A) create(Anew)
do while ( error .gt. tol .and. iter .lt. iter_max )
error=0.0_fp_kind
!$acc parallel loop reduction(max:error)
do j=1,m-2
do i=1,n-2
Anew(i,j) = 0.25_fp_kind * ( A(i+1,j ) + A(i-1,j ) + &
A(i ,j-1) + A(i ,j+1) )
error = max( error, abs(Anew(i,j)-A(i,j)) )
end do
end do
!$acc parallel loop
do j=1,m-2
do i=1,n-2
A(i,j) = Anew(i,j)
end do
end do
if(mod(iter,100).eq.0 ) write(*,'(i5,f10.6)'), iter, error
iter = iter + 1
end do
!$acc end data
call cpu_time(stop_time)
write(*,'(a,f10.3,a)') ' completed in ', stop_time-start_time, ' seconds'
deallocate (A,Anew)
end program laplace
Download oacc_laplace.f90 file
Exercise
Run the NVIDIA-written example for your language of choice. If you are using the NVIDIA HPC suite, compile with acc=gpu and run it, then recompile with acc=multicore and compare the runtimes.