Skip to content

Commit 732dcd3

Browse files
committed
refactor(tests): centralise CUDA runtime handling
- share rank-aware device selection across tests and benchmarks - query bandwidth information from the active CUDA device - standardise MPI lifecycle in remaining CUDA tests - propagate benchmark runtime failures across MPI ranks
1 parent a26850b commit 732dcd3

12 files changed

Lines changed: 149 additions & 102 deletions

src/backend/backend_runtime.f90

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,27 @@ module m_backend_runtime
5656

5757
contains
5858

59+
#ifdef CUDA
60+
subroutine select_cuda_device(nrank, devnum)
61+
!! Select a CUDA device round-robin by MPI rank. The optional result
62+
!! returns the device that CUDA reports as current after selection.
63+
integer, intent(in) :: nrank
64+
integer, optional, intent(out) :: devnum
65+
66+
integer :: ierr, ndevs, selected_device
67+
68+
ierr = cudaGetDeviceCount(ndevs)
69+
if (ndevs < 1) then
70+
error stop 'select_cuda_device: no CUDA devices available'
71+
end if
72+
73+
ierr = cudaSetDevice(mod(nrank, ndevs))
74+
ierr = cudaGetDevice(selected_device)
75+
76+
if (present(devnum)) devnum = selected_device
77+
end subroutine select_cuda_device
78+
#endif
79+
5980
subroutine init(self, mesh, separate_host_allocator)
6081
class(backend_runtime_t), target, intent(inout) :: self
6182
type(mesh_t), target, intent(inout) :: mesh
@@ -67,7 +88,7 @@ subroutine init(self, mesh, separate_host_allocator)
6788
#endif
6889

6990
#ifdef CUDA
70-
integer :: ierr, nrank, ndevs, devnum
91+
integer :: ierr, nrank
7192
#endif
7293

7394
dims = mesh%grid%vert_dims
@@ -80,12 +101,7 @@ subroutine init(self, mesh, separate_host_allocator)
80101

81102
#ifdef CUDA
82103
call MPI_Comm_rank(MPI_COMM_WORLD, nrank, ierr)
83-
ierr = cudaGetDeviceCount(ndevs)
84-
if (ndevs < 1) then
85-
error stop 'backend_runtime_t%init: no CUDA devices available'
86-
end if
87-
ierr = cudaSetDevice(mod(nrank, ndevs))
88-
ierr = cudaGetDevice(devnum)
104+
call select_cuda_device(nrank)
89105

90106
self%backend_name = 'CUDA'
91107
self%cuda_allocator = cuda_allocator_t(dims, SZ)

src/backend/cuda/backend.f90

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,13 @@ subroutine get_device_bw_info_cuda(self, mem_clock_rt, mem_bus_width, &
202202
integer, intent(out) :: mem_clock_rt
203203
integer, intent(out) :: mem_bus_width
204204
logical, intent(out) :: available
205-
integer :: ierr
205+
integer :: ierr, devnum
206206

207-
ierr = cudaDeviceGetAttribute(mem_clock_rt, cudaDevAttrMemoryClockRate, 0)
207+
ierr = cudaGetDevice(devnum)
208+
ierr = cudaDeviceGetAttribute(mem_clock_rt, cudaDevAttrMemoryClockRate, &
209+
devnum)
208210
ierr = cudaDeviceGetAttribute(mem_bus_width, &
209-
cudaDevAttrGlobalMemoryBusWidth, 0)
211+
cudaDevAttrGlobalMemoryBusWidth, devnum)
210212
available = .true.
211213

212214
end subroutine get_device_bw_info_cuda

tests/common/test_utils.f90

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ module m_test_utils
55
implicit none
66

77
private
8-
public :: initialise_mpi, finalise_test, global_all, global_sum, checkerr, &
8+
public :: initialise_mpi, finalise_test, global_all, global_sum, &
9+
check_status, checkerr, &
910
write_perf_metric, write_perf_minmax_metrics, &
1011
write_perf_summary, write_perf_minmax_summary, &
1112
write_device_bw_metric
@@ -48,6 +49,19 @@ subroutine global_sum(value)
4849
MPI_COMM_WORLD, ierr)
4950
end subroutine global_sum
5051

52+
subroutine check_status(status, label, allpass)
53+
!! Mark a test or benchmark as failed when an API returns a non-zero
54+
!! status code. The caller decides where ranks must synchronise allpass.
55+
integer, intent(in) :: status
56+
character(len=*), intent(in) :: label
57+
logical, intent(inout) :: allpass
58+
59+
if (status /= 0) then
60+
write (stderr, '(a,": status=",i0)') trim(label), status
61+
allpass = .false.
62+
end if
63+
end subroutine check_status
64+
5165
subroutine finalise_test(allpass, nrank, finalize_mpi)
5266
!! Report the aggregate test result and finalise MPI, aborting via
5367
!! `error stop` (non-zero exit status) if any check failed. When nrank is

tests/performance/perf_cuda_penta.f90

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ program perf_cuda_penta
1313
use m_cuda_common, only: SZ
1414
use m_cuda_exec_dist, only: exec_dist_penta_compact
1515
use m_cuda_tdsops, only: cuda_tdsops_t, cuda_tdsops_init
16-
use m_test_utils, only: initialise_mpi, write_perf_minmax_metrics, &
16+
use m_backend_runtime, only: select_cuda_device
17+
use m_test_utils, only: initialise_mpi, finalise_test, global_all, &
18+
check_status, &
19+
write_perf_minmax_metrics, &
1720
write_perf_minmax_summary, write_device_bw_metric
1821

1922
implicit none
@@ -31,23 +34,26 @@ program perf_cuda_penta
3134
integer :: n, n_block, n_halo, n_iters, n_warmup, n_glob, ndof, nrank, nproc
3235
integer :: pprev, pnext
3336
integer :: ierr
34-
integer :: ndevs, devnum
37+
integer :: devnum
3538
integer :: memClockRt, memBusWidth
3639
type(dim3) :: blocks, threads
3740
real(dp) :: dx
41+
logical :: allpass = .true.
3842

3943
call initialise_mpi(nrank, nproc, pprev, pnext)
4044
if (nrank == 0) then
4145
print *, 'Performance benchmark for compact10_penta (Lele 10 penta 1st-deriv)'
4246
print *, 'Scheme: alpha=0.5 beta=0.05, non-periodic, single-GPU Thomas'
4347
print *, 'Ranks:', nproc
4448
end if
45-
call select_device()
49+
call select_cuda_device(nrank, devnum)
4650
call configure_benchmark()
4751
call allocate_fields()
4852
call setup_backend()
49-
call run_case('penta_lele10_nonper', dx, penta_bw)
50-
call finalise()
53+
call global_all(allpass)
54+
if (allpass) call run_case('penta_lele10_nonper', dx, penta_bw)
55+
call global_all(allpass)
56+
call finalise_test(allpass, nrank)
5157

5258
contains
5359

@@ -72,12 +78,6 @@ subroutine allocate_fields()
7278
u_recv_e_dev = 0._dp
7379
end subroutine allocate_fields
7480

75-
subroutine select_device()
76-
ierr = cudaGetDeviceCount(ndevs)
77-
ierr = cudaSetDevice(mod(nrank, ndevs))
78-
ierr = cudaGetDevice(devnum)
79-
end subroutine select_device
80-
8181
subroutine setup_backend()
8282
integer :: i, j, k
8383

@@ -101,7 +101,9 @@ subroutine setup_backend()
101101
threads = dim3(SZ, 1, 1)
102102

103103
ierr = cudaDeviceGetAttribute(memClockRt, cudaDevAttrMemoryClockRate, devnum)
104+
call check_status(ierr, 'query CUDA memory clock rate', allpass)
104105
ierr = cudaDeviceGetAttribute(memBusWidth, cudaDevAttrGlobalMemoryBusWidth, devnum)
106+
call check_status(ierr, 'query CUDA memory bus width', allpass)
105107
end subroutine setup_backend
106108

107109
subroutine run_case(case_name, delta, consumed_bw)
@@ -119,12 +121,14 @@ subroutine run_case(case_name, delta, consumed_bw)
119121
call run_kernel()
120122
end do
121123
call sync_backend()
124+
if (.not. allpass) return
122125

123126
call start_timer(tstart)
124127
do iter = 1, n_iters
125128
call run_kernel()
126129
end do
127130
call sync_backend()
131+
if (.not. allpass) return
128132
call stop_timer(tend)
129133

130134
call collect_perf_stats(tend - tstart, consumed_bw, achieved_bw, &
@@ -180,7 +184,9 @@ end subroutine collect_perf_stats
180184

181185
subroutine sync_backend()
182186
ierr = cudaDeviceSynchronize()
187+
call check_status(ierr, 'synchronise CUDA device', allpass)
183188
call MPI_Barrier(MPI_COMM_WORLD, ierr)
189+
call global_all(allpass)
184190
end subroutine sync_backend
185191

186192
subroutine start_timer(t)
@@ -195,8 +201,4 @@ subroutine stop_timer(t)
195201
call cpu_time(t)
196202
end subroutine stop_timer
197203

198-
subroutine finalise()
199-
call MPI_Finalize(ierr)
200-
end subroutine finalise
201-
202204
end program perf_cuda_penta

tests/performance/perf_cuda_reorder.f90

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ program perf_cuda_reorder
66
use m_cuda_kernels_reorder, only: reorder_x2y, reorder_x2z, reorder_y2x, &
77
reorder_y2z, reorder_z2x, reorder_z2y, &
88
reorder_c2x, reorder_x2c
9-
use m_test_utils, only: write_perf_metric, write_perf_summary, &
9+
use m_backend_runtime, only: select_cuda_device
10+
use m_test_utils, only: initialise_mpi, finalise_test, global_all, &
11+
check_status, &
12+
write_perf_metric, write_perf_summary, &
1013
write_device_bw_metric
1114

1215
implicit none
@@ -29,12 +32,17 @@ program perf_cuda_reorder
2932

3033
integer :: ierr
3134
integer :: n_block, ndof
35+
integer :: nrank, nproc, devnum
3236
integer :: memClockRt, memBusWidth
3337
real(dp), allocatable :: u_i(:, :, :)
3438
real(dp), device, allocatable :: u_i_d(:, :, :), u_o_d(:, :, :), &
3539
u_temp_d(:, :, :)
3640
real(dp), device, allocatable :: u_c_d(:, :, :)
3741
type(dim3) :: blocks, threads
42+
logical :: allpass = .true.
43+
44+
call initialise_mpi(nrank, nproc)
45+
call select_cuda_device(nrank, devnum)
3846

3947
n_block = ny*nz/SZ
4048
ndof = nx*ny*nz
@@ -47,20 +55,26 @@ program perf_cuda_reorder
4755
call random_number(u_i)
4856
u_i_d = u_i
4957

50-
ierr = cudaDeviceGetAttribute(memClockRt, cudaDevAttrMemoryClockRate, 0)
58+
ierr = cudaDeviceGetAttribute(memClockRt, cudaDevAttrMemoryClockRate, &
59+
devnum)
60+
call check_status(ierr, 'query CUDA memory clock rate', allpass)
5161
ierr = cudaDeviceGetAttribute(memBusWidth, &
52-
cudaDevAttrGlobalMemoryBusWidth, 0)
53-
54-
call run_case('cuda_reorder_x2y', CASE_X2Y)
55-
call run_case('cuda_reorder_x2z', CASE_X2Z)
56-
call run_case('cuda_reorder_y2x', CASE_Y2X)
57-
call run_case('cuda_reorder_y2z', CASE_Y2Z)
58-
call run_case('cuda_reorder_z2x', CASE_Z2X)
59-
call run_case('cuda_reorder_z2y', CASE_Z2Y)
60-
call run_case('cuda_reorder_x2c', CASE_X2C)
61-
call run_case('cuda_reorder_c2x', CASE_C2X)
62-
63-
call write_device_bw_metric(memClockRt, memBusWidth)
62+
cudaDevAttrGlobalMemoryBusWidth, devnum)
63+
call check_status(ierr, 'query CUDA memory bus width', allpass)
64+
call global_all(allpass)
65+
66+
if (allpass) call run_case('cuda_reorder_x2y', CASE_X2Y)
67+
if (allpass) call run_case('cuda_reorder_x2z', CASE_X2Z)
68+
if (allpass) call run_case('cuda_reorder_y2x', CASE_Y2X)
69+
if (allpass) call run_case('cuda_reorder_y2z', CASE_Y2Z)
70+
if (allpass) call run_case('cuda_reorder_z2x', CASE_Z2X)
71+
if (allpass) call run_case('cuda_reorder_z2y', CASE_Z2Y)
72+
if (allpass) call run_case('cuda_reorder_x2c', CASE_X2C)
73+
if (allpass) call run_case('cuda_reorder_c2x', CASE_C2X)
74+
75+
if (allpass) call write_device_bw_metric(memClockRt, memBusWidth)
76+
call global_all(allpass)
77+
call finalise_test(allpass, nrank)
6478

6579
contains
6680

@@ -74,17 +88,20 @@ subroutine run_case(label, case_id)
7488
print *, 'Performance test:', trim(label)
7589

7690
call prepare_input(case_id)
91+
if (.not. allpass) return
7792

7893
do iter = 1, n_warmup
7994
call launch_kernel(case_id)
8095
end do
8196
call sync_device()
97+
if (.not. allpass) return
8298

8399
call cpu_time(tstart)
84100
do iter = 1, n_iters
85101
call launch_kernel(case_id)
86102
end do
87103
call sync_device()
104+
if (.not. allpass) return
88105
call cpu_time(tend)
89106

90107
call write_perf_metric(label, tend - tstart, n_iters, ndof, consumed_bw)
@@ -157,6 +174,8 @@ end subroutine launch_kernel
157174

158175
subroutine sync_device()
159176
ierr = cudaDeviceSynchronize()
177+
call check_status(ierr, 'synchronise CUDA device', allpass)
178+
call global_all(allpass)
160179
end subroutine sync_device
161180

162181
end program perf_cuda_reorder

tests/performance/perf_cuda_transeq.f90

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ program perf_cuda_transeq
77
use m_cuda_exec_dist, only: exec_dist_transeq_3fused
88
use m_cuda_sendrecv, only: sendrecv_fields
99
use m_cuda_tdsops, only: cuda_tdsops_t
10-
use m_test_utils, only: initialise_mpi, write_perf_minmax_metrics, &
10+
use m_backend_runtime, only: select_cuda_device
11+
use m_test_utils, only: initialise_mpi, finalise_test, global_all, &
12+
check_status, &
13+
write_perf_minmax_metrics, &
1114
write_perf_minmax_summary, write_device_bw_metric
1215

1316
implicit none
@@ -34,19 +37,22 @@ program perf_cuda_transeq
3437
integer :: n, n_block, n_halo, n_iters, n_warmup, n_glob, ndof, nrank, nproc
3538
integer :: pprev, pnext
3639
integer :: ierr
37-
integer :: ndevs, devnum
40+
integer :: devnum
3841
integer :: memClockRt, memBusWidth
3942
type(dim3) :: blocks, threads
4043
real(dp) :: dx_per, nu
44+
logical :: allpass = .true.
4145

4246
call initialise_mpi(nrank, nproc, pprev, pnext)
4347
if (nrank == 0) print *, 'Performance benchmark with', nproc, 'ranks'
44-
call select_device()
48+
call select_cuda_device(nrank, devnum)
4549
call configure_benchmark()
4650
call allocate_fields()
4751
call setup_backend()
48-
call run_case('periodic', dx_per, periodic_bw)
49-
call finalise()
52+
call global_all(allpass)
53+
if (allpass) call run_case('periodic', dx_per, periodic_bw)
54+
call global_all(allpass)
55+
call finalise_test(allpass, nrank)
5056

5157
contains
5258

@@ -80,12 +86,6 @@ subroutine allocate_fields()
8086
allocate (d2u_recv_s_dev(SZ, 1, n_block), d2u_recv_e_dev(SZ, 1, n_block))
8187
end subroutine allocate_fields
8288

83-
subroutine select_device()
84-
ierr = cudaGetDeviceCount(ndevs)
85-
ierr = cudaSetDevice(mod(nrank, ndevs))
86-
ierr = cudaGetDevice(devnum)
87-
end subroutine select_device
88-
8989
subroutine setup_backend()
9090
integer :: i, j, k
9191

@@ -112,7 +112,9 @@ subroutine setup_backend()
112112

113113
ierr = cudaDeviceGetAttribute(memClockRt, cudaDevAttrMemoryClockRate, &
114114
devnum)
115+
call check_status(ierr, 'query CUDA memory clock rate', allpass)
115116
ierr = cudaDeviceGetAttribute(memBusWidth, cudaDevAttrGlobalMemoryBusWidth, devnum)
117+
call check_status(ierr, 'query CUDA memory bus width', allpass)
116118
end subroutine setup_backend
117119

118120
subroutine run_case(case_name, delta, consumed_bw)
@@ -130,12 +132,14 @@ subroutine run_case(case_name, delta, consumed_bw)
130132
call run_kernel()
131133
end do
132134
call sync_backend()
135+
if (.not. allpass) return
133136

134137
call start_timer(tstart)
135138
do iter = 1, n_iters
136139
call run_kernel()
137140
end do
138141
call sync_backend()
142+
if (.not. allpass) return
139143
call stop_timer(tend)
140144

141145
call collect_perf_stats(tend - tstart, consumed_bw, achieved_bw, &
@@ -216,7 +220,9 @@ end subroutine collect_perf_stats
216220

217221
subroutine sync_backend()
218222
ierr = cudaDeviceSynchronize()
223+
call check_status(ierr, 'synchronise CUDA device', allpass)
219224
call MPI_Barrier(MPI_COMM_WORLD, ierr)
225+
call global_all(allpass)
220226
end subroutine sync_backend
221227

222228
subroutine start_timer(t)
@@ -231,8 +237,4 @@ subroutine stop_timer(t)
231237
call cpu_time(t)
232238
end subroutine stop_timer
233239

234-
subroutine finalise()
235-
call MPI_Finalize(ierr)
236-
end subroutine finalise
237-
238240
end program perf_cuda_transeq

0 commit comments

Comments
 (0)