9.2
general documentation
cs_dispatch.h
Go to the documentation of this file.
1#ifndef CS_DISPATCH_H
2#define CS_DISPATCH_H
3
4/*============================================================================
5 * Class to dispatch computation using various runtimes (OpenMP, CUDA, ...)
6 *============================================================================*/
7
8/*
9 This file is part of code_saturne, a general-purpose CFD tool.
10
11 Copyright (C) 1998-2026 EDF S.A.
12
13 This program is free software; you can redistribute it and/or modify it under
14 the terms of the GNU General Public License as published by the Free Software
15 Foundation; either version 2 of the License, or (at your option) any later
16 version.
17
18 This program is distributed in the hope that it will be useful, but WITHOUT
19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
21 details.
22
23 You should have received a copy of the GNU General Public License along with
24 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
25 Street, Fifth Floor, Boston, MA 02110-1301, USA.
26*/
27
28/*----------------------------------------------------------------------------*/
29
30#include "base/cs_defs.h"
31
32/*----------------------------------------------------------------------------
33 * Standard C++ library headers
34 *----------------------------------------------------------------------------*/
35
36#include <utility>
37#include <cmath>
38
39#if defined(SYCL_LANGUAGE_VERSION)
40#include <sycl/sycl.hpp>
41#endif
42
43#if defined(__HIP_DEVICE_COMPILE__)
44#include "hip/hip_runtime.h"
45#endif
46/*----------------------------------------------------------------------------
47 * Local headers
48 *----------------------------------------------------------------------------*/
49
50#include "base/cs_assert.h"
51#include "base/cs_mem.h"
52
53#ifdef __CUDACC__
54#include "base/cs_base_cuda.h"
55#include "base/cs_cuda_reduce.h"
56#include "cs_math_cuda.cuh"
57#endif
58
59#ifdef __HIPCC__
60#include "base/cs_base_hip.h"
61#include "base/cs_hip_reduce.h"
62#include "cs_math_hip.h"
63#endif
64
65/*=============================================================================
66 * Additional doxygen documentation
67 *============================================================================*/
68
74/*=============================================================================
75 * Macro definitions
76 *============================================================================*/
77
78#if defined(SYCL_LANGUAGE_VERSION)
79
80#define CS_DISPATCH_REDUCER_TYPE(type) auto
81
82#else
83
84#define CS_DISPATCH_REDUCER_TYPE(type) type
85
86#endif
87
88/*============================================================================
89 * Type definitions
90 *============================================================================*/
91
95typedef enum {
96
102
109template <class Derived>
111public:
112
113 // Loop over n elements
114 // Must be redefined by the child class
115 template <class F, class... Args>
116 decltype(auto)
117 parallel_for(cs_lnum_t n, F&& f, Args&&... args) = delete;
118
119 // Assembly loop over all internal faces
120 template <class M, class F, class... Args>
121 decltype(auto)
123 F&& f,
124 Args&&... args);
125
126 // Assembly loop over all boundary faces
127 template <class M, class F, class... Args>
128 decltype(auto)
130 F&& f,
131 Args&&... args);
132
133 // Parallel reduction with simple sum.
134 // Must be redefined by the child class
135 template <class T, class F, class... Args>
136 decltype(auto)
138 (cs_lnum_t n, T& sum, F&& f, Args&&... args) = delete;
139
140 // Parallel reduction with reducer template.
141 // Must be redefined by the child class
142 template <class T, class R, class F, class... Args>
143 decltype(auto)
145 (cs_lnum_t n, T& r, R& reducer, F&& f, Args&&... args) = delete;
146
147 // Wait upon completion
148 // Must be redefined by the child class
149 template <class... Args>
150 decltype(auto)
151 wait(void) = delete;
152
153 // Query sum type for assembly loop over all interior faces
154 // Must be redefined by the child class
155 template <class M>
156 bool
159
160 // Query sum type for assembly loop over all boundary faces
161 // Must be redefined by the child class
162 template <class M>
163 bool
166
167};
168
169// Default implementation of parallel_for_i_faces based on parallel_for
170template <class Derived>
171template <class M, class F, class... Args>
173 (const M* m, F&& f, Args&&... args) {
174 return static_cast<Derived*>(this)->parallel_for
175 (m->n_i_faces,
176 static_cast<F&&>(f),
177 static_cast<Args&&>(args)...);
178}
179
180// Default implementation of parallel_for_b_faces based on parallel_for_sum
181template <class Derived>
182template <class M, class F, class... Args>
184 (const M* m, F&& f, Args&&... args) {
185 return static_cast<Derived*>(this)->parallel_for
186 (m->n_b_faces,
187 static_cast<F&&>(f),
188 static_cast<Args&&>(args)...);
189}
190
191// Default implementation of get interior faces sum type
192template <class Derived>
193template <class M>
195 ([[maybe_unused]]const M* m,
198 return true;
199}
200
201// Default implementation of get boundary faces sum type
202template <class Derived>
203template <class M>
205 ([[maybe_unused]]const M* m,
208 return true;
209}
210
211/*
212 * cs_context to execute loops with OpenMP on the CPU
213 */
214
215class cs_host_context : public cs_dispatch_context_mixin<cs_host_context> {
216
217private:
218
219 cs_lnum_t n_min_per_thread;
220 int n_threads_;
222public:
223
225 : n_min_per_thread(CS_THR_MIN), n_threads_(-1)
226 {}
227
229 //
230 // \param[in] n size of array
231 // \param[in] type_size element type size (or multiple)
232 // \param[in, out] s_id start index for the current thread
233 // \param[in, out] e_id past-the-end index for the current thread
234
235private:
236
237#if defined(HAVE_OPENMP)
238
239 // Determine number of threads that should actually be used.
240 int
241 n_threads(cs_lnum_t n)
242 {
243 int n_t = n_threads_;
244 if (n_t < 0) {
245 n_t = cs_glob_n_threads;
246 int n_t_l = n / n_min_per_thread;
247 if (n_t_l < n_t)
248 n_t = n_t_l;
249 if (n_t < 1)
250 n_t = 1;
251 }
252 return n_t;
253 }
254
255#endif
256
257 // Determine element range for current thread.
258 void
259 thread_range(cs_lnum_t n,
260 [[maybe_unused]] size_t type_size,
261 cs_lnum_t &s_id,
262 cs_lnum_t &e_id)
263 {
264#if defined(HAVE_OPENMP)
265 const int t_id = omp_get_thread_num();
266 const int n_t = omp_get_num_threads();
267 const cs_lnum_t t_n = (n + n_t - 1) / n_t;
268 const cs_lnum_t cl_m = CS_CL_SIZE / type_size; /* Cache line multiple */
269
270 s_id = t_id * t_n;
271 e_id = (t_id+1) * t_n;
272 s_id = cs_align(s_id, cl_m);
273 e_id = cs_align(e_id, cl_m);
274 if (e_id > n) e_id = n;
275#else
276 s_id = 0;
277 e_id = n;
278#endif
279 }
280
281public:
282
284 void
286 this->n_min_per_thread = n;
287 }
288
292 return this->n_min_per_thread;
293 }
294
296 void
298 this->n_threads_ = n;
299 }
300
302 int
304 return this->n_threads_;
305 }
306
308 template <class F, class... Args>
309 bool
310 parallel_for(cs_lnum_t n, F&& f, Args&&... args) {
311 #ifdef _OPENMP
312 #pragma omp parallel for num_threads(n_threads(n))
313 #endif
314 for (cs_lnum_t i = 0; i < n; ++i) {
315 f(i, args...);
316 }
317 return true;
318 }
319
322 template <class M, class F, class... Args>
323 bool
324 parallel_for_i_faces(const M* m, F&& f, Args&&... args) {
325 const int n_i_groups = m->i_face_numbering->n_groups;
326 const int n_i_threads = m->i_face_numbering->n_threads;
327 const cs_lnum_t *restrict i_group_index = m->i_face_numbering->group_index;
328 for (int g_id = 0; g_id < n_i_groups; g_id++) {
329 #ifdef _OPENMP
330 #pragma omp parallel for
331 #endif
332 for (int t_id = 0; t_id < n_i_threads; t_id++) {
333 for (cs_lnum_t f_id = i_group_index[(t_id * n_i_groups + g_id) * 2];
334 f_id < i_group_index[(t_id * n_i_groups + g_id) * 2 + 1];
335 f_id++) {
336 f(f_id, args...);
337 }
338 }
339 }
340 return true;
341 }
342
345 template <class M, class F, class... Args>
346 bool
347 parallel_for_b_faces(const M* m, F&& f, Args&&... args) {
348 const int n_b_groups = m->b_face_numbering->n_groups;
349 const int n_b_threads = m->b_face_numbering->n_threads;
350 const cs_lnum_t *restrict b_group_index = m->b_face_numbering->group_index;
351 for (int g_id = 0; g_id < n_b_groups; g_id++) {
352 #ifdef _OPENMP
353 #pragma omp parallel for
354 #endif
355 for (int t_id = 0; t_id < n_b_threads; t_id++) {
356 for (cs_lnum_t f_id = b_group_index[(t_id * n_b_groups + g_id) * 2];
357 f_id < b_group_index[(t_id * n_b_groups + g_id) * 2 + 1];
358 f_id++) {
359 f(f_id, args...);
360 }
361 }
362 }
363 return true;
364 }
365
367 template <class T, class F, class... Args>
368 bool
370 T& sum,
371 F&& f,
372 Args&&... args) {
373 sum = 0;
374
375#if 0
376 #ifdef _OPENMP
377 #pragma omp parallel for reduction(+:sum) num_threads(n_threads(n))
378 #endif
379 for (cs_lnum_t i = 0; i < n; ++i) {
380 f(i, sum, args...);
381 }
382#else
383 #ifdef _OPENMP
384 #pragma omp parallel num_threads(n_threads(n))
385 #endif
386 {
387 cs_lnum_t s_id, e_id;
388 thread_range(n, 4, s_id, e_id);
389
390 const cs_lnum_t _n = e_id - s_id;
391 cs_lnum_t n_sblocks, blocks_in_sblocks;
392 const cs_lnum_t block_size = 60;
393 { // superblock counts
394 cs_lnum_t n_blocks = (_n + block_size - 1) / block_size;
395 n_sblocks = (n_blocks > 1) ? std::sqrt(n_blocks) : 1;
396 cs_lnum_t n_b = block_size * n_sblocks;
397 blocks_in_sblocks = (_n + n_b - 1) / n_b;
398 }
399
400 for (cs_lnum_t sid = 0; sid < n_sblocks; sid++) {
401 T sum_sblock = 0;
402
403 for (cs_lnum_t bid = 0; bid < blocks_in_sblocks; bid++) {
404 cs_lnum_t start_id = block_size * (blocks_in_sblocks*sid + bid) + s_id;
405 cs_lnum_t end_id = start_id + block_size;
406 if (end_id > e_id)
407 end_id = e_id;
408 T sum_block = 0;
409 for (cs_lnum_t i = start_id; i < end_id; i++) {
410 f(i, sum_block, args...);
411 }
412 sum_sblock += sum_block;
413 }
414
415 #ifdef _OPENMP
416 #pragma omp atomic
417 #endif
418 sum += sum_sblock;
419 }
420 }
421
422#endif
423 return true;
424 }
425
427 // In case the reduction involves floating-point sums,
428 // we use a Superblock / block loop to reduce numerical error.
429 template <class T, class R, class F, class... Args>
430 bool
432 T& result,
433 R& reducer,
434 F&& f,
435 Args&&... args) {
436 reducer.identity(result);
437
438 #ifdef _OPENMP
439 #pragma omp parallel num_threads(n_threads(n))
440 #endif
441 {
442 cs_lnum_t s_id, e_id;
443 thread_range(n, 4, s_id, e_id);
444
445 const cs_lnum_t _n = e_id - s_id;
446 cs_lnum_t n_sblocks, blocks_in_sblocks;
447 const cs_lnum_t block_size = 60;
448 { // superblock counts
449 cs_lnum_t n_blocks = (_n + block_size - 1) / block_size;
450 n_sblocks = (n_blocks > 1) ? std::sqrt(n_blocks) : 1;
451 cs_lnum_t n_b = block_size * n_sblocks;
452 blocks_in_sblocks = (_n + n_b - 1) / n_b;
453 }
454
455 for (cs_lnum_t sid = 0; sid < n_sblocks; sid++) {
456 T result_sblock;
457 reducer.identity(result_sblock);
458
459 for (cs_lnum_t bid = 0; bid < blocks_in_sblocks; bid++) {
460 cs_lnum_t start_id = block_size * (blocks_in_sblocks*sid + bid) + s_id;
461 cs_lnum_t end_id = start_id + block_size;
462 if (end_id > e_id)
463 end_id = e_id;
464 T result_block;
465 reducer.identity(result_block);
466 for (cs_lnum_t i = start_id; i < end_id; i++) {
467 f(i, result_block, args...);
468 reducer.combine(result_sblock, result_block);
469 }
470 }
471
472 #ifdef _OPENMP
473 #pragma omp critical
474 #endif
475 {
476 reducer.combine(result, result_sblock);
477 }
478 }
479 }
480
481 return true;
482 }
483
485 // No-op here as Open-MP based methods used here have implicit barriers.
486 template <class... Args>
487 bool
488 wait(void) {
489 return true;
490 }
491
492 // Get interior faces sum type associated with this context
493 template <class M>
494 bool
495 try_get_parallel_for_i_faces_sum_type([[maybe_unused]]const M* m,
498 return true;
499 }
500
501 // Get boundary faces sum type associated with this context
502 template <class M>
503 bool
504 try_get_parallel_for_b_faces_sum_type([[maybe_unused]]const M* m,
507 return true;
508 }
509
510};
511
512#if defined(__CUDACC__)
513
514/* Default kernel that loops over an integer range and calls a device functor.
515 This kernel uses a grid_size-stride loop and thus guarantees that all
516 integers are processed, even if the grid is smaller.
517 All arguments *must* be passed by value to avoid passing CPU references
518 to the GPU. */
519
520template <class F, class... Args>
521__global__ void cs_cuda_kernel_parallel_for(cs_lnum_t n, F f, Args... args) {
522 // grid_size-stride loop
523 for (cs_lnum_t id = blockIdx.x * blockDim.x + threadIdx.x; id < n;
524 id += blockDim.x * gridDim.x) {
525 f(id, args...);
526 }
527}
528
529/* Default kernel that loops over an integer range and calls a device functor,
530 also reducing a sum over all elements.
531 This kernel uses a grid_size-stride loop and thus guarantees that all
532 integers are processed, even if the grid is smaller.
533 All arguments *must* be passed by value to avoid passing CPU references
534 to the GPU. */
535
536template <class T, class F, class... Args>
537__global__ void
538cs_cuda_kernel_parallel_for_reduce_sum(cs_lnum_t n,
539 T *b_res,
540 F f,
541 Args... args) {
542 // grid_size-stride loop
543 extern __shared__ int p_stmp[];
544 T *stmp = reinterpret_cast<T *>(p_stmp);
545 const cs_lnum_t tid = threadIdx.x;
546
547 stmp[tid] = 0;
548
549 for (cs_lnum_t id = blockIdx.x * blockDim.x + threadIdx.x; id < n;
550 id += blockDim.x * gridDim.x) {
551 f(id, stmp[tid], args...);
552 }
553
554 switch (blockDim.x) {
555 case 1024:
556 cs_cuda_reduce_block_reduce_sum<1024, 1>(stmp, tid, b_res);
557 break;
558 case 512:
559 cs_cuda_reduce_block_reduce_sum<512, 1>(stmp, tid, b_res);
560 break;
561 case 256:
562 cs_cuda_reduce_block_reduce_sum<256, 1>(stmp, tid, b_res);
563 break;
564 case 128:
565 cs_cuda_reduce_block_reduce_sum<128, 1>(stmp, tid, b_res);
566 break;
567 default:
568 assert(0);
569 }
570}
571
572/* Default kernel that loops over an integer range and calls a device functor.
573 also computing a reduction over all elements.
574 This kernel uses a grid_size-stride loop and thus guarantees that all
575 integers are processed, even if the grid is smaller.
576 All arguments *must* be passed by value to avoid passing CPU references
577 to the GPU. */
578
579template <class T, class R, class F, class... Args>
580__global__ void
581cs_cuda_kernel_parallel_for_reduce(cs_lnum_t n,
582 T *b_res,
583 R &reducer,
584 F f,
585 Args... args) {
586 // grid_size-stride loop
587 extern __shared__ int p_stmp[];
588 T *stmp = reinterpret_cast<T *>(p_stmp);
589 const cs_lnum_t tid = threadIdx.x;
590
591 reducer.identity(stmp[tid]);
592
593 for (cs_lnum_t id = blockIdx.x * blockDim.x + threadIdx.x; id < n;
594 id += blockDim.x * gridDim.x) {
595 T rd;
596 /* It would be safer to call reducer.identyity() here in case all
597 values of rd are not set for each thread, but this might incurr
598 a small performance penalty, and is redundant in most cases,
599 so we consider all values of rd must be set by the caller. */
600 // reducer.identity(rd);
601 f(id, rd, args...);
602 stmp[tid] = rd;
603 }
604
605 switch (blockDim.x) {
606 case 1024:
607 cs_cuda_reduce_block_reduce<1024, R>(stmp, tid, b_res);
608 break;
609 case 512:
610 cs_cuda_reduce_block_reduce<512, R>(stmp, tid, b_res);
611 break;
612 case 256:
613 cs_cuda_reduce_block_reduce<256, R>(stmp, tid, b_res);
614 break;
615 case 128:
616 cs_cuda_reduce_block_reduce<128, R>(stmp, tid, b_res);
617 break;
618 default:
619 assert(0);
620 }
621}
622
627class cs_device_context : public cs_dispatch_context_mixin<cs_device_context> {
628
629private:
630
631 long grid_size_;
634 long block_size_;
635 cudaStream_t stream_;
636 int device_;
638 bool use_gpu_;
640public:
641
643
644 cs_device_context(void)
645 : grid_size_(0), block_size_(256), stream_(cs_cuda_get_stream(0)),
646 device_(0), use_gpu_(true)
647 {
648 device_ = cs_glob_cuda_device_id;
649 }
650
651 cs_device_context(long grid_size,
652 long block_size,
653 cudaStream_t stream,
654 int device)
655 : grid_size_(grid_size), block_size_(block_size), stream_(stream),
656 device_(device), use_gpu_(true)
657 {}
658
659 cs_device_context(long grid_size,
660 long block_size,
661 cudaStream_t stream)
662 : grid_size_(grid_size), block_size_(block_size), stream_(stream),
663 device_(0), use_gpu_(true)
664 {
665 device_ = cs_base_cuda_get_device();
666 }
667
668 cs_device_context(long grid_size,
669 long block_size)
670 : grid_size_(grid_size), block_size_(block_size),
671 stream_(cs_cuda_get_stream(0)), device_(0), use_gpu_(true)
672 {
673 device_ = cs_base_cuda_get_device();
674 }
675
676 cs_device_context(cudaStream_t stream)
677 : grid_size_(0), block_size_(256), stream_(stream), device_(0),
678 use_gpu_(true)
679 {
680 device_ = cs_base_cuda_get_device();
681 }
682
683#if 0 // Operators adding in process of cs_dispatch_queue addition,
684 // then marked as useless and removed.
685 // Not totally removed for now, but "quarantined", to be removed
686 // once we are sure they are not missed.
687
689
690 cs_device_context(cs_device_context const &) = default;
691
692 cs_device_context(cs_device_context &&) = default;
693
694 cs_device_context &
695 operator = (cs_device_context const &) = default;
696
697 cs_device_context &
698 operator = (cs_device_context &&) = default;
699
700#endif
701
703 //
704 // \param[in] grid_size CUDA grid size, or -1 for automatic choice
705 // \param[in] block_size CUDA block size (power of 2 if reduction is used)
706
707 void
708 set_cuda_grid(long grid_size,
709 long block_size) {
710 this->grid_size_ = (grid_size > 0) ? grid_size : -1;
711 this->block_size_ = block_size;
712 }
713
715
716 void
717 set_stream(cudaStream_t stream) {
718 this->stream_ = stream;
719 }
720
722
723 void
724 set_stream(int stream_id) {
725 this->stream_ = cs_cuda_get_stream(stream_id);
726 }
727
729
730 cudaStream_t
731 stream(void) {
732 return this->stream_;
733 }
734
736
737 void
738 set_cuda_device(int device) {
739 this->device_ = device;
740 }
741
743
744 void
745 set_use_gpu(bool use_gpu) {
746 this->use_gpu_ = use_gpu;
747 }
748
750
751 bool
752 use_gpu(void) {
753 return (device_ >= 0 && use_gpu_);
754 }
755
757
759 alloc_mode(void) {
760 cs_alloc_mode_t amode
761 = (device_ >= 0 && use_gpu_) ? cs_alloc_mode_device : CS_ALLOC_HOST;
762 return (amode);
763 }
764
766 alloc_mode(bool readable_on_cpu) {
768 if (device_ >= 0 && use_gpu_) {
769 if (readable_on_cpu)
771 else
772 amode = cs_alloc_mode_device;
773 }
774 return (amode);
775 }
776
777public:
778
780 template <class F, class... Args>
781 bool
782 parallel_for(cs_lnum_t n, F&& f, Args&&... args) {
783 if (device_ < 0 || use_gpu_ == false) {
784 return false;
785 }
786
787 long l_grid_size = grid_size_;
788 if (l_grid_size < 1) {
789 l_grid_size = (n % block_size_) ? n/block_size_ + 1 : n/block_size_;
790 }
791
792 if (n > 0) {
793 cs_cuda_kernel_parallel_for<<<l_grid_size, block_size_, 0, stream_>>>
794 (n, static_cast<F&&>(f), static_cast<Args&&>(args)...);
795
796#if defined(DEBUG) || !defined(NDEBUG)
797 cudaError_t retcode = cudaGetLastError();
798 if (retcode != cudaSuccess)
799 bft_error(__FILE__, __LINE__, 0,
800 "[CUDA error] %d: %s\n"
801 "with grid size %ld, block size %ld.",
802 retcode, ::cudaGetErrorString(retcode),
803 l_grid_size, block_size_);
804#endif
805 }
806 return true;
807 }
808
810 template <class M, class F, class... Args>
811 bool
812 parallel_for_i_faces(const M* m, F&& f, Args&&... args) {
813 const cs_lnum_t n = m->n_i_faces;
814 if (device_ < 0 || use_gpu_ == false) {
815 return false;
816 }
817
818 long l_grid_size = grid_size_;
819 if (l_grid_size < 1) {
820 l_grid_size = (n % block_size_) ? n/block_size_ + 1 : n/block_size_;
821 }
822
823 if (n > 0)
824 cs_cuda_kernel_parallel_for<<<l_grid_size, block_size_, 0, stream_>>>
825 (n, static_cast<F&&>(f), static_cast<Args&&>(args)...);
826
827 return true;
828 }
829
832 template <class T, class F, class... Args>
833 bool
835 T& sum,
836 F&& f,
837 Args&&... args) {
838 if (device_ < 0 || use_gpu_ == false) {
839 return false;
840 }
841
842 sum = 0;
843
844 long l_grid_size = grid_size_;
845 if (l_grid_size < 1) {
846 l_grid_size = (n % block_size_) ? n/block_size_ + 1 : n/block_size_;
847 }
848 if (n == 0) {
849 return true;
850 }
851
852 int stream_id = cs_cuda_get_stream_id(stream_);
853 if (stream_id < 0)
854 stream_id = 0;
855
856 T *r_grid_, *r_reduce_, *r_host_;
857 cs_cuda_get_2_stage_reduce_buffers
858 (stream_id, n, sizeof(sum), l_grid_size,
859 (void *&)r_grid_, (void *&)r_reduce_, (void *&)r_host_);
860
861 int smem_size = block_size_ * sizeof(T);
862 cs_cuda_kernel_parallel_for_reduce_sum
863 <<<l_grid_size, block_size_, smem_size, stream_>>>
864 (n, r_grid_, static_cast<F&&>(f), static_cast<Args&&>(args)...);
865
866#if defined(DEBUG) || !defined(NDEBUG)
867 cudaError_t retcode = cudaGetLastError();
868 if (retcode != cudaSuccess)
869 bft_error(__FILE__, __LINE__, 0,
870 "[CUDA error] %d: %s\n"
871 "with grid size %ld, block size %ld, shared memory size %d.",
872 retcode, ::cudaGetErrorString(retcode),
873 l_grid_size, block_size_, smem_size);
874#endif
875
876 switch (block_size_) {
877 case 1024:
878 cs_cuda_reduce_sum_single_block<1024, 1>
879 <<<1, block_size_, 0, stream_>>>
880 (l_grid_size, r_grid_, r_reduce_);
881 break;
882 case 512:
883 cs_cuda_reduce_sum_single_block<512, 1>
884 <<<1, block_size_, 0, stream_>>>
885 (l_grid_size, r_grid_, r_reduce_);
886 break;
887 case 256:
888 cs_cuda_reduce_sum_single_block<256, 1>
889 <<<1, block_size_, 0, stream_>>>
890 (l_grid_size, r_grid_, r_reduce_);
891 break;
892 case 128:
893 cs_cuda_reduce_sum_single_block<128, 1>
894 <<<1, block_size_, 0, stream_>>>
895 (l_grid_size, r_grid_, r_reduce_);
896 break;
897 default:
898 cs_assert(0);
899 }
900
901 CS_CUDA_CHECK(cudaMemcpyAsync(r_host_, r_reduce_, sizeof(sum),
902 cudaMemcpyDeviceToHost, stream_));
903
904#if defined(DEBUG) || !defined(NDEBUG)
905 retcode = cudaGetLastError();
906 if (retcode != cudaSuccess)
907 bft_error(__FILE__, __LINE__, 0,
908 "[CUDA error] %d: %s\n"
909 "with grid size %ld, block size %ld, shared memory size %d.",
910 retcode, ::cudaGetErrorString(retcode),
911 l_grid_size, block_size_, (int)smem_size);
912#endif
913
914 CS_CUDA_CHECK(cudaStreamSynchronize(stream_));
915 CS_CUDA_CHECK(cudaGetLastError());
916 sum = r_host_[0];
917
918 return true;
919 }
920
922 template <class T, class R, class F, class... Args>
923 bool
925 T& result,
926 R& reducer,
927 F&& f,
928 Args&&... args) {
929 if (device_ < 0 || use_gpu_ == false) {
930 return false;
931 }
932
933 reducer.identity(result);
934
935 long l_grid_size = grid_size_;
936 if (l_grid_size < 1) {
937 l_grid_size = (n % block_size_) ? n/block_size_ + 1 : n/block_size_;
938 }
939 if (n == 0) {
940 return true;
941 }
942
943 int stream_id = cs_cuda_get_stream_id(stream_);
944 if (stream_id < 0)
945 stream_id = 0;
946
947 T *r_grid_, *r_reduce_, *r_host_;
948 cs_cuda_get_2_stage_reduce_buffers
949 (stream_id, n, sizeof(result), l_grid_size,
950 (void *&)r_grid_, (void *&)r_reduce_, (void *&)r_host_);
951
952 int l_block_size = block_size_;
953 int smem_size = l_block_size * sizeof(T);
954 while (smem_size > cs_glob_cuda_shared_mem_per_block) {
955 // We should have a runtime failure if even blocks of size 64
956 // are too large relative to the available shared memory.
957 if (l_block_size < 2)
958 bft_error(__FILE__, __LINE__, 0,
959 "Type of size %d exceeds capacity of "
960 "CUDA shared memory (%d).",
961 (int)sizeof(T), cs_glob_cuda_shared_mem_per_block);
962 l_block_size /= 2;
963 smem_size = l_block_size * sizeof(T);
964 }
965
966#if defined(DEBUG) || !defined(NDEBUG)
967 cudaError_t retcode = cudaSuccess;
968#endif
969
970 cs_cuda_kernel_parallel_for_reduce<T, R>
971 <<<l_grid_size, l_block_size, smem_size, stream_>>>
972 (n, r_grid_, reducer, static_cast<F&&>(f),
973 static_cast<Args&&>(args)...);
974
975#if defined(DEBUG) || !defined(NDEBUG)
976 retcode = cudaGetLastError();
977 if (retcode != cudaSuccess)
978 bft_error(__FILE__, __LINE__, 0,
979 "[CUDA error] %d: %s\n"
980 "with grid size %ld, block size %d, shared memory size %d.",
981 retcode, ::cudaGetErrorString(retcode),
982 l_grid_size, l_block_size, smem_size);
983#endif
984
985 switch (l_block_size) {
986 case 1024:
987 cs_cuda_reduce_single_block<1024, R>
988 <<<1, l_block_size, smem_size, stream_>>>
989 (l_grid_size, r_grid_, r_reduce_);
990 break;
991 case 512:
992 cs_cuda_reduce_single_block<512, R>
993 <<<1, l_block_size, smem_size, stream_>>>
994 (l_grid_size, r_grid_, r_reduce_);
995 break;
996 case 256:
997 cs_cuda_reduce_single_block<256, R>
998 <<<1, l_block_size, smem_size, stream_>>>
999 (l_grid_size, r_grid_, r_reduce_);
1000 break;
1001 case 128:
1002 cs_cuda_reduce_single_block<128, R>
1003 <<<1, l_block_size, smem_size, stream_>>>
1004 (l_grid_size, r_grid_, r_reduce_);
1005 break;
1006 default:
1007 cs_assert(0);
1008 }
1009
1010#if defined(DEBUG) || !defined(NDEBUG)
1011 retcode = cudaGetLastError();
1012 if (retcode != cudaSuccess)
1013 bft_error(__FILE__, __LINE__, 0,
1014 "[CUDA error] %d: %s\n"
1015 "with grid size %ld, block size %d, shared memory size %d.",
1016 retcode, ::cudaGetErrorString(retcode),
1017 l_grid_size, l_block_size, (int)smem_size);
1018#endif
1019
1020 CS_CUDA_CHECK(cudaMemcpyAsync(r_host_, r_reduce_, sizeof(result),
1021 cudaMemcpyDeviceToHost, stream_));
1022
1023 CS_CUDA_CHECK(cudaStreamSynchronize(stream_));
1024 CS_CUDA_CHECK(cudaGetLastError());
1025 result = r_host_[0];
1026
1027 return true;
1028 }
1029
1031 template <class... Args>
1032 bool
1033 wait(void) {
1034 if (device_ > -1 && use_gpu_) {
1035 CS_CUDA_CHECK(cudaStreamSynchronize(stream_));
1036 CS_CUDA_CHECK(cudaGetLastError());
1037 return true;
1038 }
1039 return false;
1040 }
1041
1042 // Get interior faces sum type associated with this context
1043 template <class M>
1044 bool
1047 if (device_ < 0 || use_gpu_ == false) {
1048 return false;
1049 }
1050
1052 return true;
1053 }
1054
1055 // Get boundary faces sum type associated with this context
1056 template <class M>
1057 bool
1060 if (device_ < 0 || use_gpu_ == false) {
1061 return false;
1062 }
1063
1065 return true;
1066 }
1067
1068};
1069
1070#elif defined(__HIPCC__)
1071
1072/* Default kernel that loops over an integer range and calls a device functor.
1073 This kernel uses a grid_size-stride loop and thus guarantees that all
1074 integers are processed, even if the grid is smaller.
1075 All arguments *must* be passed by value to avoid passing CPU references
1076 to the GPU. */
1077
1078template <class F, class... Args>
1079__global__ void cs_hip_kernel_parallel_for(cs_lnum_t n, F f, Args... args) {
1080 // grid_size-stride loop
1081 for (cs_lnum_t id = blockIdx.x * blockDim.x + threadIdx.x; id < n;
1082 id += blockDim.x * gridDim.x) {
1083 f(id, args...);
1084 }
1085}
1086
1087/* Default kernel that loops over an integer range and calls a device functor,
1088 also reducing a sum over all elements.
1089 This kernel uses a grid_size-stride loop and thus guarantees that all
1090 integers are processed, even if the grid is smaller.
1091 All arguments *must* be passed by value to avoid passing CPU references
1092 to the GPU. */
1093
1094template <class T, class F, class... Args>
1095__global__ void
1096cs_hip_kernel_parallel_for_reduce_sum(cs_lnum_t n,
1097 T *b_res,
1098 F f,
1099 Args... args) {
1100 // grid_size-stride loop
1101 extern __shared__ int p_stmp[];
1102 T *stmp = reinterpret_cast<T *>(p_stmp);
1103 const cs_lnum_t tid = threadIdx.x;
1104
1105 stmp[tid] = 0;
1106
1107 for (cs_lnum_t id = blockIdx.x * blockDim.x + threadIdx.x; id < n;
1108 id += blockDim.x * gridDim.x) {
1109 f(id, stmp[tid], args...);
1110 }
1111
1112 switch (blockDim.x) {
1113 case 1024:
1114 cs_hip_reduce_block_reduce_sum<1024, 1>(stmp, tid, b_res);
1115 break;
1116 case 512:
1117 cs_hip_reduce_block_reduce_sum<512, 1>(stmp, tid, b_res);
1118 break;
1119 case 256:
1120 cs_hip_reduce_block_reduce_sum<256, 1>(stmp, tid, b_res);
1121 break;
1122 case 128:
1123 cs_hip_reduce_block_reduce_sum<128, 1>(stmp, tid, b_res);
1124 break;
1125 default:
1126 assert(0);
1127 }
1128}
1129
1130/* Default kernel that loops over an integer range and calls a device functor.
1131 also computing a reduction over all elements.
1132 This kernel uses a grid_size-stride loop and thus guarantees that all
1133 integers are processed, even if the grid is smaller.
1134 All arguments *must* be passed by value to avoid passing CPU references
1135 to the GPU. */
1136
1137template <class T, class R, class F, class... Args>
1138__global__ void
1139cs_hip_kernel_parallel_for_reduce(cs_lnum_t n,
1140 T *b_res,
1141 R &reducer,
1142 F f,
1143 Args... args) {
1144 // grid_size-stride loop
1145 extern __shared__ int p_stmp[];
1146 T *stmp = reinterpret_cast<T *>(p_stmp);
1147 const cs_lnum_t tid = threadIdx.x;
1148
1149 reducer.identity(stmp[tid]);
1150
1151 for (cs_lnum_t id = blockIdx.x * blockDim.x + threadIdx.x; id < n;
1152 id += blockDim.x * gridDim.x) {
1153 T rd;
1154 /* It would be safer to call reducer.identyity() here in case all
1155 values of rd are not set for each thread, but this might incurr
1156 a small performance penalty, and is redundant in most cases,
1157 so we consider all values of rd must be set by the caller. */
1158 // reducer.identity(rd);
1159 f(id, rd, args...);
1160 stmp[tid] = rd;
1161 }
1162
1163 switch (blockDim.x) {
1164 case 1024:
1165 cs_hip_reduce_block_reduce<1024, R>(stmp, tid, b_res);
1166 break;
1167 case 512:
1168 cs_hip_reduce_block_reduce<512, R>(stmp, tid, b_res);
1169 break;
1170 case 256:
1171 cs_hip_reduce_block_reduce<256, R>(stmp, tid, b_res);
1172 break;
1173 case 128:
1174 cs_hip_reduce_block_reduce<128, R>(stmp, tid, b_res);
1175 break;
1176 default:
1177 assert(0);
1178 }
1179}
1180
1185class cs_device_context : public cs_dispatch_context_mixin<cs_device_context> {
1186
1187private:
1188
1189 long grid_size_;
1192 long block_size_;
1193 hipStream_t stream_;
1194 int device_;
1196 bool use_gpu_;
1198public:
1199
1201
1202 cs_device_context(void)
1203 : grid_size_(0), block_size_(256), stream_(cs_hip_get_stream(0)),
1204 device_(0), use_gpu_(true)
1205 {
1206 device_ = cs_glob_hip_device_id;
1207 }
1208
1209 cs_device_context(long grid_size,
1210 long block_size,
1211 hipStream_t stream,
1212 int device)
1213 : grid_size_(grid_size), block_size_(block_size), stream_(stream),
1214 device_(device), use_gpu_(true)
1215 {}
1216
1217 cs_device_context(long grid_size,
1218 long block_size,
1219 hipStream_t stream)
1220 : grid_size_(grid_size), block_size_(block_size), stream_(stream),
1221 device_(0), use_gpu_(true)
1222 {
1223 device_ = cs_base_hip_get_device();
1224 }
1225
1226 cs_device_context(long grid_size,
1227 long block_size)
1228 : grid_size_(grid_size), block_size_(block_size),
1229 stream_(cs_hip_get_stream(0)), device_(0), use_gpu_(true)
1230 {
1231 device_ = cs_base_hip_get_device();
1232 }
1233
1234 cs_device_context(hipStream_t stream)
1235 : grid_size_(0), block_size_(256), stream_(stream), device_(0),
1236 use_gpu_(true)
1237 {
1238 device_ = cs_base_hip_get_device();
1239 }
1240
1241#if 0 // Operators adding in process of cs_dispatch_queue addition,
1242 // then marked as useless and removed.
1243 // Not totally removed for now, but "quarantined", to be removed
1244 // once we are sure they are not missed.
1245
1247
1248 cs_device_context(cs_device_context const &) = default;
1249
1250 cs_device_context(cs_device_context &&) = default;
1251
1252 cs_device_context &
1253 operator = (cs_device_context const &) = default;
1254
1255 cs_device_context &
1256 operator = (cs_device_context &&) = default;
1257
1258#endif
1259
1261 //
1262 // \param[in] grid_size HIP grid size, or -1 for automatic choice
1263 // \param[in] block_size HIP block size (power of 2 if reduction is used)
1264
1265 void
1266 set_hip_grid(long grid_size,
1267 long block_size) {
1268 this->grid_size_ = (grid_size > 0) ? grid_size : -1;
1269 this->block_size_ = block_size;
1270 }
1271
1273
1274 void
1275 set_stream(hipStream_t stream) {
1276 this->stream_ = stream;
1277 }
1278
1280
1281 void
1282 set_stream(int stream_id) {
1283 this->stream_ = cs_hip_get_stream(stream_id);
1284 }
1285
1287
1289
1290 void
1291 set_hip_device(int device) {
1292 this->device_ = device;
1293 }
1294
1296
1297 void
1298 set_use_gpu(bool use_gpu) {
1299 this->use_gpu_ = use_gpu;
1300 }
1301
1303
1304 bool
1305 use_gpu(void) {
1306 return (device_ >= 0 && use_gpu_);
1307 }
1308
1310
1312 alloc_mode(void) {
1313 cs_alloc_mode_t amode
1314 = (device_ >= 0 && use_gpu_) ? cs_alloc_mode_device : CS_ALLOC_HOST;
1315 return (amode);
1316 }
1317
1319 alloc_mode(bool readable_on_cpu) {
1321 if (device_ >= 0 && use_gpu_) {
1322 if (readable_on_cpu)
1324 else
1325 amode = cs_alloc_mode_device;
1326 }
1327 return (amode);
1328 }
1329
1330public:
1331
1333 template <class F, class... Args>
1334 bool
1335 parallel_for(cs_lnum_t n, F&& f, Args&&... args) {
1336 if (device_ < 0 || use_gpu_ == false) {
1337 return false;
1338 }
1339
1340 long l_grid_size = grid_size_;
1341 if (l_grid_size < 1) {
1342 l_grid_size = (n % block_size_) ? n/block_size_ + 1 : n/block_size_;
1343 }
1344
1345 if (n > 0)
1346 cs_hip_kernel_parallel_for<<<l_grid_size, block_size_, 0, stream_>>>
1347 (n, static_cast<F&&>(f), static_cast<Args&&>(args)...);
1348
1349 return true;
1350 }
1351
1353 template <class M, class F, class... Args>
1354 bool
1355 parallel_for_i_faces(const M* m, F&& f, Args&&... args) {
1356 const cs_lnum_t n = m->n_i_faces;
1357 if (device_ < 0 || use_gpu_ == false) {
1358 return false;
1359 }
1360
1361 long l_grid_size = grid_size_;
1362 if (l_grid_size < 1) {
1363 l_grid_size = (n % block_size_) ? n/block_size_ + 1 : n/block_size_;
1364 }
1365
1366 if (n > 0)
1367 cs_hip_kernel_parallel_for<<<l_grid_size, block_size_, 0, stream_>>>
1368 (n, static_cast<F&&>(f), static_cast<Args&&>(args)...);
1369
1370 return true;
1371 }
1372
1375 template <class T, class F, class... Args>
1376 bool
1378 T& sum,
1379 F&& f,
1380 Args&&... args) {
1381 if (device_ < 0 || use_gpu_ == false) {
1382 return false;
1383 }
1384
1385 sum = 0;
1386
1387 long l_grid_size = grid_size_;
1388 if (l_grid_size < 1) {
1389 l_grid_size = (n % block_size_) ? n/block_size_ + 1 : n/block_size_;
1390 }
1391 if (n == 0) {
1392 return true;
1393 }
1394
1395 int stream_id = cs_hip_get_stream_id(stream_);
1396 if (stream_id < 0)
1397 stream_id = 0;
1398
1399 T *r_grid_, *r_reduce_, *r_host_;
1400 cs_hip_get_2_stage_reduce_buffers
1401 (stream_id, n, sizeof(sum), l_grid_size,
1402 (void *&)r_grid_, (void *&)r_reduce_, (void *&)r_host_);
1403
1404 int smem_size = block_size_ * sizeof(T);
1405 cs_hip_kernel_parallel_for_reduce_sum
1406 <<<l_grid_size, block_size_, smem_size, stream_>>>
1407 (n, r_grid_, static_cast<F&&>(f), static_cast<Args&&>(args)...);
1408
1409#if defined(DEBUG) || !defined(NDEBUG)
1410 hipError_t retcode = hipGetLastError();
1411 if (retcode != hipSuccess)
1412 bft_error(__FILE__, __LINE__, 0,
1413 "[HIP error] %d: %s\n"
1414 "with grid size %ld, block size %ld, shared memory size %d.",
1415 retcode, ::hipGetErrorString(retcode),
1416 l_grid_size, block_size_, smem_size);
1417#endif
1418
1419 switch (block_size_) {
1420 case 1024:
1421 cs_hip_reduce_sum_single_block<1024, 1>
1422 <<<1, block_size_, 0, stream_>>>
1423 (l_grid_size, r_grid_, r_reduce_);
1424 break;
1425 case 512:
1426 cs_hip_reduce_sum_single_block<512, 1>
1427 <<<1, block_size_, 0, stream_>>>
1428 (l_grid_size, r_grid_, r_reduce_);
1429 break;
1430 case 256:
1431 cs_hip_reduce_sum_single_block<256, 1>
1432 <<<1, block_size_, 0, stream_>>>
1433 (l_grid_size, r_grid_, r_reduce_);
1434 break;
1435 case 128:
1436 cs_hip_reduce_sum_single_block<128, 1>
1437 <<<1, block_size_, 0, stream_>>>
1438 (l_grid_size, r_grid_, r_reduce_);
1439 break;
1440 default:
1441 cs_assert(0);
1442 }
1443
1444 CS_HIP_CHECK(hipMemcpyAsync(r_host_, r_reduce_, sizeof(sum),
1445 hipMemcpyDeviceToHost, stream_));
1446
1447#if defined(DEBUG) || !defined(NDEBUG)
1448 retcode = hipGetLastError();
1449 if (retcode != hipSuccess)
1450 bft_error(__FILE__, __LINE__, 0,
1451 "[HIP error] %d: %s\n"
1452 "with grid size %ld, block size %ld, shared memory size %d.",
1453 retcode, ::hipGetErrorString(retcode),
1454 l_grid_size, block_size_, (int)smem_size);
1455#endif
1456
1457 CS_HIP_CHECK(hipStreamSynchronize(stream_));
1458 CS_HIP_CHECK(hipGetLastError());
1459 sum = r_host_[0];
1460
1461 return true;
1462 }
1463
1465 template <class T, class R, class F, class... Args>
1466 bool
1468 T& result,
1469 R& reducer,
1470 F&& f,
1471 Args&&... args) {
1472 if (device_ < 0 || use_gpu_ == false) {
1473 return false;
1474 }
1475
1476 reducer.identity(result);
1477
1478 long l_grid_size = grid_size_;
1479 if (l_grid_size < 1) {
1480 l_grid_size = (n % block_size_) ? n/block_size_ + 1 : n/block_size_;
1481 }
1482 if (n == 0) {
1483 return true;
1484 }
1485
1486 int stream_id = cs_hip_get_stream_id(stream_);
1487 if (stream_id < 0)
1488 stream_id = 0;
1489
1490 T *r_grid_, *r_reduce_, *r_host_;
1491 cs_hip_get_2_stage_reduce_buffers
1492 (stream_id, n, sizeof(result), l_grid_size,
1493 (void *&)r_grid_, (void *&)r_reduce_, (void *&)r_host_);
1494
1495 int l_block_size = block_size_;
1496 int smem_size = l_block_size * sizeof(T);
1497 while (smem_size > cs_glob_hip_shared_mem_per_block) {
1498 // We should have a runtime failure if even blocks of size 64
1499 // are too large relative to the available shared memory.
1500 if (l_block_size < 2)
1501 bft_error(__FILE__, __LINE__, 0,
1502 "Type of size %d exceeds capacity of "
1503 "HIP shared memory (%d).",
1504 (int)sizeof(T), cs_glob_hip_shared_mem_per_block);
1505 l_block_size /= 2;
1506 smem_size = l_block_size * sizeof(T);
1507 }
1508
1509#if defined(DEBUG) || !defined(NDEBUG)
1510 hipError_t retcode = hipSuccess;
1511#endif
1512
1513 cs_hip_kernel_parallel_for_reduce<T, R>
1514 <<<l_grid_size, l_block_size, smem_size, stream_>>>
1515 (n, r_grid_, reducer, static_cast<F&&>(f),
1516 static_cast<Args&&>(args)...);
1517
1518#if defined(DEBUG) || !defined(NDEBUG)
1519 retcode = hipGetLastError();
1520 if (retcode != hipSuccess)
1521 bft_error(__FILE__, __LINE__, 0,
1522 "[HIP error] %d: %s\n"
1523 "with grid size %ld, block size %d, shared memory size %d.",
1524 retcode, ::hipGetErrorString(retcode),
1525 l_grid_size, l_block_size, smem_size);
1526#endif
1527
1528 switch (l_block_size) {
1529 case 1024:
1530 cs_hip_reduce_single_block<1024, R>
1531 <<<1, l_block_size, smem_size, stream_>>>
1532 (l_grid_size, r_grid_, r_reduce_);
1533 break;
1534 case 512:
1535 cs_hip_reduce_single_block<512, R>
1536 <<<1, l_block_size, smem_size, stream_>>>
1537 (l_grid_size, r_grid_, r_reduce_);
1538 break;
1539 case 256:
1540 cs_hip_reduce_single_block<256, R>
1541 <<<1, l_block_size, smem_size, stream_>>>
1542 (l_grid_size, r_grid_, r_reduce_);
1543 break;
1544 case 128:
1545 cs_hip_reduce_single_block<128, R>
1546 <<<1, l_block_size, smem_size, stream_>>>
1547 (l_grid_size, r_grid_, r_reduce_);
1548 break;
1549 default:
1550 cs_assert(0);
1551 }
1552
1553#if defined(DEBUG) || !defined(NDEBUG)
1554 retcode = hipGetLastError();
1555 if (retcode != hipSuccess)
1556 bft_error(__FILE__, __LINE__, 0,
1557 "[HIP error] %d: %s\n"
1558 "with grid size %ld, block size %d, shared memory size %d.",
1559 retcode, ::hipGetErrorString(retcode),
1560 l_grid_size, l_block_size, (int)smem_size);
1561#endif
1562
1563 CS_HIP_CHECK(hipMemcpyAsync(r_host_, r_reduce_, sizeof(result),
1564 hipMemcpyDeviceToHost, stream_));
1565
1566 CS_HIP_CHECK(hipStreamSynchronize(stream_));
1567 CS_HIP_CHECK(hipGetLastError());
1568 result = r_host_[0];
1569
1570 return true;
1571 }
1572
1574 template <class... Args>
1575 bool
1576 wait(void) {
1577 if (device_ > -1 && use_gpu_) {
1578 CS_HIP_CHECK(hipStreamSynchronize(stream_));
1579 CS_HIP_CHECK(hipGetLastError());
1580 return true;
1581 }
1582 return false;
1583 }
1584
1585 // Get interior faces sum type associated with this context
1586 template <class M>
1587 bool
1590 if (device_ < 0 || use_gpu_ == false) {
1591 return false;
1592 }
1593
1595 return true;
1596 }
1597
1598 // Get boundary faces sum type associated with this context
1599 template <class M>
1600 bool
1603 if (device_ < 0 || use_gpu_ == false) {
1604 return false;
1605 }
1606
1608 return true;
1609 }
1610
1611};
1612
1613#elif defined(SYCL_LANGUAGE_VERSION)
1614
1616#if !defined(CS_GLOB_SYCL_QUEUE_IS_DEFINED)
1617extern sycl::queue cs_glob_sycl_queue;
1618#define CS_GLOB_SYCL_QUEUE_IS_DEFINED 1
1619#endif
1620
1625class cs_device_context : public cs_dispatch_context_mixin<cs_device_context> {
1626
1627private:
1628
1629 sycl::queue &queue_;
1630 bool is_gpu;
1632 bool use_gpu_;
1634public:
1635
1637
1638 cs_device_context(void)
1639 : queue_(cs_glob_sycl_queue), is_gpu(false), use_gpu_(true)
1640 {
1641 is_gpu = queue_.get_device().is_gpu();
1642 }
1643
1645
1646 void
1647 set_use_gpu(bool use_gpu) {
1648 this->use_gpu_ = use_gpu;
1649 }
1650
1652
1653 bool
1654 use_gpu(void) {
1655 return (is_gpu && use_gpu_);
1656 }
1657
1659
1661 alloc_mode(void) {
1662 cs_alloc_mode_t amode
1663 = (is_gpu && use_gpu_) ? CS_ALLOC_HOST_DEVICE_SHARED : CS_ALLOC_HOST;
1664 return (amode);
1665 }
1666
1668 alloc_mode([[maybe_unused]] bool readable_on_cpu) {
1669 cs_alloc_mode_t amode
1670 = (is_gpu && use_gpu_) ? CS_ALLOC_HOST_DEVICE_SHARED : CS_ALLOC_HOST;
1671 return (amode);
1672 }
1673
1674public:
1675
1677 template <class F, class... Args>
1678 bool
1679 parallel_for(cs_lnum_t n, F&& f, Args&&... args) {
1680 if (is_gpu == false || use_gpu_ == false) {
1681 return false;
1682 }
1683
1684 queue_.parallel_for(n, static_cast<F&&>(f), static_cast<Args&&>(args)...);
1685
1686 return true;
1687 }
1688
1690 template <class M, class F, class... Args>
1691 bool
1692 parallel_for_i_faces(const M* m, F&& f, Args&&... args) {
1693 const cs_lnum_t n = m->n_i_faces;
1694 if (is_gpu == false || use_gpu_ == false) {
1695 return false;
1696 }
1697
1698 queue_.parallel_for(n, static_cast<F&&>(f), static_cast<Args&&>(args)...);
1699
1700 return true;
1701 }
1702
1704 template <class T, class F, class... Args>
1705 bool
1707 T& sum_,
1708 F&& f,
1709 Args&&... args) {
1710 if (is_gpu == false || use_gpu_ == false) {
1711 return false;
1712 }
1713
1714 sum_ = 0;
1715
1716 // TODO: use persistent allocation as we do in CUDA BLAS to avoid
1717 // excess allocation/deallocation.
1718 T *sum_ptr = (T *)sycl::malloc_shared(sizeof(T), queue_);
1719
1720 queue_.parallel_for(n,
1721 sycl::reduction(sum_ptr, (T)0, sycl::plus<T>()),
1722 static_cast<F&&>(f),
1723 static_cast<Args&&>(args)...).wait();
1724
1725 sum_ = sum_ptr[0];
1726
1727 sycl::free((void *)sum_ptr, queue_);
1728
1729 return true;
1730 }
1731
1733 template <class T, class R, class F, class... Args>
1734 bool
1736 T& result,
1737 R& reducer,
1738 F&& f,
1739 Args&&... args) {
1740
1741 // TODO implement this
1742 return false;
1743 }
1744
1746 template <class... Args>
1747 bool
1748 wait(void) {
1749 if (is_gpu && use_gpu_) {
1750 queue_.wait();
1751 return true;
1752 }
1753 return false;
1754 }
1755
1756 // Get interior faces sum type associated with this context
1757 template <class M>
1758 bool
1761 if (is_gpu == false || use_gpu_ == false) {
1762 return false;
1763 }
1764
1766 return true;
1767 }
1768
1769 // Get interior faces sum type associated with this context
1770 template <class M>
1771 bool
1774 if (is_gpu == false || use_gpu_ == false) {
1775 return false;
1776 }
1777
1779 return true;
1780 }
1781
1782};
1783
1784#elif defined(HAVE_OPENMP_TARGET)
1785
1790class cs_device_context : public cs_dispatch_context_mixin<cs_device_context> {
1791
1792private:
1793
1794 bool is_gpu;
1796 bool use_gpu_;
1798public:
1799
1801
1802 cs_device_context(void)
1803 : is_gpu(false), use_gpu_(true)
1804 {
1805 // This should be improved for any actual use of this approach
1806 // beyond basic testing
1807 is_gpu = (omp_get_num_devices() > 1) ? true : false;
1808 }
1809
1811
1812 void
1813 set_use_gpu(bool use_gpu) {
1814 this->use_gpu_ = use_gpu;
1815 }
1816
1818
1819 bool
1820 use_gpu(void) {
1821 return (is_gpu && use_gpu_);
1822 }
1823
1825
1827 alloc_mode(void) {
1828 cs_alloc_mode_t amode
1829 = (is_gpu && use_gpu_) ? CS_ALLOC_HOST_DEVICE_SHARED : CS_ALLOC_HOST;
1830 return (amode);
1831 }
1832
1834 alloc_mode([[maybe_unused]] bool readable_on_cpu) {
1835 cs_alloc_mode_t amode
1836 = (is_gpu && use_gpu_) ? CS_ALLOC_HOST_DEVICE_SHARED : CS_ALLOC_HOST;
1837 return (amode);
1838 }
1839
1840public:
1841
1843 template <class F, class... Args>
1844 bool
1845 parallel_for(cs_lnum_t n, F&& f, Args&&... args) {
1846 if (is_gpu == false || use_gpu_ == false) {
1847 return false;
1848 }
1849
1851# pragma omp target teams distribute parallel for
1852 for (cs_lnum_t i = 0; i < n; ++i) {
1853 f(i, args...);
1854 }
1855
1856 return true;
1857 }
1858
1860 template <class T, class F, class... Args>
1861 bool
1863 T& sum,
1864 F&& f,
1865 Args&&... args) {
1866 if (is_gpu == false || use_gpu_ == false) {
1867 return false;
1868 }
1869
1870 sum = 0;
1872# pragma omp target teams distribute parallel for reduction(+:sum)
1873 for (cs_lnum_t i = 0; i < n; ++i) {
1874 f(i, sum, args...);
1875 }
1876
1877 return true;
1878 }
1879
1881 template <class T, class R, class F, class... Args>
1882 bool
1884 T& result,
1885 R& reducer,
1886 F&& f,
1887 Args&&... args) {
1888
1889 // TODO implement this
1890 return false;
1891 }
1892
1894 template <class... Args>
1895 bool
1896 wait(void) {
1897 return true;
1898 }
1899
1900 // Get interior faces sum type associated with this context
1901 template <class M>
1902 bool
1905 if (is_gpu == false || use_gpu_ == false) {
1906 return false;
1907 }
1908
1910 return true;
1911 }
1912
1913 // Get interior faces sum type associated with this context
1914 template <class M>
1915 bool
1918 if (is_gpu == false || use_gpu_ == false) {
1919 return false;
1920 }
1921
1923 return true;
1924 }
1925
1926};
1927
1928#endif // __CUDACC__ or __HIPCC__ or SYCL or defined(HAVE_OPENMP_TARGET)
1929
1934class cs_void_context : public cs_dispatch_context_mixin<cs_void_context> {
1935
1936public:
1937
1939
1941 {}
1942
1943 /* Fill-in for CUDA or HIP methods, so as to allow using these methods
1944 in final cs_dispatch_context even when CUDA is not available,
1945 and without requiring a static cast of the form
1946
1947 static_cast<cs_device_context&>(ctx).set_use_gpu(true);
1948 */
1949
1950#if !defined(__CUDACC__) && !defined(__HIPCC__)
1951
1952 void
1953 set_stream([[maybe_unused]] int stream_id) {
1954 }
1955
1956#endif
1957
1958#if !defined(__CUDACC__)
1959
1960 void
1961 set_cuda_grid([[maybe_unused]] long grid_size,
1962 [[maybe_unused]] long block_size) {
1963 }
1964
1965 void
1966 set_cuda_device([[maybe_unused]] int device_id) {
1967 }
1968
1969#endif // !defined(__CUDACC__)
1970
1971#if !defined(__HIPCC__)
1972
1973 void
1974 set_hip_grid([[maybe_unused]] long grid_size,
1975 [[maybe_unused]] long block_size) {
1976 }
1977
1978 void
1979 set_hip_device([[maybe_unused]] int device_id) {
1980 }
1981
1982#endif // !defined(__HIPCC__)
1983
1984#if !defined(__CUDACC__) \
1985 && !defined(__HIPCC__) \
1986 && !defined(SYCL_LANGUAGE_VERSION) \
1987 && !defined(HAVE_OPENMP_TARGET)
1988
1989 /* Fill-in for device methods */
1990
1991 void
1992 set_use_gpu([[maybe_unused]] bool use_gpu) {
1993 }
1994
1996
1997 bool
1998 use_gpu(void) {
1999 return false;
2000 }
2001
2003
2006 return CS_ALLOC_HOST;
2007 }
2008
2010 alloc_mode([[maybe_unused]] bool readable_on_cpu) {
2011 return CS_ALLOC_HOST;
2012 }
2013
2014#endif // ! __CUDACC__ && ! __HIPCC__ && ! SYCL_LANGUAGE_VERSION && ! defined(HAVE_OPENMP_TARGET)
2015
2016public:
2017
2018 // Abort execution if no execution method is available.
2019 template <class F, class... Args>
2020 bool parallel_for([[maybe_unused]] cs_lnum_t n,
2021 [[maybe_unused]] F&& f,
2022 [[maybe_unused]] Args&&... args) {
2023 cs_assert(0);
2024 return false;
2025 }
2026
2027 // Abort execution if no execution method is available.
2028 template <class T, class F, class... Args>
2029 bool parallel_for_reduce_sum([[maybe_unused]] cs_lnum_t n,
2030 [[maybe_unused]] T& sum,
2031 [[maybe_unused]] F&& f,
2032 [[maybe_unused]] Args&&... args) {
2033 cs_assert(0);
2034 return false;
2035 }
2036
2037 // Abort execution if no execution method is available.
2038 template <class T, class R, class F, class... Args>
2039 bool parallel_for_reduce([[maybe_unused]] cs_lnum_t n,
2040 [[maybe_unused]] T& result,
2041 [[maybe_unused]] R& reducer,
2042 [[maybe_unused]] F&& f,
2043 [[maybe_unused]] Args&&... args) {
2044 cs_assert(0);
2045 return false;
2046 }
2047
2048 // Abort execution if no synchronization method is available.
2049 template <class... Args>
2050 bool
2051 wait(void) {
2052 cs_assert(0);
2053 return false;
2054 }
2055
2056};
2057
2063template <class... Contexts>
2065 : public cs_dispatch_context_mixin<cs_combined_context<Contexts...>>,
2066 public Contexts... {
2067
2068private:
2070
2071public:
2073 cs_combined_context(Contexts... contexts)
2074 : Contexts(std::move(contexts))...
2075 {}
2076
2077public:
2078
2079 /*--------------------------------------------------------------------------*/
2080 /* \brief Parallel computation over interior faces.
2081 *
2082 * This method is intended for use when assembling cell values
2083 * with face-based computations, using the appropriate \ref cs_dispatch_sum
2084 * functions.
2085 *
2086 * On CPU, loops are scheduled based on the current face numbering, so
2087 * as to avoid thread races when summing values. On GPU, atomic sums are used.
2088 *
2089 * \tparam M mesh type structure (templated mostly to avoid
2090 * dependency to mesh definitions in lower level code)
2091 * \tparam F lambda function or functor
2092 *
2093 * \param[in] m pointer to mesh
2094 * \param[in] f lambda function or functor to execute
2095 */
2096 /*--------------------------------------------------------------------------*/
2097
2098 template <class M, class F, class... Args>
2099 auto parallel_for_i_faces(const M* m, F&& f, Args&&... args) {
2100 bool launched = false;
2101 [[maybe_unused]] decltype(nullptr) try_execute[] = {
2102 ( launched = launched
2103 || Contexts::parallel_for_i_faces(m, f, args...), nullptr)...
2104 };
2105 }
2106
2107 /*--------------------------------------------------------------------------*/
2108 /* \brief Parallel computation over boundary faces.
2109 *
2110 * This method is intended for use when assembling cell values
2111 * with face-based computations, using the appropriate \ref cs_dispatch_sum
2112 * functions.
2113 *
2114 * On CPU, loops are scheduled based on the current face numbering, so
2115 * as to avoid thread races when summing values. On GPU, atomic sums are used.
2116 *
2117 * \tparam M mesh type structure (templated mostly to avoid
2118 * dependency to mesh definitions in lower level code)
2119 * \tparam F lambda function or functor
2120 *
2121 * \param[in] m pointer to mesh
2122 * \param[in] f lambda function or functor to execute
2123 */
2124 /*--------------------------------------------------------------------------*/
2125
2126 template <class M, class F, class... Args>
2127 auto parallel_for_b_faces(const M* m, F&& f, Args&&... args) {
2128 bool launched = false;
2129 [[maybe_unused]] decltype(nullptr) try_execute[] = {
2130 ( launched = launched
2131 || Contexts::parallel_for_b_faces(m, f, args...), nullptr)...
2132 };
2133 }
2134
2135 /*--------------------------------------------------------------------------*/
2136 /* \brief General parallel computation over elements.
2137 *
2138 * \tparam F lambda function or functor
2139 *
2140 * \param[in] n number of elements to compute
2141 * \param[in] f lambda function or functor to execute
2142 */
2143 /*--------------------------------------------------------------------------*/
2144
2145 template <class F, class... Args>
2146 auto parallel_for(cs_lnum_t n, F&& f, Args&&... args) {
2147 bool launched = false;
2148 [[maybe_unused]] decltype(nullptr) try_execute[] = {
2149 ( launched = launched
2150 || Contexts::parallel_for(n, f, args...), nullptr)...
2151 };
2152 }
2153
2154 /*--------------------------------------------------------------------------*/
2155 /* \brief General parallel computation over elements, with a floating-point
2156 * sum reduction.
2157 *
2158 * \tparam T reduced element type
2159 * \tparam F lambda function or functor
2160 *
2161 * \param[in] n number of elements to compute
2162 * \param[in] sum resulting sum
2163 * \param[in] f lambda function or functor to execute
2164 */
2165 /*--------------------------------------------------------------------------*/
2166
2167 template <class T, class F, class... Args>
2169 (cs_lnum_t n, T& sum, F&& f, Args&&... args) {
2170 bool launched = false;
2171 [[maybe_unused]] decltype(nullptr) try_execute[] = {
2172 ( launched = launched
2173 || Contexts::parallel_for_reduce_sum(n, sum, f, args...),
2174 nullptr)...
2175 };
2176 }
2177
2178 /*--------------------------------------------------------------------------*/
2179 /* \brief General parallel computation over elements, with a
2180 * user-defined reduction.
2181 *
2182 * \tparam T reduced element type
2183 * \tparam R reducer class
2184 * \tparam F lambda function or functor
2185 *
2186 * \param[in] n number of elements to compute
2187 * \param[out] result resulting sum
2188 * \param[in] reducer reducer object
2189 * \param[in] f lambda function or functor to execute
2190 */
2191 /*--------------------------------------------------------------------------*/
2192
2193 template <class T, class R, class F, class... Args>
2195 (cs_lnum_t n, T& result, R& reducer, F&& f, Args&&... args) {
2196 bool launched = false;
2197 [[maybe_unused]] decltype(nullptr) try_execute[] = {
2198 ( launched = launched
2199 || Contexts::parallel_for_reduce(n, result, reducer, f, args...),
2200 nullptr)...
2201 };
2202 }
2203
2204 /*--------------------------------------------------------------------------*/
2208 /*--------------------------------------------------------------------------*/
2209
2210 void
2211 wait(void) {
2212 bool done = false;
2213 [[maybe_unused]] decltype(nullptr) try_execute[] = {
2214 ( done = done
2215 || Contexts::wait(), nullptr)...
2216 };
2217 }
2218
2219 /*--------------------------------------------------------------------------*/
2229 /*--------------------------------------------------------------------------*/
2230
2231 template <class M>
2235 bool known = false;
2236 [[maybe_unused]] decltype(nullptr) try_query[] = {
2237 ( known = known
2238 || Contexts::try_get_parallel_for_i_faces_sum_type(m, sum_type),
2239 nullptr)...
2240 };
2241 return sum_type;
2242 }
2243
2244 /*--------------------------------------------------------------------------*/
2254 /*--------------------------------------------------------------------------*/
2255
2256 template <class M>
2260 bool known = false;
2261 [[maybe_unused]] decltype(nullptr) try_query[] = {
2262 ( known = known
2263 || Contexts::try_get_parallel_for_b_faces_sum_type(m, sum_type),
2264 nullptr)...
2265 };
2266 return sum_type;
2267 }
2268
2269};
2270
2271/*----------------------------------------------------------------------------*/
2276/*----------------------------------------------------------------------------*/
2277
2279#if defined(__CUDACC__) \
2280 || defined(__HIPCC__) \
2281 || defined(SYCL_LANGUAGE_VERSION) \
2282 || defined(HAVE_OPENMP_TARGET)
2283 cs_device_context,
2284#endif
2285 cs_host_context,
2286 cs_void_context
2287>
2288{
2289
2290private:
2292#if defined(__CUDACC__) \
2293 || defined(__HIPCC__) \
2294 || defined(SYCL_LANGUAGE_VERSION) \
2295 || defined(HAVE_OPENMP_TARGET)
2296 cs_device_context,
2297#endif
2300>;
2301
2302public:
2303 using base_t::base_t;
2304 using base_t::operator=;
2305
2306};
2307
2308/*
2309 Remarks:
2310
2311 Instantiation can simply be done using:
2312
2313 `cs_dispatch_context ctx;`
2314
2315 Instanciation can also be done with specific construction options,
2316 for example:
2317
2318 `cs_dispatch_context ctx(cs_device_context(stream), {});`
2319
2320 or:
2321
2322 `cs_dispatch_context ctx(cs_device_context(), {});`
2323
2324*/
2325
2326/*=============================================================================
2327 * Global variable definitions
2328 *============================================================================*/
2329
2330/*=============================================================================
2331 * Public function prototypes
2332 *============================================================================*/
2333
2334/*----------------------------------------------------------------------------*/
2347/*----------------------------------------------------------------------------*/
2348
2349#ifdef __CUDA_ARCH__ // Test whether we are on GPU or CPU...
2350
2351template <typename T>
2352__device__ static void __forceinline__
2353cs_dispatch_sum(T *dest,
2354 const T src,
2355 cs_dispatch_sum_type_t sum_type)
2356{
2357 if (sum_type == CS_DISPATCH_SUM_ATOMIC) {
2358#if 1
2359 using sum_v = assembled_value<T>;
2360 sum_v v;
2361
2362 v.get() = src;
2363 sum_v::ref(*dest).conflict_free_add(-1u, v);
2364#else
2365 atomicAdd(dest, src);
2366#endif
2367 }
2368 else if (sum_type == CS_DISPATCH_SUM_SIMPLE) {
2369 *dest += src;
2370 }
2371}
2372
2373#elif defined(__HIP_DEVICE_COMPILE__)
2374
2375template <typename T>
2376__device__ static void __forceinline__
2377cs_dispatch_sum(T *dest,
2378 const T src,
2379 cs_dispatch_sum_type_t sum_type)
2380{
2381 if (sum_type == CS_DISPATCH_SUM_ATOMIC) {
2382 atomicAdd(dest, src);
2383 }
2384 else if (sum_type == CS_DISPATCH_SUM_SIMPLE) {
2385 *dest += src;
2386 }
2387}
2388
2389#elif defined(SYCL_LANGUAGE_VERSION)
2390
2391template <typename T>
2392inline void
2393cs_dispatch_sum(T *dest,
2394 const T src,
2395 cs_dispatch_sum_type_t sum_type)
2396{
2397 if (sum_type == CS_DISPATCH_SUM_SIMPLE) {
2398 *dest += src;
2399 }
2400 else if (sum_type == CS_DISPATCH_SUM_ATOMIC) {
2401 sycl::atomic_ref<T,
2402 sycl::memory_order::relaxed,
2403 sycl::memory_scope::device> aref(*dest);
2404 aref.fetch_add(src);
2405 }
2406}
2407
2408#else // ! CUDA or HIP or SYCL
2409
2410template <typename T>
2411inline void
2413 const T src,
2414 cs_dispatch_sum_type_t sum_type)
2415{
2416 if (sum_type == CS_DISPATCH_SUM_SIMPLE) {
2417 *dest += src;
2418 }
2419 else if (sum_type == CS_DISPATCH_SUM_ATOMIC) {
2420 #ifdef _OPENMP
2421 #pragma omp atomic
2422 #endif
2423 *dest += src;
2424 }
2425}
2426
2427#endif // __CUDA_ARCH__
2428
2429/*----------------------------------------------------------------------------*/
2443/*----------------------------------------------------------------------------*/
2444
2445#ifdef __CUDA_ARCH__ // Test whether we are on GPU or CPU...
2446
2447template <size_t dim, typename T>
2448__device__ static void __forceinline__
2449cs_dispatch_sum(T *dest,
2450 const T *src,
2451 cs_dispatch_sum_type_t sum_type)
2452{
2453 if (sum_type == CS_DISPATCH_SUM_SIMPLE) {
2454 for (cs_lnum_t i = 0; i < dim; i++) {
2455 dest[i] += src[i];
2456 }
2457 }
2458 else if (sum_type == CS_DISPATCH_SUM_ATOMIC) {
2459#if __CUDA_ARCH__ >= 700
2460 using sum_v = assembled_value<T, dim>;
2461 sum_v v;
2462
2463 for (size_t i = 0; i < dim; i++) {
2464 v[i].get() = src[i];
2465 }
2466
2467 sum_v &vs = reinterpret_cast<sum_v &>(*dest);
2468 vs.conflict_free_add(-1u, v);
2469
2470 //sum_v::ref(dest).conflict_free_add(-1u, v);
2471#else
2472 for (size_t i = 0; i < dim; i++) {
2473 atomicAdd(&dest[i], src[i]);
2474 }
2475#endif
2476 }
2477}
2478
2479#elif defined(__HIP_DEVICE_COMPILE__)
2480
2481template <size_t dim, typename T>
2482__device__ static void __forceinline__
2483cs_dispatch_sum(T *dest,
2484 const T *src,
2485 cs_dispatch_sum_type_t sum_type)
2486{
2487 if (sum_type == CS_DISPATCH_SUM_SIMPLE) {
2488 for (cs_lnum_t i = 0; i < dim; i++) {
2489 dest[i] += src[i];
2490 }
2491 }
2492 else if (sum_type == CS_DISPATCH_SUM_ATOMIC) {
2493 for (size_t i = 0; i < dim; i++) {
2494 atomicAdd(&dest[i], src[i]);
2495 }
2496 }
2497}
2498
2499#elif defined(SYCL_LANGUAGE_VERSION)
2500
2501template <size_t dim, typename T>
2502inline void
2503cs_dispatch_sum(T *dest,
2504 const T *src,
2505 cs_dispatch_sum_type_t sum_type)
2506{
2507 if (sum_type == CS_DISPATCH_SUM_SIMPLE) {
2508 for (size_t i = 0; i < dim; i++) {
2509 dest[i] += src[i];
2510 }
2511 }
2512 else if (sum_type == CS_DISPATCH_SUM_ATOMIC) {
2513 for (size_t i = 0; i < dim; i++) {
2514 sycl::atomic_ref<T,
2515 sycl::memory_order::relaxed,
2516 sycl::memory_scope::device> aref(dest[i]);
2517 aref.fetch_add(src[i]);
2518 }
2519 }
2520}
2521
2522#else // ! CUDA or SYCL
2523
2524template <size_t dim, typename T>
2525inline void
2527 const T *src,
2528 cs_dispatch_sum_type_t sum_type)
2529{
2530 if (sum_type == CS_DISPATCH_SUM_SIMPLE) {
2531 for (size_t i = 0; i < dim; i++) {
2532 dest[i] += src[i];
2533 }
2534 }
2535 else if (sum_type == CS_DISPATCH_SUM_ATOMIC) {
2536 for (size_t i = 0; i < dim; i++) {
2537 #ifdef _OPENMP
2538 #pragma omp atomic
2539 #endif
2540 dest[i] += src[i];
2541 }
2542 }
2543}
2544
2545#endif // __CUDA_ARCH__
2546
2547/*----------------------------------------------------------------------------*/
2548
2549#endif /* CS_DISPATCH_H */
void bft_error(const char *const file_name, const int line_num, const int sys_error_code, const char *const format,...)
Calls the error handler (set by bft_error_handler_set() or default).
Definition: bft_error.cpp:187
Class for value assembly using warp-based partial summation to reduce conflict occurences in downstre...
Definition: cs_math_hip.h:172
__device__ inner_type & get() noexcept
Definition: cs_math_hip.h:273
Definition: cs_dispatch.h:2066
cs_combined_context()=default
auto parallel_for_reduce_sum(cs_lnum_t n, T &sum, F &&f, Args &&... args)
Definition: cs_dispatch.h:2169
cs_combined_context(Contexts... contexts)
Definition: cs_dispatch.h:2073
auto parallel_for_reduce(cs_lnum_t n, T &result, R &reducer, F &&f, Args &&... args)
Definition: cs_dispatch.h:2195
auto parallel_for_b_faces(const M *m, F &&f, Args &&... args)
Definition: cs_dispatch.h:2127
auto parallel_for_i_faces(const M *m, F &&f, Args &&... args)
Definition: cs_dispatch.h:2099
auto parallel_for(cs_lnum_t n, F &&f, Args &&... args)
Definition: cs_dispatch.h:2146
void wait(void)
Wait (synchronize) until launched computations have finished.
Definition: cs_dispatch.h:2211
cs_dispatch_sum_type_t get_parallel_for_b_faces_sum_type(const M *m)
Return sum type to be used with parallel_for_b_faces.
Definition: cs_dispatch.h:2258
cs_dispatch_sum_type_t get_parallel_for_i_faces_sum_type(const M *m)
Return sum type to be used with parallel_for_i_faces.
Definition: cs_dispatch.h:2233
Definition: cs_dispatch.h:110
decltype(auto) parallel_for(cs_lnum_t n, F &&f, Args &&... args)=delete
bool try_get_parallel_for_b_faces_sum_type(const M *m, cs_dispatch_sum_type_t &st)
Definition: cs_dispatch.h:205
decltype(auto) parallel_for_b_faces(const M *m, F &&f, Args &&... args)
Definition: cs_dispatch.h:184
decltype(auto) parallel_for_reduce_sum(cs_lnum_t n, T &sum, F &&f, Args &&... args)=delete
bool try_get_parallel_for_i_faces_sum_type(const M *m, cs_dispatch_sum_type_t &st)
Definition: cs_dispatch.h:195
decltype(auto) wait(void)=delete
decltype(auto) parallel_for_i_faces(const M *m, F &&f, Args &&... args)
Definition: cs_dispatch.h:173
decltype(auto) parallel_for_reduce(cs_lnum_t n, T &r, R &reducer, F &&f, Args &&... args)=delete
Definition: cs_dispatch.h:2288
Definition: cs_dispatch.h:215
cs_host_context()
Definition: cs_dispatch.h:224
cs_lnum_t n_min_per_cpu_thread(void)
Get minimum number of elements threshold for CPU multithread execution.
Definition: cs_dispatch.h:291
bool parallel_for(cs_lnum_t n, F &&f, Args &&... args)
Iterate using a plain omp parallel for.
Definition: cs_dispatch.h:310
bool parallel_for_reduce_sum(cs_lnum_t n, T &sum, F &&f, Args &&... args)
Plain OpenMP parallel reduction with simple sum.
Definition: cs_dispatch.h:369
void set_n_cpu_threads(int n)
Set number of threads for CPU multithread execution.
Definition: cs_dispatch.h:297
bool wait(void)
Wait upon completion.
Definition: cs_dispatch.h:488
bool try_get_parallel_for_b_faces_sum_type(const M *m, cs_dispatch_sum_type_t &st)
Definition: cs_dispatch.h:504
bool try_get_parallel_for_i_faces_sum_type(const M *m, cs_dispatch_sum_type_t &st)
Definition: cs_dispatch.h:495
bool parallel_for_b_faces(const M *m, F &&f, Args &&... args)
Definition: cs_dispatch.h:347
int n_cpu_threads(void)
Get number of threads for CPU multithread execution (-1 if automatic)
Definition: cs_dispatch.h:303
bool parallel_for_i_faces(const M *m, F &&f, Args &&... args)
Definition: cs_dispatch.h:324
bool parallel_for_reduce(cs_lnum_t n, T &result, R &reducer, F &&f, Args &&... args)
OpenMP parallel reduction with general reducer.
Definition: cs_dispatch.h:431
void set_n_min_per_cpu_thread(cs_lnum_t n)
Set minimum number of elements threshold for CPU multithread execution.
Definition: cs_dispatch.h:285
Definition: cs_dispatch.h:1934
bool parallel_for(cs_lnum_t n, F &&f, Args &&... args)
Definition: cs_dispatch.h:2020
void set_cuda_device(int device_id)
Definition: cs_dispatch.h:1966
void set_hip_grid(long grid_size, long block_size)
Definition: cs_dispatch.h:1974
cs_alloc_mode_t alloc_mode(bool readable_on_cpu)
Definition: cs_dispatch.h:2010
bool parallel_for_reduce_sum(cs_lnum_t n, T &sum, F &&f, Args &&... args)
Definition: cs_dispatch.h:2029
void set_hip_device(int device_id)
Definition: cs_dispatch.h:1979
bool wait(void)
Definition: cs_dispatch.h:2051
void set_stream(int stream_id)
Definition: cs_dispatch.h:1953
void set_use_gpu(bool use_gpu)
Definition: cs_dispatch.h:1992
cs_void_context(void)
Constructor.
Definition: cs_dispatch.h:1940
cs_alloc_mode_t alloc_mode(void)
Check preferred allocation mode depending on execution policy.
Definition: cs_dispatch.h:2005
bool parallel_for_reduce(cs_lnum_t n, T &result, R &reducer, F &&f, Args &&... args)
Definition: cs_dispatch.h:2039
void set_cuda_grid(long grid_size, long block_size)
Definition: cs_dispatch.h:1961
bool use_gpu(void)
Check whether we are trying to run on GPU.
Definition: cs_dispatch.h:1998
#define cs_assert(expr)
Abort the program if the given assertion is false.
Definition: cs_assert.h:65
int cs_base_hip_get_device(void)
Return currently selected HIP devices.
Definition: cs_base_hip.cpp:816
int cs_glob_n_threads
Definition: cs_defs.cpp:168
#define restrict
Definition: cs_defs.h:148
#define CS_THR_MIN
Definition: cs_defs.h:483
static cs_lnum_t cs_align(cs_lnum_t i, cs_lnum_t m)
Given a base index i, return the next index aligned with a size m.
Definition: cs_defs.h:639
int cs_lnum_t
local mesh entity id
Definition: cs_defs.h:325
#define CS_CL_SIZE
Definition: cs_defs.h:488
void cs_dispatch_sum(T *dest, const T src, cs_dispatch_sum_type_t sum_type)
sum values using a chosen dispatch sum type.
Definition: cs_dispatch.h:2412
cs_dispatch_sum_type_t
Definition: cs_dispatch.h:95
@ CS_DISPATCH_SUM_SIMPLE
Definition: cs_dispatch.h:97
@ CS_DISPATCH_SUM_ATOMIC
Definition: cs_dispatch.h:99
#define cs_alloc_mode_device
Definition: cs_mem.h:185
cs_alloc_mode_t
Definition: cs_mem.h:46
@ CS_ALLOC_HOST
Definition: cs_mem.h:48
@ CS_ALLOC_HOST_DEVICE_SHARED
Definition: cs_mem.h:53
static void sum(const cs_mpi_wrapper &mpi_w, T &first, Vals &... values)
Sum values of a given datatype over a given communicator.
Definition: cs_parall.h:893
bool parallel_for_reduce_sum(cs_lnum_t n, T &sum, F &&f, Args &&... args)
parallel_for_reduce_sum construct using a dispatch context based on template parameter (enum) "exec_t...
Definition: cs_execution_context.h:347
bool parallel_for_i_faces(const M *m, F &&f, Args &&... args)
parallel_for_i_faces construct using a dispatch context based on template parameter (enum) "exec_type...
Definition: cs_execution_context.h:286
bool wait(void)
wait construct using a dispatch context based on template parameter of exec_type (enum)
Definition: cs_execution_context.h:543
bool parallel_for_reduce(cs_lnum_t n, T &result, R &reducer, F &&f, Args &&... args)
parallel_for_reduce construct using a dispatch context based on template parameter (enum) "exec_type"
Definition: cs_execution_context.h:442
bool parallel_for(cs_lnum_t n, F &&f, Args &&... args)
parallel_for construct using a dispatch context based on template parameter (enum) "exec_type"
Definition: cs_execution_context.h:229