hipBLAS examples#
hipBLAS is a thin
layer over rocBLAS whose API follows cuBLAS. hipFORT exposes it through the
hipfort_hipblas module, which mirrors the hipBLAS C API one to one and
re-exports the enumerations (HIPBLAS_OP_N, HIPBLAS_FILL_MODE_LOWER and
so on) from hipfort_hipblas_enums.
Every program on this page is a complete, self-contained example that is built
and run as part of the hipFORT test suite. The Fortran 2008 sources live in
test/f2008/hipblas and the equivalent Fortran 2003 sources, which use
type(c_ptr) device pointers and explicit byte counts instead of Fortran
array pointers, live in test/f2003/hipblas. The packed triangular solve,
stpsv, is Fortran 2008 only.
If you want direct access to rocBLAS rather than a cuBLAS-style interface, see
the rocBLAS examples, where the equivalent programs
are written against the hipfort_rocblas module.
The examples are grouped the way the BLAS routines themselves are: Level 1
operates on vectors, Level 2 on a matrix and a vector, and Level 3 on two
matrices. Most routines are provided in four precisions, identified by the
usual BLAS prefix: s (single-precision real), d (double-precision
real), c (single-precision complex) and z (double-precision complex).
Each section below shows one precision and names the sibling test files that
cover the others.
hipBLAS call pattern#
A hipBLAS program always follows the same sequence:
Create a handle with
hipblasCreate.Allocate device memory and copy the input data over, either with
hipMalloc/hipMemcpyor, in the Fortran 2008 interfaces, with thehipMalloc(source=...)shortcut that allocates and copies in one call.Call the hipBLAS routine.
Call
hipDeviceSynchronizebefore reading a result, whether it was written to a host scalar or to device memory.Copy device results back to the host.
Free the device memory and release the handle with
hipblasDestroy.
Keep the following conventions in mind:
hipBLAS starts in host pointer mode, so
alpha,betaand scalar results such as a dot product are read from host memory without any setup call. Only a program that wants those values to live on the device needshipblasSetPointerMode. This is the main day-to-day difference from rocBLAS, whose examples set the pointer mode explicitly.hipBLAS matrices are stored column-major, which matches Fortran’s native array layout directly, so no transpose trick is needed to call hipBLAS from Fortran.
The leading dimension of a device matrix is usually just its first dimension,
size(dA,1).Enumerations such as
HIPBLAS_OP_N,HIPBLAS_FILL_MODE_LOWER,HIPBLAS_DIAG_NON_UNITandHIPBLAS_SIDE_LEFTcome from thehipfort_hipblas_enumsmodule, whichhipfort_hipblasre-exports, souse hipfort_hipblason its own is enough.Every hipBLAS call returns a status code. The examples wrap them in
hipblasCheckfrom thehipfort_checkmodule, which aborts on failure. (sgemv.f08andsger.f08route their hipBLAS calls throughhipCheckinstead; both abort on a non-zero status.)
Building an example#
The examples only need the hipblas and hip hipFORT components:
find_package(hipfort REQUIRED COMPONENTS hip hipblas)
add_executable(my_blas saxpy.f08)
target_link_libraries(my_blas PRIVATE hipfort::hipblas hipfort::hip)
See Using hipFORT in your application for the full set of build options.
Level 1: vector operations#
Scaled vector update (axpy)#
hipblas?axpy computes y := alpha * x + y. This example runs it in
single precision and checks the result against the expected value.
program hip_saxpy
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer :: n = 6
type(c_ptr) :: handle = c_null_ptr
integer :: j
real, allocatable, dimension(:) :: x, y, y_exact
real, parameter :: alpha = 2.0
real, pointer, dimension(:) :: dx, dy
real :: error
real, parameter :: error_max = 10*epsilon(error)
allocate(x(n))
allocate(y(n))
allocate(y_exact(n))
do j = 1,n
x(j) = j
y(j) = j
end do
do j = 1,n
y_exact(j) = alpha*x(j) + y(j)
end do
write(*,"(a)",advance="no") "-- Running test 'SAXPY' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
call hipCheck(hipMalloc(dx,shape(x)))
call hipCheck(hipMalloc(dy,shape(y)))
call hipCheck(hipMemcpy(dx, x, hipMemcpyHostToDevice))
call hipCheck(hipMemcpy(dy, y, hipMemcpyHostToDevice))
call hipblasCheck(hipblasSaxpy(handle,n,alpha,dx,1,dy,1))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(y, dy, hipMemcpyDeviceToHost))
do j = 1,n
error = abs((y_exact(j) - y(j))/y_exact(j))
if( error > error_max )then
write(*,*) "FAILED! Error bigger than max! Error = ", error
call exit(1)
end if
end do
call hipCheck(hipFree(dx))
call hipCheck(hipFree(dy))
call hipblasCheck(hipblasDestroy(handle))
deallocate(x,y)
write(*,*) "PASSED!"
end program hip_saxpy
test/f2008/hipblas/daxpy.f08, caxpy.f08 and zaxpy.f08 run the same
computation in double-precision real, single-precision complex and
double-precision complex, respectively.
Vector scaling (scal)#
hipblas?scal computes x := alpha * x. The operation is in place, so
the device vector is both the input and the output.
program hip_dscal
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer, parameter :: N = 10240;
double precision, parameter :: alpha = 10.d0
double precision,pointer,dimension(:) :: dx
double precision,allocatable,target,dimension(:) :: hx
double precision,allocatable,dimension(:) ::hx_scaled
double precision :: error
double precision, parameter :: error_max = 10*epsilon(error_max)
type(c_ptr) :: hip_blas_handle = c_null_ptr
integer :: i
write(*,"(a)",advance="no") "-- Running test 'dscal' (Fortran 2008 interfaces) - "
allocate(hx(N))
allocate(hx_scaled(N))
hx(:) = 10.d0
hx_scaled = alpha*hx
call hipblasCheck(hipblasCreate(hip_blas_handle))
call hipCheck(hipMalloc(dx,shape(hx)))
! Transfer data from host to device memory
call hipCheck(hipMemcpy(dx, hx, hipMemcpyHostToDevice))
call hipblasCheck(hipblasDscal(hip_blas_handle, N, alpha, dx, 1))
call hipCheck(hipDeviceSynchronize())
! Transfer data back to host memory
call hipCheck(hipMemcpy(hx, dx, hipMemcpyDeviceToHost))
call hipCheck(hipFree(dx))
! Verification
do i = 1,N
error = abs(hx(i) - hx_scaled(i) )
if( error .gt. error_max ) then
write(*,*) "FAILED! Error bigger than max! Error = ", error, " hx(i) = ", hx(i)
call exit
endif
end do
call hipblasCheck(hipblasDestroy(hip_blas_handle))
deallocate(hx_scaled,hx)
write(*,*) "PASSED!"
end program hip_dscal
This is the only scal example in the test suite; there is no
single-precision or complex counterpart. The rocBLAS examples cover the remaining precisions, including the
mixed-precision forms that scale a complex vector by a real scalar.
Vector copy and swap#
hipblas?copy copies x into y on the device, leaving x
untouched.
program hip_scopy
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer :: n = 6
type(c_ptr) :: handle = c_null_ptr
integer :: j
real, allocatable, target, dimension(:) :: x, y
integer, parameter :: bytes_per_element = 4 !float precision
real, pointer, dimension(:) :: dx,dy
real :: error
real, parameter :: error_max = 10*epsilon(error)
allocate(x(n))
allocate(y(n))
do j = 1,n
x(j) = j
! write(*,*) "value of x(j)" , x(j)
end do
write(*,"(a)",advance="no") "-- Running test 'Scopy' (Fortran 2008 interfaces) - "
do j = 1,n
y(j) = x(j)
! write(*,*) "value of y(j)" , y(j)
end do
call hipblasCheck(hipblasCreate(handle))
call hipCheck(hipMalloc(dx,source=x))
call hipCheck(hipMalloc(dy,source=y))
call hipblasCheck(hipblasScopy(handle,n,dx,1,dy,1))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(y, dy, hipMemcpyDeviceToHost))
do j = 1,n
error = abs(y(j) - x(j))
if( error > error_max )then
write(*,*) "FAILED! Error bigger than max! Error = ", error
call exit(1)
end if
end do
call hipCheck(hipFree(dx))
call hipCheck(hipFree(dy))
call hipblasCheck(hipblasDestroy(handle))
deallocate(x,y)
write(*,*) "PASSED!"
end program hip_scopy
hipblas?swap exchanges the contents of the two vectors instead, so both
are modified. The program below checks that x and y have traded
values.
program hip_sswap
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer :: n = 6
integer :: i
real(kind=4), allocatable, target, dimension(:) :: x, y, x_exact, y_exact
real(kind=4), pointer, dimension(:) :: dx, dy
type(c_ptr) :: handle = c_null_ptr
real :: error
real, parameter :: error_max = 10*epsilon(error)
allocate(x(n))
allocate(y(n))
allocate(x_exact(n))
allocate(y_exact(n))
do i = 1,n
x(i) = i
x_exact(i) = x(i)
y(i) = 2 * i
y_exact(i) = y(i)
end do
write(*,"(a)",advance="no") "-- Running test 'SSWAP' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
call hipCheck(hipMalloc(dx,shape(x)))
call hipCheck(hipMalloc(dy,shape(y)))
call hipCheck(hipMemcpy(dx, x, hipMemcpyHostToDevice))
call hipCheck(hipMemcpy(dy, y, hipMemcpyHostToDevice))
call hipblasCheck(hipblasSswap(handle,n,dx,1,dy,1))
call hipCheck(hipMemcpy(x, dx, hipMemcpyDeviceToHost))
call hipCheck(hipMemcpy(y, dy, hipMemcpyDeviceToHost))
do i = 1,n
error = MAX(abs((y_exact(i) - x(i))/y_exact(i)), abs((x_exact(i) - y(i))/x_exact(i)))
if( error > error_max )then
write(*,*) "FAILED! Error bigger than max! Error = ", error
call exit(1)
end if
end do
call hipblasCheck(hipblasDestroy(handle))
call hipCheck(hipFree(dx))
call hipCheck(hipFree(dy))
deallocate(x,y)
write(*,*) "PASSED!"
end program hip_sswap
Both are single-precision only in the test suite, and neither has a rocBLAS counterpart there.
Dot products#
hipblas?dot computes the dot product of two real vectors and returns the
scalar result through a pointer. Because hipBLAS is in host pointer mode by
default, that pointer is an ordinary host variable.
program hip_sdot
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer, parameter :: n = 10240
real(kind=4), allocatable, dimension(:) :: hx, hy
real(kind=4), pointer, dimension(:) :: dx, dy
real(kind=4), target :: res
real(kind=4) :: res_exact, error
real(kind=4), parameter :: error_max = 10*epsilon(error)
type(c_ptr) :: handle = c_null_ptr
write(*,"(a)",advance="no") "-- Running test 'SDOT' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
allocate(hx(n), hy(n))
hx = 1.0
hy = 2.0
res_exact = 2.0 * n
call hipCheck(hipMalloc(dx, shape(hx)))
call hipCheck(hipMalloc(dy, shape(hy)))
call hipCheck(hipMemcpy(dx, hx, hipMemcpyHostToDevice))
call hipCheck(hipMemcpy(dy, hy, hipMemcpyHostToDevice))
res = 0.0
call hipblasCheck(hipblasSdot(handle, n, dx, 1, dy, 1, c_loc(res)))
call hipCheck(hipDeviceSynchronize())
error = abs((res_exact - res) / res_exact)
if (error > error_max) then
write(*,*) "FAILED! error = ", error, " result = ", res
call exit(1)
end if
call hipCheck(hipFree(dx))
call hipCheck(hipFree(dy))
call hipblasCheck(hipblasDestroy(handle))
deallocate(hx, hy)
write(*,*) "PASSED!"
end program hip_sdot
test/f2008/hipblas/ddot.f08 is the double-precision equivalent.
Complex vectors have two dot product variants: hipblas?dotc conjugates
the first vector before multiplying, and hipblas?dotu does not. Compare
test/f2008/hipblas/cdotc.f08 and cdotu.f08 (also available in double
precision as zdotc.f08 and zdotu.f08) to see the different expected
results for the same input data.
Euclidean norm (nrm2)#
hipblas?nrm2 computes the Euclidean norm of a vector and returns it
through a pointer, which in host pointer mode is an ordinary host variable.
program hip_snrm2
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
use hipfort_hipblas_enums
implicit none
! nrm2(x) with x = 1 over n elements, so the result is sqrt(n).
integer, parameter :: n = 1024
real(c_float), allocatable, dimension(:) :: hx
real(c_float), target :: res
real(c_float) :: res_exact, error
real(c_float), parameter :: error_max = 10*epsilon(error)
real(c_float), pointer, dimension(:) :: dx
type(c_ptr) :: handle = c_null_ptr
write(*,"(a)",advance="no") "-- Running test 'snrm2' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
allocate(hx(n))
hx = 1.0
res_exact = sqrt(real(n, kind=kind(res_exact)))
call hipCheck(hipMalloc(dx, shape(hx)))
call hipCheck(hipMemcpy(dx, hx, hipMemcpyHostToDevice))
res = 0.0
call hipblasCheck(hipblasSnrm2(handle, n, dx, 1, c_loc(res)))
call hipCheck(hipDeviceSynchronize())
error = abs((res_exact - res) / res_exact)
if (error > error_max) then
write(*,*) "FAILED! error = ", error, " result = ", res
call exit(1)
end if
call hipCheck(hipFree(dx))
call hipblasCheck(hipblasDestroy(handle))
write(*,*) "PASSED!"
end program hip_snrm2
test/f2008/hipblas/dnrm2.f08 is the double-precision equivalent. The
complex forms are named for both types involved, because the norm of a complex
vector is real: hipblasScnrm2 takes a single-precision complex vector and
returns a single-precision real result, and hipblasDznrm2 is its
double-precision counterpart. See scnrm2.f08 and dznrm2.f08.
Sum of absolute values (asum)#
hipblas?asum sums the absolute values of a vector’s elements. For complex
vectors it sums abs(real(x)) + abs(aimag(x)) per element rather than the
complex modulus.
program hip_sasum
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
use hipfort_hipblas_enums
implicit none
! asum(x) with x = 1 over n elements, so the result is n.
integer, parameter :: n = 1024
real(c_float), allocatable, dimension(:) :: hx
real(c_float), target :: res
real(c_float) :: res_exact, error
real(c_float), parameter :: error_max = 10*epsilon(error)
real(c_float), pointer, dimension(:) :: dx
type(c_ptr) :: handle = c_null_ptr
write(*,"(a)",advance="no") "-- Running test 'sasum' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
allocate(hx(n))
hx = 1.0
res_exact = real(n, kind=kind(res_exact))
call hipCheck(hipMalloc(dx, shape(hx)))
call hipCheck(hipMemcpy(dx, hx, hipMemcpyHostToDevice))
res = 0.0
call hipblasCheck(hipblasSasum(handle, n, dx, 1, c_loc(res)))
call hipCheck(hipDeviceSynchronize())
error = abs((res_exact - res) / res_exact)
if (error > error_max) then
write(*,*) "FAILED! error = ", error, " result = ", res
call exit(1)
end if
call hipCheck(hipFree(dx))
call hipblasCheck(hipblasDestroy(handle))
write(*,*) "PASSED!"
end program hip_sasum
test/f2008/hipblas/dasum.f08 is the double-precision equivalent, and
scasum.f08 and dzasum.f08 are the mixed real/complex forms named on the
same convention as scnrm2.
Index of the largest element (iamax)#
hipblasI?amax returns the index of the element with the largest absolute
value. The returned index is 1-based, so it can be used to subscript a
Fortran array directly.
program hip_isamax
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
use hipfort_hipblas_enums
implicit none
! The largest |x| sits at index 5, and hipBLAS returns a 1-based index.
integer, parameter :: n = 8
real(c_float), allocatable, dimension(:) :: hx
integer(c_int), target :: res
integer(c_int), parameter :: res_exact = 5
real(c_float), pointer, dimension(:) :: dx
type(c_ptr) :: handle = c_null_ptr
write(*,"(a)",advance="no") "-- Running test 'isamax' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
allocate(hx(n))
hx = 1.0
hx(5) = 10.0
call hipCheck(hipMalloc(dx, shape(hx)))
call hipCheck(hipMemcpy(dx, hx, hipMemcpyHostToDevice))
res = 0
call hipblasCheck(hipblasIsamax(handle, n, dx, 1, c_loc(res)))
call hipCheck(hipDeviceSynchronize())
if (res /= res_exact) then
write(*,*) "FAILED! wrong index: result = ", res, " expected ", res_exact
call exit(1)
end if
call hipCheck(hipFree(dx))
call hipblasCheck(hipblasDestroy(handle))
write(*,*) "PASSED!"
end program hip_isamax
test/f2008/hipblas/idamax.f08, icamax.f08 and izamax.f08 cover the
remaining precisions. The matching iamin routines have no hipBLAS example
here; the rocBLAS examples cover them.
Level 2: matrix-vector operations#
Matrix-vector multiplication#
hipblas?gemv computes y := alpha * op(A) * x + beta * y. This example
uses constant matrix and vector entries so the expected result is a constant
vector and easy to check.
program hip_sgemv
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer :: m = 6
integer :: n = 5
integer :: i, j
real, parameter :: alpha = 1.0
real, parameter :: beta = 0.0
type(c_ptr) :: handle = c_null_ptr
real(kind=4), allocatable, target, dimension(:) :: a, x, y
real(kind=4), pointer, dimension(:) :: da, dx, dy
real :: error
real, parameter :: error_max = 10*epsilon(error)
allocate(x(n))
allocate(y(m))
allocate(a(m*n))
a(:) = 1.0
x(:) = 1.0
y(:) = 1.0
write(*,"(a)",advance="no") "-- Running test 'SGEMV' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
call hipCheck(hipMalloc(dx,source=x))
call hipCheck(hipMalloc(dy,source=y))
call hipCheck(hipMalloc(da,source=a))
call hipCheck(hipblasSgemv(handle,HIPBLAS_OP_N,m,n,alpha,da,m,dx,1,beta,dy,1))
call hipCheck(hipMemcpy(y, dy, hipMemcpyDeviceToHost))
do i = 1,m
error = abs(5.0 - y(i))
if( error > 10*epsilon(error) )then
write(*,*) "FAILED! Error bigger than max! Error = ", error, "y(i) = ", y(i)
call exit(1)
end if
end do
call hipblasCheck(hipblasDestroy(handle))
call hipCheck(hipFree(da))
call hipCheck(hipFree(dx))
call hipCheck(hipFree(dy))
deallocate(a,x,y)
write(*,*) "PASSED!"
end program hip_sgemv
test/f2008/hipblas/dgemv.f08, cgemv.f08 and zgemv.f08 cover the
remaining precisions.
Rank-1 update (ger)#
hipblas?ger computes A := alpha * x * y**T + A, adding the outer
product of two vectors to a matrix in place. This program keeps the
type(c_ptr) device pointers and explicit byte counts of the Fortran 2003
style even though it lives among the Fortran 2008 sources.
! HIPBLAS, i.e. cuBLAS and rocBLAS, assumes column-major matrix memory layouts.
! Hence, no matrix must be transposed when interfacing with Fortran.
program hip_sger
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer :: m = 6
integer :: n = 5
real, parameter :: alpha = 2.0
type(c_ptr) :: handle = c_null_ptr
integer :: i, j
real :: error
real, parameter :: error_max = 10*epsilon(error)
real(kind=4), allocatable, target, dimension(:,:) :: a
real(kind=4), allocatable, target, dimension(:) :: x, y
type(c_ptr) :: da = c_null_ptr, dx = c_null_ptr, dy = c_null_ptr
integer(c_size_t) :: Nabytes, Nxbytes, Nybytes
integer, parameter :: bytes_per_element = 4 !float precision
Nxbytes = m * bytes_per_element
Nybytes = n * bytes_per_element
Nabytes = m * n * bytes_per_element
allocate(x(m))
allocate(y(n))
allocate(a(m,n))
do i = 1,m
do j = 1,n
a(i,j) = 1.0
end do
end do
do i = 1,m
x(i) = 1.0
end do
do i = 1,n
y(i) = 1.0
end do
write(*,"(a)",advance="no") "-- Running test 'SGER' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
call hipCheck(hipMalloc(dx,Nxbytes))
call hipCheck(hipMalloc(dy,Nybytes))
call hipCheck(hipMalloc(da,Nabytes))
!call hipCheck(hipblasSetMatrix(m,n,bytes_per_element,a,m,da,m))
!call hipCheck(hipblasSetVector(m,bytes_per_element,x,1,dx,1))
!call hipCheck(hipblasSetVector(n,bytes_per_element,y,1,dy,1))
call hipCheck(hipMemcpy(da, c_loc(a), Nabytes, hipMemcpyHostToDevice))
call hipCheck(hipMemcpy(dx, c_loc(x), Nxbytes, hipMemcpyHostToDevice))
call hipCheck(hipMemcpy(dy, c_loc(y), Nybytes, hipMemcpyHostToDevice))
call hipCheck(hipblasSger(handle,m,n,alpha,dx,1,dy,1,da,m))
!call hipCheck(hipblasGetMatrix(m,n,bytes_per_element,da,m,a,m));
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(c_loc(a), da, Nabytes, hipMemcpyDeviceToHost))
!do i=1,m
! do j = 1,n
! write(*,*) a(i,j)
! end do
!end do
do i = 1,m
do j = 1,n
error = abs(3.0 - a(i,j))
if( error > 10*epsilon(error) )then
write(*,*) "FAILED! Error bigger than max! Error = ", error, "a(i,j) = ", a(i,j)
call exit(1)
end if
end do
end do
call hipblasCheck(hipblasDestroy(handle))
call hipCheck(hipFree(da))
call hipCheck(hipFree(dx))
call hipCheck(hipFree(dy))
deallocate(a)
deallocate(x)
deallocate(y)
write(*,*) "PASSED!"
end program hip_sger
test/f2008/hipblas/dger.f08 is the double-precision equivalent. The
rocBLAS suite additionally provides the conjugated and unconjugated complex
forms, gerc and geru; see the rocBLAS examples.
Triangular solve#
hipblas?trsv solves A * x = b in place for a triangular matrix A,
given the fill mode (HIPBLAS_FILL_MODE_LOWER or
HIPBLAS_FILL_MODE_UPPER), the transpose operation and whether the diagonal
is unit or not. dx holds b on entry and x on exit.
program hip_strsv
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer(kind(HIPBLAS_FILL_MODE_UPPER)), parameter :: uplo = HIPBLAS_FILL_MODE_LOWER
integer(kind(HIPBLAS_OP_N)), parameter :: transA = HIPBLAS_OP_N
integer(kind(HIPBLAS_DIAG_NON_UNIT)), parameter :: diag = HIPBLAS_DIAG_NON_UNIT
integer, parameter :: m = 1024
real(kind=4), allocatable, target, dimension(:,:) :: hA
real(kind=4), allocatable, target, dimension(:) :: hx
real(kind=4), pointer, dimension(:,:) :: dA
real(kind=4), pointer, dimension(:) :: dx
type(c_ptr) :: handle = c_null_ptr
real(kind=4), parameter :: x_exact = 1.0
real(kind=4) :: error
real(kind=4), parameter :: error_max = 10*epsilon(error)
integer :: i, j
write(*,"(a)",advance="no") "-- Running test 'STRSV' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
allocate(hA(m,m), hx(m))
hA = 0.0
do j = 1, m
do i = j, m
hA(i,j) = 1.0
end do
end do
do i = 1, m
hx(i) = real(i)
end do
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dx, source=hx))
call hipblasCheck(hipblasStrsv(handle, uplo, transA, diag, m, dA, size(dA,1), dx, 1))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(hx, dx, hipMemcpyDeviceToHost))
do i = 1, m
error = abs((x_exact - hx(i)) / x_exact)
if (error > error_max) then
write(*,*) "FAILED! Error bigger than max! Error = ", error, " hx(", i, ") = ", hx(i)
call exit(1)
end if
end do
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dx))
call hipblasCheck(hipblasDestroy(handle))
deallocate(hA, hx)
write(*,*) "PASSED!"
end program hip_strsv
test/f2008/hipblas/dtrsv.f08, ctrsv.f08 and ztrsv.f08 cover the
remaining precisions.
Packed triangular solve#
hipblas?tpsv solves the same problem as trsv, but the triangular
matrix is stored in packed form: only the referenced triangle is kept, in a
single one-dimensional array, which halves the memory footprint for large
matrices.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Copyright (c) 2020-2026 Advanced Micro Devices, Inc.
!
! Permission is hereby granted, free of charge, to any person obtaining a copy
! of this software and associated documentation files (the "Software"), to deal
! in the Software without restriction, including without limitation the rights
! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
! copies of the Software, and to permit persons to whom the Software is
! furnished to do so, subject to the following conditions:
!
! The above copyright notice and this permission notice shall be included in
! all copies or substantial portions of the Software.
!
! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
! THE SOFTWARE.
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
program hipblas_stpsv_test
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer(kind(HIPBLAS_FILL_MODE_LOWER)), parameter :: uplo = HIPBLAS_FILL_MODE_LOWER
integer(kind(HIPBLAS_OP_N)), parameter :: transA = HIPBLAS_OP_N
integer(kind(HIPBLAS_DIAG_NON_UNIT)), parameter :: diag = HIPBLAS_DIAG_NON_UNIT
integer, parameter :: n = 64
! Packed lower-triangular matrix (column-by-column). Every stored element of an
! all-ones lower-triangular matrix is 1, so the packing order is irrelevant here.
real(c_float), allocatable, dimension(:) :: hAP, hx
real(c_float), parameter :: x_exact = 1.0
real(c_float), pointer, dimension(:) :: dAP => null(), dx => null()
type(c_ptr) :: handle = c_null_ptr
integer :: i
real(c_float) :: error
real(c_float), parameter :: error_max = 10*epsilon(error)
write(*,"(a)",advance="no") "-- Running test 'STPSV' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
allocate(hAP(n*(n+1)/2), hx(n))
hAP(:) = 1.0 ! all-ones lower-triangular L, packed
do i = 1, n
hx(i) = real(i) ! b(i) = i -> exact solution of L x = b is x(i) = 1
end do
! Allocate device memory (source= implies a blocking memcpy)
call hipCheck(hipMalloc(dAP, source=hAP))
call hipCheck(hipMalloc(dx, source=hx))
! Packed triangular solve L x = b, in place. dAP is passed as a Fortran array
! (the generic array form) — this is the case that used to fail to compile
! because the packed matrix dummy was declared type(c_ptr) (SWDEV-485451).
call hipblasCheck(hipblasStpsv(handle, uplo, transA, diag, n, dAP, dx, 1))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(hx, dx, hipMemcpyDeviceToHost))
do i = 1, n
error = abs((x_exact - hx(i))/x_exact)
if( error > error_max )then
write(*,*) "FAILED! Error bigger than max! Error = ", error, " hx(", i, ") = ", hx(i)
call exit(1)
end if
end do
call hipCheck(hipFree(dAP))
call hipCheck(hipFree(dx))
call hipblasCheck(hipblasDestroy(handle))
deallocate(hAP, hx)
write(*,*) "PASSED!"
end program hipblas_stpsv_test
This is the only packed-storage example; there is no double-precision or complex counterpart in the test suite.
Level 3: matrix-matrix operations#
Matrix-matrix multiplication#
hipblas?gemm computes C := alpha * op(A) * op(B) + beta * C. As with
gemv, this example uses constant matrix entries so the exact result is a
constant matrix.
program hip_dgemm
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer(kind(HIPBLAS_OP_N)), parameter :: transa = HIPBLAS_OP_N, transb = HIPBLAS_OP_N;
double precision, parameter :: alpha = 1.1d0, beta = 0.9d0;
integer, parameter :: m = 1024, n = 1024, k = 1024;
double precision, allocatable, dimension(:,:) :: ha, hb, hc, hc_exact
double precision, pointer, dimension(:,:) :: da, db, dc
type(c_ptr) :: handle = c_null_ptr
integer :: i,j
double precision :: error
double precision, parameter :: error_max = 10*epsilon(error)
write(*,"(a)",advance="no") "-- Running test 'DGEMM' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
allocate(ha(m,k))
allocate(hb(k,n))
allocate(hc(m,n))
allocate(hc_exact(m,n))
! Use these constant matrices so the exact answer is also a
! constant matrix and therefore easy to check
ha(:,:) = 1.d0
hb(:,:) = 2.d0
hc(:,:) = 3.d0
hc_exact = alpha*k*2.d0 + beta*3.d0
! Allocate device memory
call hipCheck(hipMalloc(da,source=ha)) ! implies (blocking) memcpy
call hipCheck(hipMalloc(db,source=hb))
call hipCheck(hipMalloc(dc,source=hc))
call hipblasCheck(hipblasDgemm(handle,transa,transb,m,n,k,alpha,da,size(da,1),db,size(db,1),beta,dc,size(dc,1)))
call hipCheck(hipDeviceSynchronize())
! Transfer data back to host memory
call hipCheck(hipMemcpy(hc, dc, hipMemcpyDeviceToHost))
do j = 1,n
do i = 1,m
error = abs((hc_exact(i,j) - hc(i,j))/hc_exact(i,j))
if( error > error_max )then
write(*,*) "FAILED! Error bigger than max! Error = ", error
call exit(1)
end if
end do
end do
call hipCheck(hipFree(da))
call hipCheck(hipFree(db))
call hipCheck(hipFree(dc))
call hipblasCheck(hipblasDestroy(handle))
deallocate(ha,hb,hc,hc_exact)
write(*,*) "PASSED!"
end program hip_dgemm
test/f2008/hipblas/sgemm.f08, cgemm.f08 and zgemm.f08 cover the
remaining precisions.
Batched matrix multiplication#
hipblas?gemmBatched runs several independent gemm calls in one launch,
with A, B and C each passed as a device array of device pointers.
hipFORT’s Fortran 2008 pointer-convenience interfaces cannot be used for this
argument shape: passing a plain array would only give hipBLAS a single
pointer, not the array of device pointers the routine requires. This test
therefore builds the pointer array with the same type(c_ptr) idiom used in
the Fortran 2003 interfaces.
! Note: hipfort's Fortran 2008 pointer-convenience interfaces for
! hipblas?gemmBatched call c_loc() on a plain array and therefore pass a
! single pointer, not the array of device pointers the routine requires. This
! test consequently uses the type(c_ptr) array-of-pointers idiom (as in f2003);
! only the per-batch device allocation uses the f2008 hipMalloc(source=) style.
program hip_dgemm_batched
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer(kind(HIPBLAS_OP_N)), parameter :: transa = HIPBLAS_OP_N, transb = HIPBLAS_OP_N
double precision, parameter :: alpha = 1.1d0, beta = 0.9d0
integer, parameter :: m = 512, n = 512, k = 512, batch_count = 4
integer, parameter :: bytes_per_element = 8 ! double precision
integer, parameter :: size_a = m*k, size_b = k*n, size_c = m*n
integer(c_size_t), parameter :: Ncbytes = size_c*bytes_per_element
integer :: lda, ldb, ldc, i, b
double precision, allocatable, target, dimension(:,:) :: ha, hb, hc ! (elems, batch)
double precision, allocatable, dimension(:) :: hc_exact ! one value per batch
! Per-batch device matrices as typed Fortran pointers (reused each iteration)
double precision, pointer, dimension(:) :: da_b, db_b, dc_b
! Host arrays of device pointers (one device matrix per batch entry)
type(c_ptr), target :: da(batch_count), db(batch_count), dc(batch_count)
! Device-resident pointer arrays passed to the batched routine
type(c_ptr) :: da_p = c_null_ptr, db_p = c_null_ptr, dc_p = c_null_ptr
type(c_ptr) :: handle = c_null_ptr
double precision :: error
double precision, parameter :: error_max = 10*epsilon(error)
write(*,"(a)",advance="no") "-- Running test 'DGEMM_BATCHED' (Fortran 2008 interfaces) - "
! hipBLAS defaults to host pointer mode: no set-pointer-mode call needed
call hipblasCheck(hipblasCreate(handle))
lda = m; ldb = k; ldc = m
allocate(ha(size_a,0:batch_count-1))
allocate(hb(size_b,0:batch_count-1))
allocate(hc(size_c,0:batch_count-1))
allocate(hc_exact(0:batch_count-1))
! Constant matrices with a distinct per-batch value so the exact answer is
! a distinct constant per batch. A is held constant; B varies per batch.
ha(:,:) = 1.d0
do b = 0, batch_count-1
hb(:,b) = dble(b+1)
hc(:,b) = 3.d0
hc_exact(b) = alpha*k*dble(b+1) + beta*3.d0
end do
! Allocate one device matrix per batch entry (f2008 source= copy) and record
! its device address in the host pointer array
do b = 1, batch_count
call hipCheck(hipMalloc(da_b,source=ha(:,b-1)))
call hipCheck(hipMalloc(db_b,source=hb(:,b-1)))
call hipCheck(hipMalloc(dc_b,source=hc(:,b-1)))
da(b) = c_loc(da_b(1))
db(b) = c_loc(db_b(1))
dc(b) = c_loc(dc_b(1))
end do
! Allocate the device-resident pointer arrays and copy the host pointer
! arrays into them (hipBLAS requires the pointer array in device memory)
call hipCheck(hipMalloc(da_p, int(batch_count,c_size_t)*c_sizeof(da(1))))
call hipCheck(hipMalloc(db_p, int(batch_count,c_size_t)*c_sizeof(db(1))))
call hipCheck(hipMalloc(dc_p, int(batch_count,c_size_t)*c_sizeof(dc(1))))
call hipCheck(hipMemcpy(da_p, c_loc(da(1)), int(batch_count,c_size_t)*c_sizeof(da(1)), hipMemcpyHostToDevice))
call hipCheck(hipMemcpy(db_p, c_loc(db(1)), int(batch_count,c_size_t)*c_sizeof(db(1)), hipMemcpyHostToDevice))
call hipCheck(hipMemcpy(dc_p, c_loc(dc(1)), int(batch_count,c_size_t)*c_sizeof(dc(1)), hipMemcpyHostToDevice))
call hipblasCheck(hipblasDgemmBatched(handle,transa,transb,m,n,k, &
alpha,da_p,lda,db_p,ldb,beta,dc_p,ldc,batch_count))
call hipCheck(hipDeviceSynchronize())
! Transfer each batch result back to host memory
do b = 1, batch_count
call hipCheck(hipMemcpy(c_loc(hc(1,b-1)), dc(b), Ncbytes, hipMemcpyDeviceToHost))
end do
do b = 0, batch_count-1
do i = 1, size_c
error = abs((hc_exact(b) - hc(i,b))/hc_exact(b))
if( error > error_max )then
write(*,*) "FAILED! Error bigger than max! batch = ", b, " error = ", error
call exit(1)
end if
end do
end do
do b = 1, batch_count
call hipCheck(hipFree(da(b)))
call hipCheck(hipFree(db(b)))
call hipCheck(hipFree(dc(b)))
end do
call hipCheck(hipFree(da_p))
call hipCheck(hipFree(db_p))
call hipCheck(hipFree(dc_p))
call hipblasCheck(hipblasDestroy(handle))
deallocate(ha)
deallocate(hb)
deallocate(hc)
deallocate(hc_exact)
write(*,*) "PASSED!"
end program hip_dgemm_batched
test/f2008/hipblas/sgemm_batched.f08, cgemm_batched.f08 and
zgemm_batched.f08 cover the remaining precisions.
Strided-batched matrix multiplication#
hipblas?gemmStridedBatched also runs several gemm calls in one launch,
but A, B and C are contiguous device arrays with a fixed stride
between the start of each batch’s matrix, instead of an array of pointers.
This is usually simpler to set up than the batched form when the batches are
already laid out contiguously in memory.
program hip_dgemm_strided_batched
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer(kind(HIPBLAS_OP_N)), parameter :: transa = HIPBLAS_OP_N, transb = HIPBLAS_OP_N
double precision, parameter :: alpha = 1.1d0, beta = 0.9d0
integer, parameter :: m = 512, n = 512, k = 512, batch_count = 4
integer :: lda, ldb, ldc, i, b
integer(c_int64_t) :: stride_a, stride_b, stride_c
integer :: size_a, size_b, size_c
double precision, allocatable, dimension(:) :: ha, hb, hc
double precision, allocatable, dimension(:) :: hc_exact ! one value per batch
double precision, pointer, dimension(:) :: da, db, dc
type(c_ptr) :: handle = c_null_ptr
double precision :: error
double precision, parameter :: error_max = 10*epsilon(error)
write(*,"(a)",advance="no") "-- Running test 'DGEMM_STRIDED_BATCHED' (Fortran 2008 interfaces) - "
! hipBLAS defaults to host pointer mode: no set-pointer-mode call needed
call hipblasCheck(hipblasCreate(handle))
lda = m; ldb = k; ldc = m
stride_a = int(lda,c_int64_t)*k
stride_b = int(ldb,c_int64_t)*n
stride_c = int(ldc,c_int64_t)*n
size_a = int(stride_a)*batch_count
size_b = int(stride_b)*batch_count
size_c = int(stride_c)*batch_count
allocate(ha(size_a))
allocate(hb(size_b))
allocate(hc(size_c))
allocate(hc_exact(0:batch_count-1))
! Constant matrices with a distinct per-batch value so the exact answer is
! a distinct constant per batch. A is held constant; B varies per batch.
ha(:) = 1.d0
do b = 0, batch_count-1
hb(b*stride_b+1 : (b+1)*stride_b) = dble(b+1)
hc(b*stride_c+1 : (b+1)*stride_c) = 3.d0
hc_exact(b) = alpha*k*dble(b+1) + beta*3.d0
end do
! Allocate device memory
call hipCheck(hipMalloc(da,source=ha)) ! implies (blocking) memcpy
call hipCheck(hipMalloc(db,source=hb))
call hipCheck(hipMalloc(dc,source=hc))
call hipblasCheck(hipblasDgemmStridedBatched(handle,transa,transb,m,n,k, &
alpha,da,lda,stride_a,db,ldb,stride_b,beta,dc,ldc,stride_c,batch_count))
call hipCheck(hipDeviceSynchronize())
! Transfer data back to host memory
call hipCheck(hipMemcpy(hc, dc, hipMemcpyDeviceToHost))
do b = 0, batch_count-1
do i = 1, int(stride_c)
error = abs((hc_exact(b) - hc(b*stride_c+i))/hc_exact(b))
if( error > error_max )then
write(*,*) "FAILED! Error bigger than max! batch = ", b, " error = ", error
call exit(1)
end if
end do
end do
call hipCheck(hipFree(da))
call hipCheck(hipFree(db))
call hipCheck(hipFree(dc))
call hipblasCheck(hipblasDestroy(handle))
deallocate(ha)
deallocate(hb)
deallocate(hc)
deallocate(hc_exact)
write(*,*) "PASSED!"
end program hip_dgemm_strided_batched
test/f2008/hipblas/sgemm_strided_batched.f08,
cgemm_strided_batched.f08 and zgemm_strided_batched.f08 cover the
remaining precisions.
Triangular solve with multiple right-hand sides#
hipblas?trsm solves A * X = alpha * B in place for a triangular matrix
A and a matrix of right-hand sides B, which is the gemm-like
counterpart of trsv. dB holds B on entry and the solution X on
exit.
program hip_dtrsm
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
implicit none
integer(kind(HIPBLAS_SIDE_LEFT)), parameter :: side = HIPBLAS_SIDE_LEFT
integer(kind(HIPBLAS_FILL_MODE_LOWER)), parameter :: uplo = HIPBLAS_FILL_MODE_LOWER
integer(kind(HIPBLAS_OP_N)), parameter :: transA = HIPBLAS_OP_N
integer(kind(HIPBLAS_DIAG_NON_UNIT)), parameter :: diag = HIPBLAS_DIAG_NON_UNIT
integer, parameter :: m = 1024, n = 1024
double precision, parameter :: alpha = 2.d0
double precision, allocatable, target, dimension(:,:) :: hA, hB
double precision, pointer, dimension(:,:) :: dA, dB
type(c_ptr) :: handle = c_null_ptr
double precision, parameter :: x_exact = 1.d0
double precision :: error
double precision, parameter :: error_max = 10*epsilon(error)
integer :: i, j
write(*,"(a)",advance="no") "-- Running test 'DTRSM' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
allocate(hA(m,m), hB(m,n))
hA = 0.d0
do j = 1, m
do i = j, m
hA(i,j) = 1.d0
end do
end do
do j = 1, n
do i = 1, m
hB(i,j) = dble(i) / 2.d0
end do
end do
call hipCheck(hipMalloc(dA, source=hA))
call hipCheck(hipMalloc(dB, source=hB))
call hipblasCheck(hipblasDtrsm(handle, side, uplo, transA, diag, m, n, alpha, dA, size(dA,1), dB, size(dB,1)))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(hB, dB, hipMemcpyDeviceToHost))
do j = 1, n
do i = 1, m
error = abs((x_exact - hB(i,j)) / x_exact)
if (error > error_max) then
write(*,*) "FAILED! Error bigger than max! Error = ", error, " hB(", i, ",", j, ") = ", hB(i,j)
call exit(1)
end if
end do
end do
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dB))
call hipblasCheck(hipblasDestroy(handle))
deallocate(hA, hB)
write(*,*) "PASSED!"
end program hip_dtrsm
test/f2008/hipblas/strsm.f08, ctrsm.f08 and ztrsm.f08 cover the
remaining precisions.
Triangular matrix multiplication#
hipblas?trmm computes C := alpha * op(A) * B (or the mirrored
right-hand form), where A is triangular and only the triangle chosen by
the fill mode is referenced.
program hip_strmm
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
use hipfort_hipblas_enums
implicit none
! C := alpha*op(A)*B with A upper triangular (out-of-place trmm).
! A = [1 2; 0 3] and B = I, so C = A.
integer, parameter :: ld = 2
real(c_float), parameter :: alpha = 1.0
real(c_float) :: hA(ld,ld) = reshape([1.0, 0.0, 2.0, 3.0], [ld,ld])
real(c_float) :: hB(ld,ld) = reshape([1.0, 0.0, 0.0, 1.0], [ld,ld])
real(c_float) :: hC(ld,ld)
real(c_float) :: expected(ld,ld) = reshape([1.0, 0.0, 2.0, 3.0], [ld,ld])
real(c_float), pointer, dimension(:,:) :: dA, dB, dC
type(c_ptr) :: handle = c_null_ptr
integer :: i, j
real(c_float) :: error
real(c_float), parameter :: error_max = 10*epsilon(error)
write(*,"(a)",advance="no") "-- Running test 'strmm' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
hC = 0.0
call hipCheck(hipMalloc(dA, shape(hA)))
call hipCheck(hipMemcpy(dA, hA, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dB, shape(hB)))
call hipCheck(hipMemcpy(dB, hB, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dC, shape(hC)))
call hipCheck(hipMemcpy(dC, hC, hipMemcpyHostToDevice))
call hipblasCheck(hipblasStrmm(handle, HIPBLAS_SIDE_LEFT, HIPBLAS_FILL_MODE_UPPER, HIPBLAS_OP_N, &
HIPBLAS_DIAG_NON_UNIT, ld, ld, alpha, dA, size(dA,1), dB, size(dB,1), dC, size(dA,1)))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(hC, dC, hipMemcpyDeviceToHost))
do j = 1, ld
do i = 1, ld
error = abs(expected(i,j) - hC(i,j))
if (error > error_max) then
write(*,*) "FAILED! error = ", error, " at ", i, j
call exit(1)
end if
end do
end do
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dB))
call hipCheck(hipFree(dC))
call hipblasCheck(hipblasDestroy(handle))
write(*,*) "PASSED!"
end program hip_strmm
test/f2008/hipblas/dtrmm.f08, ctrmm.f08 and ztrmm.f08 cover the
remaining precisions.
Rank-k update (syrk)#
hipblas?syrk computes C := alpha * op(A) * op(A)**T + beta * C, where
C is symmetric and only the triangle chosen by the fill mode is
referenced.
program hip_ssyrk
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
use hipfort_hipblas_enums
implicit none
! C := alpha*A*A**T + beta*C, upper triangle. A = [1 0; 2 3] gives
! A*A**T = [1 2; 2 13], so the referenced upper triangle is 1, 2, 13.
integer, parameter :: ld = 2
real(c_float), parameter :: alpha = 1.0, beta = 0.0
real(c_float) :: hA(ld,ld) = reshape([1.0, 2.0, 0.0, 3.0], [ld,ld])
real(c_float) :: hC(ld,ld)
real(c_float) :: expected(ld,ld) = reshape([1.0, 0.0, 2.0, 13.0], [ld,ld])
real(c_float), pointer, dimension(:,:) :: dA, dC
type(c_ptr) :: handle = c_null_ptr
integer :: i, j
real(c_float) :: error
real(c_float), parameter :: error_max = 10*epsilon(error)
write(*,"(a)",advance="no") "-- Running test 'ssyrk' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
hC = 0.0
call hipCheck(hipMalloc(dA, shape(hA)))
call hipCheck(hipMemcpy(dA, hA, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dC, shape(hC)))
call hipCheck(hipMemcpy(dC, hC, hipMemcpyHostToDevice))
call hipblasCheck(hipblasSsyrk(handle, HIPBLAS_FILL_MODE_UPPER, HIPBLAS_OP_N, ld, ld, &
alpha, dA, size(dA,1), beta, dC, size(dA,1)))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(hC, dC, hipMemcpyDeviceToHost))
do j = 1, ld
do i = 1, j
error = abs(expected(i,j) - hC(i,j))
if (error > error_max) then
write(*,*) "FAILED! error = ", error, " at ", i, j
call exit(1)
end if
end do
end do
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dC))
call hipblasCheck(hipblasDestroy(handle))
write(*,*) "PASSED!"
end program hip_ssyrk
test/f2008/hipblas/dsyrk.f08, csyrk.f08 and zsyrk.f08 cover the
remaining precisions. The Hermitian form, herk, has no hipBLAS example
here; the rocBLAS examples cover it.
Symmetric matrix product (symm)#
hipblas?symm computes C := alpha * A * B + beta * C with A
symmetric, or the mirrored right-hand form selected by the side argument.
As with syrk, only one triangle of A is referenced.
program hip_ssymm
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
use hipfort_hipblas_enums
implicit none
! C := alpha*A*B + beta*C with A symmetric (upper triangle referenced).
! A = [1 2; 2 3] and B = I, so C = A.
integer, parameter :: ld = 2
real(c_float), parameter :: alpha = 1.0, beta = 0.0
real(c_float) :: hA(ld,ld) = reshape([1.0, 2.0, 2.0, 3.0], [ld,ld])
real(c_float) :: hB(ld,ld) = reshape([1.0, 0.0, 0.0, 1.0], [ld,ld])
real(c_float) :: hC(ld,ld)
real(c_float) :: expected(ld,ld) = reshape([1.0, 2.0, 2.0, 3.0], [ld,ld])
real(c_float), pointer, dimension(:,:) :: dA, dB, dC
type(c_ptr) :: handle = c_null_ptr
integer :: i, j
real(c_float) :: error
real(c_float), parameter :: error_max = 10*epsilon(error)
write(*,"(a)",advance="no") "-- Running test 'ssymm' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
hC = 0.0
call hipCheck(hipMalloc(dA, shape(hA)))
call hipCheck(hipMemcpy(dA, hA, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dB, shape(hB)))
call hipCheck(hipMemcpy(dB, hB, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dC, shape(hC)))
call hipCheck(hipMemcpy(dC, hC, hipMemcpyHostToDevice))
call hipblasCheck(hipblasSsymm(handle, HIPBLAS_SIDE_LEFT, HIPBLAS_FILL_MODE_UPPER, ld, ld, &
alpha, dA, size(dA,1), dB, size(dB,1), beta, dC, size(dA,1)))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(hC, dC, hipMemcpyDeviceToHost))
do j = 1, ld
do i = 1, ld
error = abs(expected(i,j) - hC(i,j))
if (error > error_max) then
write(*,*) "FAILED! error = ", error, " at ", i, j
call exit(1)
end if
end do
end do
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dB))
call hipCheck(hipFree(dC))
call hipblasCheck(hipblasDestroy(handle))
write(*,*) "PASSED!"
end program hip_ssymm
test/f2008/hipblas/dsymm.f08, csymm.f08 and zsymm.f08 cover the
remaining precisions. The Hermitian form, hemm, likewise appears only
among the rocBLAS programs.
Matrix addition and transposition (geam)#
hipblas?geam computes C := alpha * op(A) + beta * op(B). Because each
operand has its own transpose flag and either scalar may be zero, the same
routine also serves as an out-of-place transpose or a scaled copy.
program hip_sgeam
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
use hipfort_hipblas_enums
implicit none
! C := alpha*op(A) + beta*op(B) with alpha = beta = 1 and no
! transposition, so C is the elementwise sum A + B.
integer, parameter :: ld = 2
real(c_float), parameter :: alpha = 1.0, beta = 1.0
real(c_float) :: hA(ld,ld) = reshape([1.0, 2.0, 3.0, 4.0], [ld,ld])
real(c_float) :: hB(ld,ld) = reshape([10.0, 20.0, 30.0, 40.0], [ld,ld])
real(c_float) :: hC(ld,ld)
real(c_float) :: expected(ld,ld) = reshape([11.0, 22.0, 33.0, 44.0], [ld,ld])
real(c_float), pointer, dimension(:,:) :: dA, dB, dC
type(c_ptr) :: handle = c_null_ptr
integer :: i, j
real(c_float) :: error
real(c_float), parameter :: error_max = 10*epsilon(error)
write(*,"(a)",advance="no") "-- Running test 'sgeam' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
hC = 0.0
call hipCheck(hipMalloc(dA, shape(hA)))
call hipCheck(hipMemcpy(dA, hA, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dB, shape(hB)))
call hipCheck(hipMemcpy(dB, hB, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dC, shape(hC)))
call hipCheck(hipMemcpy(dC, hC, hipMemcpyHostToDevice))
call hipblasCheck(hipblasSgeam(handle, HIPBLAS_OP_N, HIPBLAS_OP_N, ld, ld, &
alpha, dA, size(dA,1), beta, dB, size(dB,1), dC, size(dA,1)))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(hC, dC, hipMemcpyDeviceToHost))
do j = 1, ld
do i = 1, ld
error = abs(expected(i,j) - hC(i,j))
if (error > error_max) then
write(*,*) "FAILED! error = ", error, " at ", i, j
call exit(1)
end if
end do
end do
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dB))
call hipCheck(hipFree(dC))
call hipblasCheck(hipblasDestroy(handle))
write(*,*) "PASSED!"
end program hip_sgeam
test/f2008/hipblas/dgeam.f08, cgeam.f08 and zgeam.f08 cover the
remaining precisions.
Extended-precision matrix multiplication (GemmEx)#
hipblasGemmEx computes D := alpha * op(A) * op(B) + beta * C with the
type of every buffer, and the type used for the arithmetic, given explicitly
as arguments rather than fixed by the routine name. That makes it the entry
point for mixed precision work.
program hip_gemmex
use iso_c_binding
use hipfort
use hipfort_check
use hipfort_hipblas
use hipfort_hipblas_enums
implicit none
! C := alpha*op(A)*op(B) + beta*C through the extended-precision entry point,
! with all buffers f32 and an f32 compute type. A = I and beta = 0, so C = B.
integer, parameter :: ld = 2
real(c_float), target :: alpha = 1.0, beta = 0.0
real(c_float) :: hA(ld,ld) = reshape([1.0, 0.0, 0.0, 1.0], [ld,ld])
real(c_float) :: hB(ld,ld) = reshape([1.0, 2.0, 3.0, 4.0], [ld,ld])
real(c_float) :: hC(ld,ld)
real(c_float) :: expected(ld,ld) = reshape([1.0, 2.0, 3.0, 4.0], [ld,ld])
real(c_float), pointer, dimension(:,:) :: dA, dB, dC
type(c_ptr) :: handle = c_null_ptr
integer :: i, j
real(c_float) :: error
real(c_float), parameter :: error_max = 10*epsilon(error)
write(*,"(a)",advance="no") "-- Running test 'gemmex' (Fortran 2008 interfaces) - "
call hipblasCheck(hipblasCreate(handle))
hC = 0.0
call hipCheck(hipMalloc(dA, shape(hA)))
call hipCheck(hipMemcpy(dA, hA, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dB, shape(hB)))
call hipCheck(hipMemcpy(dB, hB, hipMemcpyHostToDevice))
call hipCheck(hipMalloc(dC, shape(hC)))
call hipCheck(hipMemcpy(dC, hC, hipMemcpyHostToDevice))
call hipblasCheck(hipblasGemmEx(handle, HIPBLAS_OP_N, HIPBLAS_OP_N, ld, ld, ld, &
c_loc(alpha), c_loc(dA(1,1)), HIP_R_32F, ld, &
c_loc(dB(1,1)), HIP_R_32F, ld, c_loc(beta), &
c_loc(dC(1,1)), HIP_R_32F, ld, HIPBLAS_COMPUTE_32F, HIPBLAS_GEMM_DEFAULT))
call hipCheck(hipDeviceSynchronize())
call hipCheck(hipMemcpy(hC, dC, hipMemcpyDeviceToHost))
do j = 1, ld
do i = 1, ld
error = abs(expected(i,j) - hC(i,j))
if (error > error_max) then
write(*,*) "FAILED! error = ", error, " at ", i, j
call exit(1)
end if
end do
end do
call hipCheck(hipFree(dA))
call hipCheck(hipFree(dB))
call hipCheck(hipFree(dC))
call hipblasCheck(hipblasDestroy(handle))
write(*,*) "PASSED!"
end program hip_gemmex
Because the buffer types are runtime arguments, there is a single GemmEx
program rather than one per precision. Note the source file is gemmex.f08,
without the underscore used by the rocBLAS equivalent gemm_ex.f08.