(git:39fe4c4)
Loading...
Searching...
No Matches
dbm_multiply_gpu.c
Go to the documentation of this file.
1/*----------------------------------------------------------------------------*/
2/* CP2K: A general program to perform molecular dynamics simulations */
3/* Copyright 2000-2026 CP2K developers group <https://cp2k.org> */
4/* */
5/* SPDX-License-Identifier: BSD-3-Clause */
6/*----------------------------------------------------------------------------*/
7
8#include "../offload/offload_runtime.h"
9#if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
10
11#include "../offload/offload_library.h"
12#include "../offload/offload_mempool.h"
13#include "dbm_hyperparams.h"
14#include "dbm_multiply_gpu.h"
16
17#include <assert.h>
18#include <stdio.h>
19
20/*******************************************************************************
21 * \brief Internal routine for initializing the gpu backend.
22 * \author Ole Schuett
23 ******************************************************************************/
24void dbm_multiply_gpu_start(const int max_batch_size, const int nshards,
25 dbm_shard_t *shards_c_host,
26 dbm_multiply_gpu_context_t *ctx) {
27 // Select GPU device.
29
30 ctx->nshards = nshards;
31 ctx->max_batch_size = max_batch_size;
32 offloadStreamCreate(&ctx->main_stream);
33 offloadEventCreate(&ctx->upload_event);
34
35 // Allocate device storage for batches.
36 const size_t size = nshards * max_batch_size * sizeof(dbm_task_t);
37 ctx->batches_dev = offload_mempool_device_malloc(size);
38
39 // Allocate and upload shards of result matrix C.
40 ctx->shards_c_dev = malloc(nshards * sizeof(dbm_shard_gpu_t));
41 assert(ctx->shards_c_dev != NULL || nshards == 0);
42 for (int i = 0; i < nshards; i++) {
43 const dbm_shard_t *const shard_c_host = &shards_c_host[i];
44 dbm_shard_gpu_t *const shard_g = &ctx->shards_c_dev[i];
45 shard_g->data_size = shard_c_host->data_size;
46 offloadStreamCreate(&shard_g->stream);
47 offloadEventCreate(&shard_g->event);
48 // only allocate data_size on device rather than data_allocated
49 shard_g->data_allocated = shard_c_host->data_size;
50 shard_g->data =
51 offload_mempool_device_malloc(shard_g->data_allocated * sizeof(double));
52 offloadMemcpyAsyncHtoD(shard_g->data, shard_c_host->data,
53 shard_g->data_size * sizeof(double),
54 shard_g->stream);
55 }
56}
57
58/*******************************************************************************
59 * \brief Private routine for uploading a single pack onto the device.
60 * \author Ole Schuett
61 ******************************************************************************/
62static void upload_pack(const dbm_pack_t *pack_host, dbm_pack_gpu_t *pack_dev,
63 const offloadStream_t stream) {
64 // Reallocate only when the device buffer is too small; data_allocated tracks
65 // the device capacity independently from the host-side data_size that changes
66 // each tick (previous code compared against the wrong field).
67 const size_t size = pack_host->data_size * sizeof(double);
68 if (pack_dev->data_allocated < pack_host->data_size) {
69 offload_mempool_device_free(pack_dev->data);
70 pack_dev->data = offload_mempool_device_malloc(size);
71 pack_dev->data_allocated = pack_host->data_size;
72 }
73 offloadMemcpyAsyncHtoD(pack_dev->data, pack_host->data, size, stream);
74}
75
76/*******************************************************************************
77 * \brief Internal routine for uploading newly arrived packs onto the device.
78 * \author Ole Schuett and Hans Pabst
79 ******************************************************************************/
80bool dbm_multiply_gpu_upload_packs(const dbm_pack_t *pack_a,
81 const dbm_pack_t *pack_b,
82 dbm_multiply_gpu_context_t *ctx) {
83 // Assume GPU device was activated earlier.
84 // Wait for all c-streams to complete before overwriting old packs.
85 for (int i = 0; i < ctx->nshards; i++) {
86 offloadEventRecord(ctx->upload_event, ctx->shards_c_dev[i].stream);
87 offloadStreamWaitEvent(ctx->main_stream, ctx->upload_event);
88 }
89 // Record event to check if all c-streams already completed.
90 offloadEventRecord(ctx->upload_event, ctx->main_stream);
91
92 bool uploaded = false;
93 /*if (offloadEventQuery(ctx->upload_event))*/
94 {
95 upload_pack(pack_a, &ctx->pack_a_dev, ctx->main_stream);
96 upload_pack(pack_b, &ctx->pack_b_dev, ctx->main_stream);
97
98 // Have all c-streams wait until new packs are uploaded.
99 offloadEventRecord(ctx->upload_event, ctx->main_stream);
100 for (int i = 0; i < ctx->nshards; i++) {
101 offloadStreamWaitEvent(ctx->shards_c_dev[i].stream, ctx->upload_event);
102 }
103 uploaded = true;
104 }
105
106 return uploaded;
107}
108
109/*******************************************************************************
110 * \brief Internal routine for executing the tasks in given batch on the GPU.
111 * \author Ole Schuett
112 ******************************************************************************/
113void dbm_multiply_gpu_process_batch(const int ntasks, const dbm_task_t *batch,
114 const double alpha, dbm_shard_t *shard_c,
115 const int kshard, const bool finish,
116 dbm_multiply_gpu_context_t *ctx) {
117 // Assume GPU device was activated earlier.
118 dbm_shard_gpu_t *const shard_g = &ctx->shards_c_dev[kshard];
119 double *old_data_dev = NULL;
120
121 if (0 < ntasks) {
122 assert(NULL != shard_c && NULL != shard_g);
123
124 // Upload new batch.
125 dbm_task_t *batch_dev = &ctx->batches_dev[kshard * ctx->max_batch_size];
126 const size_t size = ntasks * sizeof(dbm_task_t);
127 offloadMemcpyAsyncHtoD(batch_dev, batch, size, shard_g->stream);
128
129 // Reallocate shard_g->data if necessary.
130 if (shard_c->data_promised > shard_g->data_allocated) {
131 shard_g->data_allocated = DBM_ALLOCATION_FACTOR * shard_c->data_promised;
132 assert(shard_c->data_promised <= shard_g->data_allocated);
133 old_data_dev = shard_g->data;
134 shard_g->data = offload_mempool_device_malloc(shard_g->data_allocated *
135 sizeof(double));
136 // Omit to wait for copy before freeing old buffer.
137 offloadMemcpyAsyncDtoD(shard_g->data, old_data_dev,
138 shard_g->data_size * sizeof(double),
139 shard_g->stream);
140 }
141 offloadEventRecord(shard_g->event, shard_g->stream);
142
143 // Zero new blocks if necessary.
144 if (shard_c->data_promised > shard_g->data_size) {
145 const int tail = shard_c->data_promised - shard_g->data_size;
146 offloadMemsetAsync(&shard_g->data[shard_g->data_size], 0,
147 tail * sizeof(double), shard_g->stream);
148 shard_g->data_size = shard_c->data_promised;
149 }
150
151 OFFLOAD_CHECK(offloadGetLastError());
152 assert(0 != shard_g->data_size);
153
154 // Launch kernel.
155 dbm_multiply_gpu_launch_kernel(shard_g->stream, alpha, ntasks, batch,
156 batch_dev, ctx->pack_a_dev.data,
157 ctx->pack_b_dev.data, shard_g->data);
158 OFFLOAD_CHECK(offloadGetLastError());
159 }
160
161 if (finish) { // Start downloading the current shard of matrix_c.
162 // Grow host buffer if necessary.
164 // Download results from device.
165 assert(shard_c->data_size == shard_g->data_size);
166 offloadMemcpyAsyncDtoH(shard_c->data, shard_g->data,
167 shard_g->data_size * sizeof(double),
168 shard_g->stream);
169 }
170
171 if (0 < ntasks) {
172 // Wait for:
173 // - Batch to be uploaded (before refilling it).
174 // - Safely freeing device buffer (if resized).
175 offloadEventSynchronize(shard_g->event);
176
177 if (NULL != old_data_dev) {
178 offload_mempool_device_free(old_data_dev);
179 }
180 }
181}
182
183/*******************************************************************************
184 * \brief Internal routine for shutting down the gpu backend.
185 * \author Ole Schuett
186 ******************************************************************************/
187void dbm_multiply_gpu_stop(dbm_multiply_gpu_context_t *ctx) {
188 // Assume GPU device was activated earlier.
189 // Wait for completion, then free gpu ressources.
190#pragma omp parallel for DBM_OMP_SCHEDULE
191 for (int i = 0; i < ctx->nshards; i++) {
192 dbm_shard_gpu_t *const shard_g = &ctx->shards_c_dev[i];
193 offloadStreamSynchronize(shard_g->stream);
194 offloadStreamDestroy(shard_g->stream);
195 offloadEventDestroy(shard_g->event);
196 offload_mempool_device_free(shard_g->data);
197 }
198 free(ctx->shards_c_dev);
199
200 offload_mempool_device_free(ctx->pack_a_dev.data);
201 offload_mempool_device_free(ctx->pack_b_dev.data);
202 offload_mempool_device_free(ctx->batches_dev);
203 offloadStreamDestroy(ctx->main_stream);
204 offloadEventDestroy(ctx->upload_event);
205}
206
207#endif // defined(__OFFLOAD) && !defined(__NO_OFFLOAD_DBM)
208
209// EOF
#define DBM_ALLOCATION_FACTOR
void dbm_shard_allocate_promised_blocks(dbm_shard_t *shard)
Internal routine for allocating and zeroing any promised block's data.
Definition dbm_shard.c:239
static void const int const int i
subroutine, public offload_activate_chosen_device()
Activates the device selected via offload_set_chosen_device()
void offload_mempool_device_free(const void *memory)
Internal routine for releasing memory back to the pool.
void * offload_mempool_device_malloc(const size_t size)
Internal routine for allocating device memory from the pool.
Internal struct for storing a pack - essentially a shard for MPI.
double * data
Internal struct for storing a matrix shard.
Definition dbm_shard.h:30
double * data
Definition dbm_shard.h:42
int data_size
Definition dbm_shard.h:41
int data_promised
Definition dbm_shard.h:39
Internal struct for storing a task, ie. a single block multiplication.