TrioCFD 1.9.9_beta
TrioCFD documentation
Loading...
Searching...
No Matches
TRUSTArray.cpp
1/****************************************************************************
2* Copyright (c) 2025, 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
16#include <arch.h>
17#include <TRUSTArray.h>
18#include <string.h>
19#ifdef TRUST_USE_GPU
20#include <DeviceMemory.h>
21#endif
22
23#ifndef LATATOOLS
24#include <Perf_counters.h>
25#endif
26
27// TRUSTArray kernels for device moved in .cpp file to avoid multiple definition during link
28template <typename _TYPE_, typename _SIZE_>
30{
31#ifndef LATATOOLS
32 this->ensureDataOnHost();
33 _SIZE_ sz = size_array();
34 os << sz << finl;
35 if (sz > 0)
36 {
37 const _TYPE_* v = span_.data();
38 os.put(v,sz,sz);
39 }
40#endif
41 return os;
42}
43
44template <typename _TYPE_, typename _SIZE_>
46{
47#ifndef LATATOOLS
48 _SIZE_ sz;
49 is >> sz;
50 if (sz >= 0)
51 {
52 // Call to the method without precondition on the derived type (since readOn is virtual, other properties will be correctly initialised)
53 resize_array_(sz);
54 if (sz > 0)
55 {
56 _TYPE_* v = span_.data();
57 is.get(v,sz);
58 }
59 }
60 else
61 {
62 Cerr << "Error in TRUSTArray:readOn : size = " << sz << finl;
64 }
65#endif
66 return is;
67}
68
69
70/** Protected method for resize. Used by derived classes.
71 * Same as resize_array() with less checks.
72 *
73 * This is also where we deal with the STORAGE::TEMP_STORAGE capability, i.e. the Trav arrays.
74 * There memory is taken from a shared pool (TRUSTTravPool). This kind of array should never be
75 * used in 64bits, since Trav are meaningful when inside the timestepping (so the 32bit world after the
76 * Scatter isntruction).
77 */
78template <typename _TYPE_, typename _SIZE_>
79void TRUSTArray<_TYPE_, _SIZE_>::resize_array_(_SIZE_ new_size, RESIZE_OPTIONS opt)
80{
81 assert(new_size >= 0);
82
83 if (mem_ == nullptr)
84 {
85 if (!span_.empty()) // ref_data! We may pass here if just changing the shape of a tab
86 {
87 assert(size_array() == new_size);
88 return; // Nothing to do ...
89 }
90 // We avoid allocating for empty arrays ... those are typically situations where we will resize (with a non
91 // null size) just after, so the real allocation will be made at that point.
92 if(new_size == 0) return;
93
94 // First allocation - memory space should really be malloc'd:
95 if(storage_type_ == STORAGE::TEMP_STORAGE)
96 mem_ = TRUSTTravPool<_TYPE_>::GetFreeBlock((int)new_size);
97 else
98 mem_ = std::make_shared<Vector_>(Vector_(new_size));
99
100 span_ = Span_(*mem_);
101
102 // We should never have to worry about device allocation here:
103 if (isAllocatedOnDevice(mem_->data()))
104 data_location_ = std::make_shared<DataLocation>(DataLocation::Device);
105 else
106 data_location_ = std::make_shared<DataLocation>(DataLocation::HostOnly);
107
108 if(opt == RESIZE_OPTIONS::COPY_INIT)
109 operator=((_TYPE_)0); // To initialize on device or host
110 //std::fill(mem_->begin(), mem_->end(), (_TYPE_) 0);
111 }
112 else
113 {
114 // Array is already allocated, we want to resize:
115 // array must not be shared! (also checked in resize_array()) ... but, still, we allow passing here (i.e. no assert)
116 // only if we keep the same size_array(). This is for example invoked by TRUSTTab when just changing the overall shape of
117 // the array without modifying the total number of elems ...
118 _SIZE_ sz_arr = size_array();
119 if(new_size != sz_arr) // Yes, we compare to the span's size
120 {
121 assert(ref_count() == 1); // from here on, we *really* should not be shared
122
123 if (storage_type_ == STORAGE::TEMP_STORAGE)
124 {
125 // No 64b Trav:
126 assert( (std::is_same<trustIdType, int>::value || !std::is_same<_SIZE_, trustIdType>::value) );
127
128 // Resize of a Trav: if the underlying mem_ is already big enough, just update the span, and possibly fill with 0
129 // else, really increase memory allocation using the TRUSTTravPool.
130 _SIZE_ mem_sz = (_SIZE_)mem_->size();
131 if (new_size <= mem_sz)
132 {
133 // Cheat, simply update the span (up or down)
134 span_ = Span_(span_.begin(), span_.begin()+new_size);
135 // Possibly set to 0 extended part:
136 if (new_size > sz_arr && opt == RESIZE_OPTIONS::COPY_INIT)
137 {
138 ensureDataOnHost();
139 std::fill(span_.begin() + sz_arr, span_.end(), (_TYPE_) 0);
140 }
141 }
142 else // Real size increase of the underlying std::vector
143 {
144 // ResizeBlock
145 mem_ = TRUSTTravPool<_TYPE_>::ResizeBlock(mem_, (int)new_size);
146 span_ = Span_(*mem_);
147 if (opt == RESIZE_OPTIONS::COPY_INIT)
148 {
149 ensureDataOnHost();
150 std::fill(span_.begin() + sz_arr, span_.end(), (_TYPE_) 0);
151 }
152 }
153 }
154 else // Normal (non Trav) arrays
155 {
156#ifndef LATATOOLS
157 bool onDevice = isAllocatedOnDevice(*this);
158 if (onDevice)
159 {
160 // ToDo Kokkos: resize on device is not optimal for the moment as it makes 2 copy D2H and H2D
161 copyFromDevice(*this); // Force copy to host
162 _TYPE_ * prev_ad = span_.data(); // before resize!
163 deleteOnDevice(prev_ad, sz_arr); // Delete current block
164 set_data_location(DataLocation::HostOnly);
165 }
166#endif
167 mem_->resize(new_size);
168 span_ = Span_(*mem_);
169 // Possibly set to 0 extended part, since we have a custom Vector allocator not doing it by default (TVAlloc):
170 if (new_size > sz_arr && opt == RESIZE_OPTIONS::COPY_INIT)
171 std::fill(span_.begin()+sz_arr, span_.end(), (_TYPE_) 0);
172#ifndef LATATOOLS
173 if (onDevice)
174 {
175 // Re-allocate and copy on device:
176 mapToDevice(*this);
177 }
178#endif
179 }
180 }
181 }
182}
183
184/** Copies elements source[first_element_source + i] into elements (*this)[first_element_dest + i] for 0 <= i < nb_elements
185* The other elements of (*this) are left unchanged.
186
187* @param const ArrOfDouble& m: the array to use, must be different from *this !
188* @param _SIZE_ nb_elements: number of elements to copy, nb_elements >= -1. If nb_elements==-1, the entire array m is copied. Default value: -1
189* @param _SIZE_ first_element_dest. Default value: 0
190* @param _SIZE_ first_element_source. Default value: 0
191* @return ArrOfDouble& : *this
192* @throw Exits with an error if the size of array m is larger than the size of array this.
193*/
194template <typename _TYPE_, typename _SIZE_>
195TRUSTArray<_TYPE_, _SIZE_>& TRUSTArray<_TYPE_, _SIZE_>::inject_array(const TRUSTArray& source, _SIZE_ nb_elements, _SIZE_ first_element_dest, _SIZE_ first_element_source)
196{
197 assert(&source != this && nb_elements >= -1);
198 assert(first_element_dest >= 0 && first_element_source >= 0);
199
200 if (nb_elements < 0) nb_elements = source.size_array();
201
202 assert(first_element_source + nb_elements <= source.size_array());
203 assert(first_element_dest + nb_elements <= size_array());
204
205 if (nb_elements > 0)
206 {
207 bool kernelOnDevice = checkDataOnDevice(source);
208#ifndef LATATOOLS
209 if (statistics().get_use_gpu() && nb_elements>100) start_gpu_timer(__KERNEL_NAME__);
210#endif
211 if (kernelOnDevice)
212 {
213#ifndef LATATOOLS
214 const auto addr_source = source.view_ro<1>();
215 auto addr_dest = view_rw<1>();
216 Kokkos::parallel_for(__KERNEL_NAME__, nb_elements, KOKKOS_LAMBDA(const _SIZE_ i) { addr_dest[first_element_dest+i] = addr_source[first_element_source+i]; });
217#endif
218 }
219 else
221 // PL: We use memcpy because it is REALLY faster (10% faster on RNR_G20)
222 const _TYPE_ * addr_source = source.span_.data() + first_element_source;
223 _TYPE_ * addr_dest = span_.data() + first_element_dest;
224 memcpy(addr_dest, addr_source, nb_elements * sizeof(_TYPE_));
225#ifdef TRUST_USE_GPU
227 Cerr << "[Host] Filling a large TRUSTArray (" << nb_elements << " items) which is slow during a GPU run! Set a breakpoint to fix." << finl;
228#endif
229 }
230#ifndef LATATOOLS
231 if (statistics().get_use_gpu() && nb_elements>100) end_gpu_timer(__KERNEL_NAME__, kernelOnDevice);
232#endif
233 }
234 return *this;
235}
236
237template<typename _TYPE_, typename _SIZE_>
238template<typename _TAB_>
239void TRUSTArray<_TYPE_, _SIZE_>::ref_conv_helper_(_TAB_& out) const
240{
241 out.detach_array();
242 // Same as 'attach_array()', but since we are crossing templates parameters, we can not call it directly:
243 out.mem_ = mem_;
244 out.span_ = span_;
245 out.data_location_ = data_location_;
246 out.storage_type_ = storage_type_;
247}
248
249/*! @brief Conversion methods - from a small array (_SIZE_=int) of TID (_TYPE_=trustIdType), return a big one (_SIZE_=trustIdType).
250 * No data copied! This behaves somewhat like a ref_array. Used in LATA stuff notably. Not implemented for _TYPE_=double or float
251 * (because never needed).
252 * @param out the output big array that will reference the same data
253 */
254template<>
256{
257 ref_conv_helper_(out);
258}
259
260template<typename _TYPE_, typename _SIZE_>
262{
263 // Should no be used for anything else than specialisations listed above.
264 assert(false);
265 Process::exit("TRUSTArray<>::ref_as_big() should not be used with those current template types.");
266}
267
268/*! @brief Conversion methods - from a big array (_SIZE_=trustIdType), return a small one (_SIZE_=int).
269 * Overflow is detected in debug if array is too big to be fit into _SIZE_=int.
270 * No data copied! This behaves somewhat like a ref_array. Used in LATA stuff and FT notably.
271 * @param out the output small array that will reference the same data
272 */
273template<>
275{
276 // Check size fits in 32bits:
277 assert(size_array() < std::numeric_limits<int>::max());
278 ref_conv_helper_(out);
279}
280
281template<>
283{
284 // Check size fits in 32bits:
285 assert(size_array() < std::numeric_limits<int>::max());
286 ref_conv_helper_(out);
287}
288
289template<typename _TYPE_, typename _SIZE_>
291{
292 // Should no be used for anything else than specialisations listed above.
293 assert(false);
294 Process::exit("TRUSTArray<>::ref_as_big() should not be used with those current template types.");
295}
296
297/*! @brief Conversion from a BigArrOfTID to an ArrOfInt. Careful, it always does a copy! It is your responsibility
298 * to invoke it only when necessary (typically you should avoid this when trustIdType == int ...)
299 * @param out the output ArrOfInt filled with the converted values
300 */
301template<>
303{
304 // Not too big?
305 assert(size_array() < std::numeric_limits<int>::max());
306 int sz_int = (int)size_array(); // we may cast!
307 out.resize_array_(sz_int); // the one with '_' skipping the checks, so we can be called from Tab too
308 if (sz_int)
309 {
310 // All values within int range?
311 assert(( *std::min_element(span_.begin(), span_.end()) > std::numeric_limits<int>::min() ));
312 assert(( *std::max_element(span_.begin(), span_.end()) < std::numeric_limits<int>::max() ));
313 }
314 // Yes, copy:
315 std::copy(span_.begin(), span_.end(), out.span_.begin());
316}
317
318template<typename _TYPE_, typename _SIZE_>
320{
321 // Should no be used for anything else than specialisations listed above.
322 assert(false);
323 Process::exit("TRUSTArray<>::from_tid_to_int() should not be used with those current template types.");
324}
325
326
327/** Fills the array with the value x passed as parameter (x is assigned to every element of the array)
328 */
329template <typename _TYPE_, typename _SIZE_>
331{
332 const _SIZE_ size = size_array();
333 bool kernelOnDevice = checkDataOnDevice();
334#ifndef LATATOOLS
335 if (statistics().get_use_gpu() && size>100) start_gpu_timer(__KERNEL_NAME__);
336#endif
337 if (kernelOnDevice)
338 {
339#ifndef LATATOOLS
340 auto data = view_rw<1>();
341 Kokkos::parallel_for(__KERNEL_NAME__, size, KOKKOS_LAMBDA(const int i) { data[i] = x; });
342#endif
343 }
344 else
345 {
346 _TYPE_ *data = span_.data();
347 for (_SIZE_ i = 0; i < size; i++) data[i] = x;
348 }
349#ifndef LATATOOLS
350 if (statistics().get_use_gpu() && size>100) end_gpu_timer(__KERNEL_NAME__, kernelOnDevice);
351#endif
352 return *this;
353}
354
355/** Element-wise addition over all elements of the array: the size of y must be at least equal to the size of this
356 */
357template <typename _TYPE_, typename _SIZE_>
359{
360 assert(size_array()==y.size_array());
361 _SIZE_ size = size_array();
362 bool kernelOnDevice = checkDataOnDevice(y);
363#ifndef LATATOOLS
364 if (statistics().get_use_gpu() && size>100) start_gpu_timer(__KERNEL_NAME__);
365#endif
366 if (kernelOnDevice)
367 {
368#ifndef LATATOOLS
369 const auto dy = y.view_ro<1>();
370 auto dx = view_rw<1>();
371 Kokkos::parallel_for(__KERNEL_NAME__, size, KOKKOS_LAMBDA(const _SIZE_ i) { dx[i] += dy[i]; });
372#endif
373 }
374 else
375 {
376 const _TYPE_* dy = y.span_.data();
377 _TYPE_* dx = span_.data();
378 for (_SIZE_ i = 0; i < size; i++) dx[i] += dy[i];
379 }
380#ifndef LATATOOLS
381 if (statistics().get_use_gpu() && size>100) end_gpu_timer(__KERNEL_NAME__, kernelOnDevice);
382#endif
383 return *this;
384}
385
386/** Adds the same value to every element of the array
387 */
388template <typename _TYPE_, typename _SIZE_>
390{
391 _SIZE_ size = size_array();
392 bool kernelOnDevice = checkDataOnDevice();
393#ifndef LATATOOLS
394 if (statistics().get_use_gpu() && size>100) start_gpu_timer(__KERNEL_NAME__);
395#endif
396 if (kernelOnDevice)
397 {
398#ifndef LATATOOLS
399 auto data = view_rw<1>();
400 Kokkos::parallel_for(__KERNEL_NAME__, size, KOKKOS_LAMBDA(const _SIZE_ i) { data[i] += dy; });
401#endif
402 }
403 else
404 {
405 _TYPE_ *data = span_.data();
406 for(_SIZE_ i = 0; i < size; i++) data[i] += dy;
407 }
408#ifndef LATATOOLS
409 if (statistics().get_use_gpu() && size>100) end_gpu_timer(__KERNEL_NAME__, kernelOnDevice);
410#endif
411 return *this;
412}
413
414/** Element-wise subtraction over all elements of the array: array must be the same size as *this
415 */
416template <typename _TYPE_, typename _SIZE_>
418{
419 assert(size_array() == y.size_array());
420 _SIZE_ size = size_array();
421 bool kernelOnDevice = checkDataOnDevice(y);
422#ifndef LATATOOLS
423 if (statistics().get_use_gpu() && size>100) start_gpu_timer(__KERNEL_NAME__);
424#endif
425 if (kernelOnDevice)
426 {
427#ifndef LATATOOLS
428 auto data = view_rw<1>();
429 const auto data_y = y.view_ro<1>();
430 Kokkos::parallel_for(__KERNEL_NAME__, size, KOKKOS_LAMBDA(const _SIZE_ i) { data[i] -= data_y[i]; });
431#endif
432 }
433 else
434 {
435 _TYPE_ * data = span_.data();
436 const _TYPE_ * data_y = y.span_.data();
437 for (_SIZE_ i = 0; i < size; i++) data[i] -= data_y[i];
438 }
439#ifndef LATATOOLS
440 if (statistics().get_use_gpu() && size>100) end_gpu_timer(__KERNEL_NAME__, kernelOnDevice);
441#endif
442 return *this;
443}
444
445/** soustrait la meme valeur a toutes les cases
446 */
447template <typename _TYPE_, typename _SIZE_>
449{
450 operator+=(-dy);
451 return *this;
452}
453
454/** muliplie toutes les cases par dy
455 */
456template <typename _TYPE_, typename _SIZE_>
458{
459 _SIZE_ size = size_array();
460 bool kernelOnDevice = checkDataOnDevice();
461#ifndef LATATOOLS
462 if (statistics().get_use_gpu() && size>100) start_gpu_timer(__KERNEL_NAME__);
463#endif
464 if (kernelOnDevice)
465 {
466#ifndef LATATOOLS
467 auto data = view_rw<1>();
468 Kokkos::parallel_for(__KERNEL_NAME__, size, KOKKOS_LAMBDA(const _SIZE_ i) { data[i] *= dy; });
469#endif
470 }
471 else
472 {
473 _TYPE_ *data = span_.data();
474 for(_SIZE_ i=0; i < size; i++) data[i] *= dy;
475 }
476#ifndef LATATOOLS
477 if (statistics().get_use_gpu() && size>100) end_gpu_timer(__KERNEL_NAME__, kernelOnDevice);
478#endif
479 return *this;
480}
481
482/** divise toutes les cases par dy (pas pour TRUSTArray<int>)
483 */
484template <typename _TYPE_, typename _SIZE_>
486{
487 if (std::is_integral<_TYPE_>::value) throw; // division should not be called on integral types.
488 operator*=(1/dy);
489 return *this;
490}
491
492// Pour instancier les methodes templates dans un .cpp
493template class TRUSTArray<double, int>;
494template class TRUSTArray<int, int>;
495template class TRUSTArray<float, int>;
496
497#if INT_is_64_ == 2
499template class TRUSTArray<int, trustIdType>;
501template class TRUSTArray<trustIdType, int>;
502template class TRUSTArray<float, trustIdType>;
503#endif
virtual int get(int *ob, std::streamsize n)
Definition Entree.cpp:222
friend class Entree
Definition Objet_U.h:71
friend class Sortie
Definition Objet_U.h:70
static void exit(int exit_code=-1)
Exit routine for TRUST within a Kokkos region.
Definition Process.cpp:466
static int je_suis_maitre()
Returns 1 if on the master processor of the current group (i.e. me() == 0), 0 otherwise.
Definition Process.cpp:82
virtual int put(const unsigned *ob, std::streamsize n, std::streamsize nb_colonnes=1)
Definition Sortie.cpp:101
Represents a an array of int/int64/double/... values.
Definition TRUSTArray.h:81
void from_tid_to_int(TRUSTArray< int, int > &out) const
void resize_array_(_SIZE_ n, RESIZE_OPTIONS opt=RESIZE_OPTIONS::COPY_INIT)
TRUSTArray & operator*=(const _TYPE_ dy)
_SIZE_ size_array() const
TRUSTArray & operator/=(const _TYPE_ dy)
TRUSTArray & inject_array(const TRUSTArray &source, _SIZE_ nb_elements=-1, _SIZE_ first_element_dest=0, _SIZE_ first_element_source=0)
void ref_as_big(TRUSTArray< _TYPE_, trustIdType > &out) const
TRUSTArray & operator+=(const TRUSTArray &y)
TRUSTArray & operator-=(const TRUSTArray &y)
void ref_as_small(TRUSTArray< _TYPE_, int > &out) const
std::vector< int, TVAlloc< int > > Vector_
Definition TRUSTArray.h:101
tcb::span< int > Span_
Definition TRUSTArray.h:102
TRUSTArray & operator=(const TRUSTArray &)
Entree & readOn(Entree &is) override
Reads an Objet_U from an input stream. Virtual method to override.
friend class TRUSTArray
Definition TRUSTArray.h:108
Sortie & printOn(Sortie &os) const override
Writes the object to an output stream. Virtual method to override.
static block_ptr_t GetFreeBlock(int sz)
Retrieve a free block of size sz.
static block_ptr_t ResizeBlock(block_ptr_t p, int new_sz)
"Resize" a temporary Trav block - two possible strategies: Strategy 1
static bool warning(trustIdType nb_items)