(git:d077e66)
Loading...
Searching...
No Matches
grid_library.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#include "grid_library.h"
8#include "grid_common.h"
9#include "grid_constants.h"
10
13
14#include <assert.h>
15#include <omp.h>
16#include <stddef.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#define GRID_LIBRARY_PRINT(FN, MSG, OUTPUT_UNIT) \
22 ((FN)(MSG, (int)strlen(MSG), OUTPUT_UNIT))
23
24// counter dimensions
25#define GRID_NBACKENDS 5
26#define GRID_NKERNELS 4
27#define GRID_MAX_LP 20
28
29typedef struct {
31 void *scratch; // thread local scratch, grown on demand
35
37static bool library_initialized = false;
38static int max_threads = 0;
40 .backend = GRID_BACKEND_AUTO, .validate = false, .apply_cutoff = false};
41
42#if !defined(_OPENMP)
43#error "OpenMP is required. Please add -fopenmp to your C compiler flags."
44#endif
45
46#if defined(NDEBUG)
47#error \
48 "Please do not build CP2K with NDEBUG. There is no performance advantage and asserts will save your neck."
49#endif
50
51/*******************************************************************************
52 * \brief Initializes the grid library.
53 * \author Ole Schuett
54 ******************************************************************************/
57 printf("Error: Grid library was already initialized.\n");
58 abort();
59 }
60
61#if defined(__OFFLOAD) && !defined(__NO_OFFLOAD_GRID)
62 // Reserve global GPU memory for storing the intermediate Cab matrix blocks.
63 // CUDA does not allow to increase this limit after a kernel was launched.
64 // Unfortunately, the required memory is hard to predict because we neither
65 // know which tasks will be run nor how many thread blocks the available GPU
66 // can execute in parallel... 64 MiB ought to be enough for anybody ;-)
67 offloadEnsureMallocHeapSize(64 * 1024 * 1024);
68#endif
69
70 max_threads = omp_get_max_threads();
72 assert(per_thread_globals != NULL);
73
74// Using parallel regions to ensure memory is allocated near a thread's core.
75#pragma omp parallel default(none) shared(per_thread_globals) \
76 num_threads(max_threads)
77 {
78 const int ithread = omp_get_thread_num();
79 per_thread_globals[ithread] = malloc(sizeof(grid_library_globals));
80 assert(per_thread_globals[ithread] != NULL);
81 memset(per_thread_globals[ithread], 0, sizeof(grid_library_globals));
82 }
83
85}
86
87/*******************************************************************************
88 * \brief Finalizes the grid library.
89 * \author Ole Schuett
90 ******************************************************************************/
93 printf("Error: Grid library is not initialized.\n");
94 abort();
95 }
96
97 for (int i = 0; i < max_threads; i++) {
99 free(per_thread_globals[i]->scratch);
100 free(per_thread_globals[i]);
101 }
102 free(per_thread_globals);
103 per_thread_globals = NULL;
104 library_initialized = false;
105}
106
107/*******************************************************************************
108 * \brief Returns a pointer to the thread local sphere cache.
109 * \author Ole Schuett
110 ******************************************************************************/
112 const int ithread = omp_get_thread_num();
113 assert(ithread < max_threads);
114 return &per_thread_globals[ithread]->sphere_cache;
115}
116
117/*******************************************************************************
118 * \brief Returns a pointer to a thread local scratch buffer with at least the
119 * given size. The buffer grows on demand and is reused across calls.
120 ******************************************************************************/
121void *grid_library_get_scratch(const size_t bytes) {
122 const int ithread = omp_get_thread_num();
123 assert(ithread < max_threads);
124 grid_library_globals *globals = per_thread_globals[ithread];
125 if (globals->scratch_size < bytes) {
126 free(globals->scratch);
127 globals->scratch = malloc(bytes);
128 assert(globals->scratch != NULL);
129 globals->scratch_size = bytes;
130 }
131 return globals->scratch;
132}
133
134/*******************************************************************************
135 * \brief Configures the grid library.
136 * \author Ole Schuett
137 ******************************************************************************/
138void grid_library_set_config(const enum grid_backend backend,
139 const bool validate, const bool apply_cutoff) {
140 config.backend = backend;
141 config.validate = validate;
142 config.apply_cutoff = apply_cutoff;
143}
144
145/*******************************************************************************
146 * \brief Returns the library config.
147 * \author Ole Schuett
148 ******************************************************************************/
150
151/*******************************************************************************
152 * \brief Adds given increment to counter specified by lp, backend, and kernel.
153 * \author Ole Schuett
154 ******************************************************************************/
155void grid_library_counter_add(const int lp, const enum grid_backend backend,
156 const enum grid_library_kernel kernel,
157 const int increment) {
158 assert(lp >= 0);
159 assert(kernel < GRID_NKERNELS);
160 const int back = backend - GRID_BACKEND_REF;
161 assert(back < GRID_NBACKENDS);
162 const int idx = back * GRID_NKERNELS * GRID_MAX_LP + kernel * GRID_MAX_LP +
163 imin(lp, GRID_MAX_LP - 1);
164 const int ithread = omp_get_thread_num();
165 assert(ithread < max_threads);
166 per_thread_globals[ithread]->counters[idx] += increment;
167}
168
169/*******************************************************************************
170 * \brief Comperator passed to qsort to compare two counters.
171 * \author Ole Schuett
172 ******************************************************************************/
173static int compare_counters(const void *a, const void *b) {
174 return *(long *)b - *(long *)a;
175}
176
177/*******************************************************************************
178 * \brief Prints statistics gathered by the grid library.
179 * \author Ole Schuett
180 ******************************************************************************/
181void grid_library_print_stats(const int fortran_comm,
182 void (*print_func)(const char *, int, int),
183 const int output_unit) {
184 if (!library_initialized) {
185 printf("Error: Grid library is not initialized.\n");
186 abort();
187 }
188
189 const cp_mpi_comm_t comm = cp_mpi_comm_f2c(fortran_comm);
190 // Sum all counters across threads and mpi ranks.
191 const int ncounters = GRID_NBACKENDS * GRID_NKERNELS * GRID_MAX_LP;
192 long counters[ncounters][2];
193 memset(counters, 0, ncounters * 2 * sizeof(long));
194 double total = 0.0;
195 for (int i = 0; i < ncounters; i++) {
196 counters[i][1] = i; // needed as inverse index after qsort
197 for (int j = 0; j < max_threads; j++) {
198 counters[i][0] += per_thread_globals[j]->counters[i];
199 }
200 cp_mpi_sum_long(&counters[i][0], 1, comm);
201 total += counters[i][0];
202 }
203
204 // Sort counters.
205 qsort(counters, ncounters, 2 * sizeof(long), &compare_counters);
206
207 // Determine if anything needs to be printed.
208 bool print = false;
209 for (int i = 0; i < ncounters && !print; i++) {
210 if (counters[i][0] != 0) {
211 print = true;
212 }
213 }
214 if (!print) {
215 return; // nothing to be printed
216 }
217
218 // Print counters.
219 GRID_LIBRARY_PRINT(print_func, "\n", output_unit);
222 " ----------------------------------------------------------------"
223 "---------------\n",
224 output_unit);
227 " - "
228 " -\n",
229 output_unit);
232 " - GRID STATISTICS "
233 " -\n",
234 output_unit);
237 " - "
238 " -\n",
239 output_unit);
242 " ----------------------------------------------------------------"
243 "---------------\n",
244 output_unit);
247 " LP KERNEL BACKEND "
248 "COUNT PERCENT\n",
249 output_unit);
250
251 const char *kernel_names[] = {"collocate ortho", "integrate ortho",
252 "collocate general", "integrate general"};
253 const char *backend_names[] = {"REF", "CPU", "DGEMM", "GPU"};
254
255 for (int i = 0; i < ncounters; i++) {
256 if (counters[i][0] == 0)
257 continue; // skip empty counters
258 const double percent = 100.0 * counters[i][0] / total;
259 const int idx = counters[i][1];
260 const int backend_stride = GRID_NKERNELS * GRID_MAX_LP;
261 const int back = idx / backend_stride;
262 const int kern = (idx % backend_stride) / GRID_MAX_LP;
263 const int lp = (idx % backend_stride) % GRID_MAX_LP;
264 char buffer[100];
265 snprintf(buffer, sizeof(buffer), " %-5i %-17s %-6s %34li %10.2f%%\n", lp,
266 kernel_names[kern], backend_names[back], counters[i][0], percent);
267 GRID_LIBRARY_PRINT(print_func, buffer, output_unit);
268 }
269
272 " ----------------------------------------------------------------"
273 "---------------\n",
274 output_unit);
275}
276
277// EOF
void cp_mpi_sum_long(long *values, const int count, const cp_mpi_comm_t comm)
Wrapper around MPI_Allreduce for op MPI_SUM and datatype MPI_LONG.
Definition cp_mpi.c:437
cp_mpi_comm_t cp_mpi_comm_f2c(const int fortran_comm)
Wrapper around MPI_Comm_f2c.
Definition cp_mpi.c:150
int cp_mpi_comm_t
Definition cp_mpi.h:18
static bool library_initialized
Definition dbm_library.c:25
static int max_threads
Definition dbm_library.c:26
static int compare_counters(const void *a, const void *b)
Comperator passed to qsort to compare two counters.
static void print_func(const char *msg, int msglen, int output_unit)
Wrapper for printf, passed to dbm_library_print_stats.
Definition dbm_miniapp.c:26
static int imin(int x, int y)
Returns the smaller of the two integers (missing from the C standard).
Definition dbm_miniapp.c:37
static GRID_HOST_DEVICE int idx(const orbital a)
Return coset index of given orbital angular momentum.
grid_backend
@ GRID_BACKEND_REF
@ GRID_BACKEND_AUTO
static void const int const int i
void apply_cutoff(void *ptr)
void grid_library_finalize(void)
Finalizes the grid library.
#define GRID_MAX_LP
#define GRID_LIBRARY_PRINT(FN, MSG, OUTPUT_UNIT)
static grid_library_config config
void * grid_library_get_scratch(const size_t bytes)
Returns a pointer to a thread local scratch buffer with at least the given size. The buffer grows on ...
grid_sphere_cache * grid_library_get_sphere_cache(void)
Returns a pointer to the thread local sphere cache.
#define GRID_NKERNELS
void grid_library_init(void)
Initializes the grid library.
#define GRID_NBACKENDS
grid_library_config grid_library_get_config(void)
Returns the library config.
void grid_library_counter_add(const int lp, const enum grid_backend backend, const enum grid_library_kernel kernel, const int increment)
Adds given increment to counter specified by lp, backend, and kernel.
void grid_library_set_config(const enum grid_backend backend, const bool validate, const bool apply_cutoff)
Configures the grid library.
void grid_library_print_stats(const int fortran_comm, void(*print_func)(const char *, int, int), const int output_unit)
Prints statistics gathered by the grid library.
static int compare_counters(const void *a, const void *b)
Comperator passed to qsort to compare two counters.
static grid_library_globals ** per_thread_globals
grid_library_kernel
Various kernels provided by the grid library.
void grid_sphere_cache_free(grid_sphere_cache *cache)
Free the memory of the sphere cache.
Configuration of the grid library.
grid_sphere_cache sphere_cache
long counters[GRID_NBACKENDS *GRID_NKERNELS *GRID_MAX_LP]
Struct holding the entire sphere cache, ie. for all grids.