TrioCFD 1.9.9_beta
TrioCFD documentation
Loading...
Searching...
No Matches
Schema_Comm_Vecteurs.cpp
1/****************************************************************************
2* Copyright (c) 2026, CEA
3* All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9*
10* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
11* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
12* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13*
14*****************************************************************************/
15#include <Schema_Comm_Vecteurs.h>
16#include <Comm_Group.h>
17#include <communications.h>
18#include <PE_Groups.h>
19#include <sstream>
20#include <comm_incl.h>
21
26#if INT_is_64_ == 2
27ArrOfTID Schema_Comm_Vecteurs::tmp_area_tid_;
28#endif
30bool check_comm_vector = false;
31
40
41void Schema_Comm_Vecteurs_Static_Data::init(int min_buf_size, bool bufferOnDevice)
42{
43 if (buf_pointers_size_ == 0)
44 {
46 buf_pointers_size_ = n * 2; // Maximum required when exchanging with all procs in exchange()
47 buf_pointers_ = new char*[n*2];
48 for (int i = 0; i < n*2; i++)
49 buf_pointers_[i] = 0;
50 }
51 // Is the global buffer large enough?
52 if (buffer_base_size_ < min_buf_size)
53 {
55 {
58 }
59 delete [] buffer_base_;
60 buffer_base_ = new char[min_buf_size];
61 // GF: zero-initialization added for mpiwrapper/valgrind but is it useful?
62 for (int i = 0; i < min_buf_size; i++)
63 buffer_base_[i] = 0;
64 buffer_base_size_ = min_buf_size;
65 }
66 if (bufferOnDevice && buffer_base_device_size_ < min_buf_size)
67 {
68 // Allocate buffer_base_ on device:
71 allocateOnDevice(buffer_base_, min_buf_size);
72 buffer_base_device_size_ = min_buf_size;
73 }
74}
75
77{
78 /* ToDo OpenMP Fix crash when using AmgX: Failing in Thread:0
79 call to cuInit returned error 4: Deinitialized
80 deleteOnDevice(buffer_base_, buffer_base_size_);
81 */
82 delete[] buffer_base_;
83 delete[] buf_pointers_;
84}
85
87{
88 status_ = RESET;
89 const char* env_var = getenv("TRUST_USE_MPI_GPU_AWARE");
90 use_gpu_aware_mpi_ = env_var != nullptr && std::stoi(env_var) == 1;
92 {
93#ifdef CRAY_MPICH_VER
94 if (getenv("MPICH_GPU_SUPPORT_ENABLED") == nullptr)
95 Process::exit("You try to enable GPU communications on Cray MPICH with TRUST_USE_MPI_GPU_AWARE=1 but forgot to set also MPICH_GPU_SUPPORT_ENABLED=1 !");
96#endif
97 std::cerr << "[MPI] Enabling GPU capability to communicate between devices." << std::endl;
98 //Cerr << "[MPI] Warning! Only MPI calls with device pointers will benefit. Classic MPI calls with host pointers will be slower..." << finl;
99 }
100}
101
106
107/*! @brief Resets buffer sizes.
108 *
109 * Buffer sizes must then be defined with add_send/recv_area_...().
110 * This method must be called simultaneously on all processors in the group.
111 *
112 */
114{
115 assert(status_ == END_INIT || status_ == RESET);
116 // Reset the sizes_ arrays
117 const int np = Process::nproc();
118 send_buf_sizes_.resize_array(np, RESIZE_OPTIONS::NOCOPY_NOINIT);
119 send_buf_sizes_ = 0;
120 recv_buf_sizes_.resize_array(np, RESIZE_OPTIONS::NOCOPY_NOINIT);
121 recv_buf_sizes_ = 0;
122 send_procs_.resize_array(0);
123 recv_procs_.resize_array(0);
124 sorted_ = 1;
127 {
128#if defined(TRUST_USE_CUDA) && !defined(MPIX_CUDA_AWARE_SUPPORT)
129 Process::exit("MPI version is detected as not CUDA-Aware. You can't use TRUST_USE_MPI_GPU_AWARE=1");
130#endif
131 }
132}
133
134/*! @brief Once the data to exchange has been declared with add_send/recv_area_..(),
135 *
136 * initializes buffer offsets and allocates a global buffer of sufficient size.
137 * Must be called by all processors in the group.
138 *
139 */
141{
142 assert(status_ == BEGIN_INIT);
143 assert(Process::nproc() == send_buf_sizes_.size_array());
144 // Verification of send and receive buffer sizes:
146 {
147 const int n = send_buf_sizes_.size_array();
148 ArrOfInt tmp(n);
149 envoyer_all_to_all(send_buf_sizes_, tmp);
150 int err = 0;
151 for (int i = 0; i < n; i++)
152 if (tmp[i] != recv_buf_sizes_[i])
153 err++;
154 if (Process::mp_sum(err))
155 {
156 Cerr << "Error in Schema_Comm_Vecteurs::end_init(): send_size_ and recv_size_ don't match, see log files" << finl;
157 Process::Journal() << "Error in Schema_Comm_Vecteurs::end_init():\n"
158 << "send_sizes_ = " << send_buf_sizes_
159 << "\n recv_sizes_ = " << recv_buf_sizes_ << finl;
162 }
163 }
164 // Sort processor indices in ascending order
165 if (!sorted_)
166 {
167 send_procs_.ordonne_array();
168 recv_procs_.ordonne_array();
169 }
170 const int nsend = send_procs_.size_array();
171 const int nrecv = recv_procs_.size_array();
172 int offset = 0;
173 int i;
174 for (i = 0; i < nsend; i++)
175 {
176 int pe = send_procs_[i];
177 const int size = (send_buf_sizes_[pe]+7)&(~7); // align size on 8 bytes
178 offset += size;
179 assert(pe >= i); // send_procs_ sorted in ascending order
180 send_buf_sizes_[i] = size;
181 }
182 for (i = 0; i < nrecv; i++)
183 {
184 int pe = recv_procs_[i];
185 const int size = (recv_buf_sizes_[pe]+7)&(~7); // align size on 8 bytes
186 offset += size;
187 assert(pe >= i); // recv_procs_ sorted in ascending order
188 recv_buf_sizes_[i] = size;
189 }
190 min_buf_size_ = offset; // buffer size to allocate recv_buf_offset_[i] = offset;
191
192 send_buf_sizes_.resize_array(nsend);
193 recv_buf_sizes_.resize_array(nrecv);
195}
196
197/*! @brief Starts a new data exchange (buffer sizes must have been initialized with begin_init()...end_init()).
198 *
199 * Sets sdata_.buf_pointers_ to the beginning of the buffers for each processor
200 * for which a "send" buffer was declared.
201 * After begin_comm(), the buffers must be filled using
202 * get_next_area_int() or get_next_area_double() in the same order
203 * as declared during the initialization phase, then exchange() must be called.
204 *
205 */
206void Schema_Comm_Vecteurs::begin_comm(bool bufferOnDevice)
207{
208 assert(status_ == END_INIT);
209 // Not an assert because the error is serious and presumably rare...
210 if (buffer_locked_)
211 {
212 Cerr << "Internal error in Schema_Comm_Vecteurs::begin_comm(): buffers already locked by another communication" << finl;
214 }
215 buffer_locked_ = true;
216 sdata_.init(min_buf_size_, bufferOnDevice);
217
218 // Point the buffers to the beginning of the send_buffers
219 char *ptr = sdata_.buffer_base_;
220
221 const int nsend = send_procs_.size_array();
222 for (int i = 0; i < nsend; i++)
223 {
224 const int pe = send_procs_[i];
225 sdata_.buf_pointers_[pe] = ptr;
226 ptr += send_buf_sizes_[i];
227 }
228 buffer_locked_ = true;
230 bufferOnDevice_ = bufferOnDevice;
231}
232
233void Schema_Comm_Vecteurs::exchange(IsExchangeBlocking exchange_type, const std::string kernel_name)
234{
235
236 char * ptr = sdata_.buffer_base_;
237 const Comm_Group& group = PE_Groups::current_group();
238 const int nsend = send_procs_.size_array();
239 const int nrecv = recv_procs_.size_array();
240
241 if ((exchange_type == IsExchangeBlocking::DefaultBlocking)||(exchange_type == IsExchangeBlocking::NonBlockingStart))
242 {
243
244 // Copy buffer before MPI send
245 if (bufferOnDevice_)
246 {
248 copyFromDevice(sdata_.buffer_base_, min_buf_size_); // Copy buffer to host for MPI communication
249 else
250 {
251 // Communication between devices. Use device buffer:
252 ptr = addrOnDevice(sdata_.buffer_base_);
253 }
254 }
255
256 assert(status_ == BEGIN_COMM);
257 // Check that all buffers are full
258 assert(check_buffers_full());
259 // Exchange the data
260
261 // Use the sdata_.buf_pointers_ array to store the addresses
262 // of buffers to pass to Comm_Group::send_recv_start()
263 // (dimensioned to 2*nproc() so sufficient)
264 assert(nsend + nrecv <= sdata_.buf_pointers_size_);
265 char ** send_bufs = sdata_.buf_pointers_;
266 char ** recv_bufs = sdata_.buf_pointers_ + nsend;
267 for (int i = 0; i < nsend; i++)
268 {
269 send_bufs[i] = ptr;
270 ptr += send_buf_sizes_[i];
271 }
272 for (int i = 0; i < nrecv; i++)
273 {
274 recv_bufs[i] = ptr;
275 ptr += recv_buf_sizes_[i];
276 }
277
278 // We should be able to use int64 as type here because
279 // the buffers are aligned on 8 bytes.
280
281 if (exchange_type == IsExchangeBlocking::NonBlockingStart) start_gpu_timer(kernel_name);
283 recv_procs_, recv_buf_sizes_, recv_bufs,
285 }
286
287 if ((exchange_type == IsExchangeBlocking::DefaultBlocking)||(exchange_type == IsExchangeBlocking::NonBlockingFinish))
288 {
289 group.send_recv_finish();
290 if (exchange_type == IsExchangeBlocking::NonBlockingFinish) end_gpu_timer(kernel_name);
291 // Point the buffers to the received data
292 char * recv_ptr = sdata_.buffer_base_;
293 for (int i = 0; i < nsend; i++)
294 recv_ptr += send_buf_sizes_[i];
295 for (int i = 0; i < nrecv; i++)
296 {
297 const int pe = recv_procs_[i];
298 sdata_.buf_pointers_[pe] = recv_ptr;
299 recv_ptr += recv_buf_sizes_[i];
300 }
302
303 // Copy buffer to device after MPI recv if GPU-Aware MPI is not enabled:
304 if (bufferOnDevice_ && !use_gpu_aware_mpi_) copyToDevice(sdata_.buffer_base_, min_buf_size_);
305 }
306}
307
309{
310 assert(status_ == EXCHANGED);
311 // Check that all data has been read
312 assert(check_buffers_full());
313 status_ = END_INIT; // ready for a new begin_comm()
314 buffer_locked_ = false;
315 bufferOnDevice_ = false;
316}
317
318/*! @brief Depending on status_, verifies that all buffer pointers point to the end of the buffer allocated for each processor in send
319 *
320 * or receive mode. Returns 0 on error (if a buffer has not been
321 * completely filled or emptied).
322 *
323 */
325{
326 char *ptr = sdata_.buffer_base_;
327 const int nsend = send_procs_.size_array();
328 int i;
329 int ok = 1;
330 if (status_ == BEGIN_COMM)
331 {
332 for (i = 0; i < nsend; i++)
333 {
334 ptr += send_buf_sizes_[i];
335 const int pe = send_procs_[i];
336 char *ptr2 = sdata_.buf_pointers_[pe];
337 ALIGN_SIZE(ptr2, sizeof(double));
338 if (ptr != ptr2)
339 {
340 Cerr << "Internal error in Schema_Comm_Vecteurs::check_buffers_full(): send buffer for processor "
341 << pe << " is not full" << finl;
342 ok = 0;
343 }
344 }
345 }
346 else if (status_ == EXCHANGED)
347 {
348 for (i = 0; i < nsend; i++)
349 ptr += send_buf_sizes_[i];
350 const int nrecv = recv_procs_.size_array();
351 for (i = 0; i < nrecv; i++)
352 {
353 ptr += recv_buf_sizes_[i];
354 const int pe = recv_procs_[i];
355 char *ptr2 = sdata_.buf_pointers_[pe];
356 ALIGN_SIZE(ptr2, sizeof(double));
357 if (ptr != ptr2)
358 {
359 Cerr << "Internal error in Schema_Comm_Vecteurs::check_buffers_full(): recv buffer for processor "
360 << pe << " has not been read entirely" << finl;
361 ok = 0;
362 }
363 }
364 }
365 else
366 {
367 Cerr << "check_buffers_full: What ?" << finl;
369 }
370 return ok;
371}
372
373/*! @brief Verifies that there are at least byte_size bytes remaining in the buffer of processor pe.
374 *
375 */
376int Schema_Comm_Vecteurs::check_next_area(int pe, int byte_size) const
377{
378 assert(byte_size >= 0);
379 if (byte_size == 0)
380 {
381 return 1;
382 }
383 const ArrOfInt& procs = (status_ == BEGIN_COMM) ? send_procs_ : recv_procs_;
384 const ArrOfInt& sizes = (status_ == BEGIN_COMM) ? send_buf_sizes_ : recv_buf_sizes_;
385 const int n = procs.size_array();
386 int i;
387 char * ptr = sdata_.buffer_base_;
388 // If in reception phase, the start of the receive buffers is located at
389 // the end of the send buffers:
390 if (status_ != BEGIN_COMM)
391 {
392 const int nsend = send_procs_.size_array();
393 for (i = 0; i < nsend; i++)
394 ptr += send_buf_sizes_[i];
395 }
396 for (i = 0; i < n; i++)
397 {
398 ptr += sizes[i]; // pointer to the end of this processor's buffer
399 if (procs[i] == pe)
400 return (sdata_.buf_pointers_[pe] + byte_size) <= ptr;
401 }
402 // no buffer declared for this processor
403 return 0;
404}
: This class describes a group of processors on which
Definition Comm_Group.h:37
static int check_enabled()
Definition Comm_Group.h:154
virtual void send_recv_finish() const =0
int nproc() const
Returns the number of processors in the group *this.
Definition Comm_Group.h:185
virtual void send_recv_start(const ArrOfInt &send_list, const ArrOfInt &send_size, const char *const *const send_buffers, const ArrOfInt &recv_list, const ArrOfInt &recv_size, char *const *const recv_buffers, TypeHint typehint=CHAR) const =0
static const Comm_Group & current_group()
Returns a reference to the current active processor group.
Definition PE_Groups.h:64
static const Comm_Group & groupe_TRUST()
Returns a reference to the group containing all TRUST processors.
static Sortie & Journal(int message_level=0)
Returns a static Sortie object used as an event journal.
Definition Process.cpp:592
static int nproc()
Returns the number of processors in the current group. See Comm_Group::nproc() and PE_Groups::current...
Definition Process.cpp:102
static double mp_sum(double)
Computes the sum of x over all processors in the current group.
Definition Process.cpp:145
static void barrier()
Synchronizes all processors in the current group (waits until all processors have reached the barrier...
Definition Process.cpp:133
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
Static data shared by all Schema_Comm_Vecteur classes, with destructor to free memory at end of execu...
void init(int size, bool bufferOnDevice)
static Schema_Comm_Vecteurs_Static_Data sdata_
static ArrOfDouble tmp_area_double_
int check_next_area(int pe, int byte_size) const
Verifies that there are at least byte_size bytes remaining in the buffer of processor pe.
void end_init()
Once the data to exchange has been declared with add_send/recv_area_..(),.
void begin_init()
Resets buffer sizes.
int check_buffers_full() const
Depending on status_, verifies that all buffer pointers point to the end of the buffer allocated for ...
static ArrOfFloat tmp_area_float_
void exchange(IsExchangeBlocking exchange_type=IsExchangeBlocking::DefaultBlocking, const std::string kernel_name="noname")
void begin_comm(bool bufferOnDevice=false)
Starts a new data exchange (buffer sizes must have been initialized with begin_init()....
_SIZE_ size_array() const