Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
PapiPerformanceService.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* PapiPerformanceService.cc (C) 2000-2024 */
9/* */
10/* Performance information using PAPI. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/std/PapiPerformanceService.h"
15
16#include "arcane/utils/ValueConvert.h"
17#include "arcane/utils/PlatformUtils.h"
18#include "arcane/utils/FatalErrorException.h"
19#include "arcane/utils/CriticalSection.h"
20#include "arcane/utils/IPerformanceCounterService.h"
21
22#include "arcane/core/FactoryService.h"
23#include "arcane/core/IParallelSuperMng.h"
24
25#include "arcane/std/ProfilingInfo.h"
26#include "arcane/impl/TimerMng.h"
27
28#include <map>
29#include <set>
30
31/*---------------------------------------------------------------------------*/
32/*---------------------------------------------------------------------------*/
33
34namespace Arcane
35{
36
37namespace
38{
39 /*
40 * Checks if PAPI_library_init() has been called, and if not,
41 * calls this method.
42 *
43 * Note that PAPI_library_init() can only be called once.
44 */
45 void
46 _checkInitPAPI()
47 {
48 if (PAPI_is_initialized() == PAPI_NOT_INITED) {
49 int retval = PAPI_library_init(PAPI_VER_CURRENT);
50 if (retval != PAPI_VER_CURRENT && retval > 0)
51 ARCANE_FATAL("PAPI version mismatch r={0} current={1}", retval, PAPI_VER_CURRENT);
52 if (retval < 0)
53 ARCANE_FATAL("Error in PAPI_library_init r={0} msg={1}", retval, PAPI_strerror(retval));
54 }
55 }
56
57} // namespace
58/*---------------------------------------------------------------------------*/
59/*---------------------------------------------------------------------------*/
60
62 ServiceProperty("PapiProfilingService", ST_Application),
64
65/*---------------------------------------------------------------------------*/
66/*---------------------------------------------------------------------------*/
67
68/*
69 * TODO: With threads, use one instance per subdomain
70 * and use PAPI_register_thread and PAPI_unregister_thread.
71 * (see example papi overflow_pthreads.c).
72 */
73
74/*---------------------------------------------------------------------------*/
75/*---------------------------------------------------------------------------*/
76
77PapiPerformanceService::
78PapiPerformanceService(const ServiceBuildInfo& sbi)
79: AbstractService(sbi)
80, m_period(500000)
81, m_event_set(PAPI_NULL)
82, m_application(sbi.application())
83{
84}
85
86/*---------------------------------------------------------------------------*/
87/*---------------------------------------------------------------------------*/
88
89PapiPerformanceService::
90~PapiPerformanceService()
91{
92 delete m_timer_mng;
93}
94
95/*---------------------------------------------------------------------------*/
96/*---------------------------------------------------------------------------*/
97
98namespace
99{
100 ProfInfos* global_infos = nullptr;
101 int global_nb_total_call = 0;
102} // namespace
103
104/*---------------------------------------------------------------------------*/
105/*---------------------------------------------------------------------------*/
106
107void PapiPerformanceService::
108arcane_papi_handler(int EventSet, void* address, long_long overflow_vector, void* context)
109{
110 ARCANE_UNUSED(context);
111 static bool is_in_handler = false;
112 // On Linux with gcc, exceptions use libunwind contained
113 // in gcc and this can cause deadlocks with our usage
114 // if this handler is called during exception unwinding.
115 // To avoid this problem, we do nothing as long as an exception is
116 // active.
117 if (Exception::hasPendingException()) {
118 cout << "** WARNING: PapiHandler in pending exception\n";
119 return;
120 }
121 if (is_in_handler)
122 return;
123
124 is_in_handler = true;
125 ++global_nb_total_call;
126
127 int overflow_event[MAX_COUNTER];
128 int nb_overflow_event = MAX_COUNTER;
129 PAPI_get_overflow_event_index(EventSet, overflow_vector, overflow_event, &nb_overflow_event);
130 global_infos->addEvent(address, overflow_event, nb_overflow_event);
131 is_in_handler = false;
132}
133
134/*---------------------------------------------------------------------------*/
135/*---------------------------------------------------------------------------*/
136
137bool PapiPerformanceService::
138_addEvent(int event_code, int event_index)
139{
140 char event_name[PAPI_MAX_STR_LEN];
141 //int event_code = PAPI_TOT_CYC;
142 int retval = PAPI_add_event(m_event_set, event_code);
143 PAPI_event_code_to_name(event_code, event_name);
144 info() << "Adding Papi event name=" << event_name;
145 if (retval != PAPI_OK) {
146 pwarning() << "** ERROR in add_event (index=" << event_index << ") r=" << retval
147 << " msg=" << PAPI_strerror(retval);
148 return false;
149 }
150 return true;
151}
152
153/*---------------------------------------------------------------------------*/
154/*---------------------------------------------------------------------------*/
155
156void PapiPerformanceService::
157initialize()
158{
159 CriticalSection cs(m_application->parallelSuperMng()->threadMng());
160 if (m_is_initialized)
161 return;
162 if (m_is_running)
163 return;
164
165 m_is_initialized = true;
166
167 int major = PAPI_VERSION_MAJOR(PAPI_VERSION);
168 int minor = PAPI_VERSION_MINOR(PAPI_VERSION);
169 int sub_minor = PAPI_VERSION_REVISION(PAPI_VERSION);
170 info() << "PROFILING: start profiling using 'PAPI' version="
171 << major << "." << minor << "." << sub_minor;
172
173 if (!global_infos)
174 global_infos = new ProfInfos(traceMng());
175
176 global_infos->setNbEventBeforeGettingStack(2000);
177 global_infos->setFunctionDepth(5);
178
179 int retval = 0;
180 caddr_t start, end;
181 const PAPI_exe_info_t* prginfo;
182
183 _checkInitPAPI();
184
185 retval = PAPI_thread_init((unsigned long (*)(void))(pthread_self));
186 if (retval != PAPI_OK)
187 ARCANE_FATAL("Error in PAPI_thread_init r={0} msg={1}", retval, PAPI_strerror(retval));
188
189 prginfo = PAPI_get_executable_info();
190
191 start = reinterpret_cast<caddr_t>(prginfo->address_info.text_start);
192 end = reinterpret_cast<caddr_t>(prginfo->address_info.text_end);
193
194 info() << "** PROGRAM INFOS: start=" << (long)start << " end=" << (long)end << " length=" << (end - start);
195
196 if ((retval = PAPI_create_eventset(&m_event_set)) != PAPI_OK)
197 ARCANE_FATAL("ERROR in PAPI_create_eventset r={0}", retval);
198
199 const int NB_EVENT = 3;
200 int papi_events[NB_EVENT];
201
202 // Event 0 must always be PAPI_TOT_CYC because we use it
203 // for statistics
204 papi_events[0] = PAPI_TOT_CYC;
205 // TODO: check if these events are supported by the processor
206 papi_events[1] = PAPI_RES_STL;
207 papi_events[2] = PAPI_DP_OPS;
208
209 String papi_user_events = platform::getEnvironmentVariable("ARCANE_PAPI_EVENTS");
210 int nb_event = NB_EVENT;
211 if (!papi_user_events.null()) {
213 papi_user_events.split(strs, ':');
214 int nb_str = strs.size();
215 nb_str = math::min(NB_EVENT - 1, nb_str);
216 for (Integer i = 0; i < nb_str; ++i) {
217 const String& ename = strs[i];
218 info() << "USER_EVENT name=" << ename;
219 int new_event;
220 int retval = PAPI_event_name_to_code((char*)ename.localstr(), &new_event);
221 if (retval != PAPI_OK) {
222 pwarning() << "Can not set event from name=" << ename << " r=" << retval;
223 }
224 else
225 papi_events[i + 1] = new_event;
226 }
227 nb_event = nb_str + 1;
228 }
229 bool is_valid_event[NB_EVENT];
230
231 for (Integer i = 0; i < nb_event; ++i)
232 is_valid_event[i] = _addEvent(papi_events[i], i);
233
234 //int period = 500000;
235 String period_str = platform::getEnvironmentVariable("ARCANE_PROFILING_PERIOD");
236 if (!period_str.null()) {
237 bool is_bad = builtInGetValue(m_period, period_str);
238 if (is_bad) {
239 pwarning() << "Can not convert '" << period_str << "' to int";
240 }
241 }
242 String only_str = platform::getEnvironmentVariable("ARCANE_PROFILING_ONLYFLOPS");
243 if (!only_str.null())
244 m_only_flops = true;
245
246 if (m_only_flops) {
247 _printFlops();
248 }
249 else {
250 // We must not let the period be too small otherwise we spend
251 // all the time in the processing of 'arcane_papi_handler'.
252 if (m_period < 100000)
253 m_period = 100000;
254 for (Integer i = 0; i < nb_event; ++i) {
255 if (is_valid_event[i]) {
256 retval = PAPI_overflow(m_event_set, papi_events[i], m_period, 0, arcane_papi_handler);
257 if (retval != PAPI_OK) {
258 // The PAPI_TOT_CYC event is indispensable
259 if (i == 0) {
260 fatal() << "** ERROR in papi_overflow i=" << i << " r=" << retval
261 << " msg=" << PAPI_strerror(retval);
262 }
263 else
264 pwarning() << "** ERROR in papi_overflow i=" << i << " r=" << retval
265 << " msg=" << PAPI_strerror(retval);
266 is_valid_event[i] = false;
267 }
268 }
269 }
270 info() << "Sampling period: (in events) " << m_period;
271 }
272}
273
274/*---------------------------------------------------------------------------*/
275/*---------------------------------------------------------------------------*/
276
277void PapiPerformanceService::
278startProfiling()
279{
280 if (m_only_flops)
281 return;
282 ARCANE_CHECK_POINTER(global_infos);
283 global_infos->startProfiling();
284 if (!m_is_running) {
285 int retval = PAPI_start(m_event_set);
286 if (retval != PAPI_OK) {
287 ARCANE_FATAL("** ERROR in papi_start r={0}", retval);
288 }
289 m_is_running = true;
290 }
291}
292
293/*---------------------------------------------------------------------------*/
294/*---------------------------------------------------------------------------*/
295
296void PapiPerformanceService::
297switchEvent()
298{
299}
300
301/*---------------------------------------------------------------------------*/
302/*---------------------------------------------------------------------------*/
303
304void PapiPerformanceService::
305_printFlops()
306{
307 float real_time = 0.0f;
308 float proc_time = 0.0f;
309 long long flpins = 0.0;
310 float mflops = 0.0f;
311 // Starting from PAPI 6.0, there is no longer PAPI_flops but instead
312 // it is 'PAPI_flops_rate' but there is an additional argument
313 // to specify the type of flop to calculate (single precision,
314 // double precision, ...)
315#if PAPI_VERSION >= PAPI_VERSION_NUMBER(6, 0, 0, 0)
316 int retval = PAPI_flops_rate(PAPI_DP_OPS, &real_time, &proc_time, &flpins, &mflops);
317#else
318 int retval = PAPI_flops(&real_time, &proc_time, &flpins, &mflops);
319#endif
320 if (retval != PAPI_OK)
321 error() << "** ERROR in PAPI_flops r=" << retval;
322 else {
323 info() << "PAPI_Flops: real_time=" << real_time
324 << " proc_time=" << proc_time
325 << " flpins=" << flpins
326 << " mflips=" << mflops;
327 }
328}
329
330/*---------------------------------------------------------------------------*/
331/*---------------------------------------------------------------------------*/
332
333void PapiPerformanceService::
334stopProfiling()
335{
336 if (!global_infos)
337 return;
338
339 if (m_only_flops) {
340 _printFlops();
341 return;
342 }
343 CriticalSection cs(m_application->parallelSuperMng()->threadMng());
344
345 //info() << "PROFILING: stop profiling nb_call=" << global_nb_total_call;
346 if (m_is_running) {
347 int retval = PAPI_stop(m_event_set, 0);
348 if (retval != PAPI_OK) {
349 ARCANE_FATAL("** ERROR in papi_stop r={0}", retval);
350 }
351 m_is_running = false;
352 }
353 global_infos->stopProfiling();
354}
355
356/*---------------------------------------------------------------------------*/
357/*---------------------------------------------------------------------------*/
358
359void PapiPerformanceService::
360printInfos(bool dump_file)
361{
362 if (global_infos)
363 global_infos->printInfos(dump_file);
364}
365
366/*---------------------------------------------------------------------------*/
367/*---------------------------------------------------------------------------*/
368
369void PapiPerformanceService::
370dumpJSON(JSONWriter& writer)
371{
372 if (global_infos)
373 global_infos->dumpJSON(writer);
374}
375
376/*---------------------------------------------------------------------------*/
377/*---------------------------------------------------------------------------*/
378
379void PapiPerformanceService::
380getInfos(Int64Array& array)
381{
382 if (global_infos)
383 global_infos->getInfos(array);
384}
385
386/*---------------------------------------------------------------------------*/
387/*---------------------------------------------------------------------------*/
388
389void PapiPerformanceService::
390reset()
391{
392 if (global_infos)
393 global_infos->reset();
394}
395
396/*---------------------------------------------------------------------------*/
397/*---------------------------------------------------------------------------*/
398
399/*---------------------------------------------------------------------------*/
400/*---------------------------------------------------------------------------*/
401
402class PapiTimerMng
403: public TimerMng
404{
405 public:
406
407 explicit PapiTimerMng(ITraceMng* tm)
408 : TimerMng(tm)
409 , m_nb_event(0)
410 , m_event_set(PAPI_NULL)
411 , m_is_started(false)
412 , m_is_init(false)
413 , m_elapsed_us(0)
414 , m_elapsed_cycle()
415 {}
416 ~PapiTimerMng()
417 {
418 if (m_is_started)
419 PAPI_stop(m_event_set, m_values.data());
420 }
421
422 public:
423
424 void init();
425 void _addEvent(int event);
426 void start();
427 Real stop(const char* msg);
428
431 {
432 return stop("test");
433 }
434
436 void _setRealTime() override
437 {
438 if (!m_is_init) {
439 m_is_init = true;
440 init();
441 start();
442 }
443 }
444
445 private:
446
447 int m_nb_event;
448 int m_event_set;
449 bool m_is_started;
450 bool m_is_init;
451 UniqueArray<long_long> m_values;
452 UniqueArray<long_long> m_start_values;
453 long_long m_elapsed_us;
454 long_long m_elapsed_cycle;
455};
456
457/*---------------------------------------------------------------------------*/
458/*---------------------------------------------------------------------------*/
459
460void PapiTimerMng::
461init()
462{
463 _checkInitPAPI();
464
465 int retval = PAPI_thread_init((unsigned long (*)(void))(pthread_self));
466 if (retval != PAPI_OK)
467 ARCANE_FATAL("PAPI_thread_init r={0}", retval);
468
469 retval = PAPI_create_eventset(&m_event_set);
470 if (retval != PAPI_OK)
471 ARCANE_FATAL("PAPI_create_eventset r={0}", retval);
472
473 _addEvent(PAPI_TOT_CYC);
474 _addEvent(PAPI_DP_OPS);
475 m_values.resize(m_nb_event);
476 m_values.fill(0);
477 m_start_values.resize(m_nb_event);
478 m_start_values.fill(0);
479
480 retval = PAPI_start(m_event_set);
481 if (retval != PAPI_OK)
482 ARCANE_FATAL("PAPI_start r={0}", retval);
483 m_is_started = true;
484}
485
486/*---------------------------------------------------------------------------*/
487/*---------------------------------------------------------------------------*/
488
489void PapiTimerMng::
490_addEvent(int event)
491{
492 int retval = PAPI_add_event(m_event_set, event);
493 if (retval != PAPI_OK) {
494 cerr << "** CAN NOT FIND EVENT " << event << '\n';
495 return;
496 }
497 ++m_nb_event;
498}
499
500/*---------------------------------------------------------------------------*/
501/*---------------------------------------------------------------------------*/
502
503void PapiTimerMng::
504start()
505{
506 int retval = PAPI_read(m_event_set, m_start_values.data());
507 if (retval != PAPI_OK) {
508 cerr << "** CAN NOT START EVENT\n";
509 }
510 m_elapsed_us = PAPI_get_real_usec();
511 m_elapsed_cycle = PAPI_get_real_cyc();
512}
513
514/*---------------------------------------------------------------------------*/
515/*---------------------------------------------------------------------------*/
516
517Real PapiTimerMng::
518stop(const char* msg)
519{
520 int retval = PAPI_read(m_event_set, m_values.data());
521 if (retval != PAPI_OK) {
522 cerr << "** CAN NOT STOP EVENT\n";
523 }
524 long_long elapsed_us = PAPI_get_real_usec() - m_elapsed_us;
525 //long_long elapsed_cycle = PAPI_get_real_cyc() - m_elapsed_cycle;
526
527 double sec_time = ((double)elapsed_us) / 1.0e6;
528
529 std::cout << " -- -- Time: ";
530 std::cout.width(60);
531 std::cout << msg << " = ";
532 std::cout.width(10);
533 std::cout << sec_time << " ";
534
535 //cout << "** TIME: " << (elapsed_us) << " CYCLE=" << elapsed_cycle << '\n';
536
537 long_long nb_cycle = m_values[0] - m_start_values[0];
538 long_long nb_flop = m_values[1] - m_start_values[1];
539
540 std::cout.width(15);
541 std::cout << nb_cycle << " ";
542 std::cout.width(12);
543 std::cout << nb_flop;
544
545 std::cout << " (";
546 std::cout.width(5);
547 std::cout << nb_flop / elapsed_us;
548 std::cout << ")";
549
550 std::cout << '\n';
551
552 //for( Integer i=0, is=m_values.size() ; i<is; ++i ){
553 // long_long diff = (m_values[i]-m_start_values[i]);
554 // cout << "** EVENT: " << diff << " FLOP=" << (diff/elapsed_us) << '\n';
555 //}
556 return sec_time;
557}
558
559/*---------------------------------------------------------------------------*/
560/*---------------------------------------------------------------------------*/
561
562/*---------------------------------------------------------------------------*/
563/*---------------------------------------------------------------------------*/
564
565ITimerMng* PapiPerformanceService::
566timerMng()
567{
568 if (!m_timer_mng)
569 m_timer_mng = new PapiTimerMng(traceMng());
570 return m_timer_mng;
571}
572
573/*---------------------------------------------------------------------------*/
574/*---------------------------------------------------------------------------*/
575
576/*---------------------------------------------------------------------------*/
577/*---------------------------------------------------------------------------*/
578
579class PapiPerformanceCounterService
580: public TraceAccessor
582{
583 public:
584
585 PapiPerformanceCounterService(const ServiceBuildInfo& sbi)
587 , m_nb_event(0)
588 , m_event_set(PAPI_NULL)
589 , m_is_started(false)
590 {
591 }
592 ~PapiPerformanceCounterService()
593 {
594 if (m_is_started)
595 (void)PAPI_stop(m_event_set, nullptr);
596 }
597
598 public:
599
600 void build()
601 {
602 }
603
604 void initialize() override
605 {
606 int retval = 0;
607
608 _checkInitPAPI();
609
610 retval = PAPI_thread_init((unsigned long (*)(void))(pthread_self));
611 if (retval != PAPI_OK)
612 ARCANE_FATAL("Error in 'PAPI_thread_init' r={0}", retval);
613
614 //int event_mask = MASK_FP_OPS | MASK_L2_TCM | MASK_TOT_CYC;
615 //m_event_set = _makeEventSet(&num_events,&event_mask);
616 retval = PAPI_create_eventset(&m_event_set);
617 if (retval != PAPI_OK)
618 ARCANE_FATAL("Error in 'PAPI_createeventset' r={0}", retval);
619
620 _addEvent(PAPI_TOT_CYC);
621 _addEvent(PAPI_RES_STL);
622 _addEvent(PAPI_L2_TCM);
623 }
624 void _addEvent(int event)
625 {
626 int retval = PAPI_add_event(m_event_set, event);
627 if (retval != PAPI_OK) {
628 error() << "** CAN NOT FIND EVENT " << event << '\n';
629 return;
630 }
631 ++m_nb_event;
632 }
633 void start() final
634 {
635 if (m_is_started)
636 ARCANE_FATAL("start() has alredy been called");
637 int retval = PAPI_start(m_event_set);
638 if (retval != PAPI_OK)
639 ARCANE_FATAL("Error in 'PAPI_start' r={0}", retval);
640 m_is_started = true;
641 }
642 void stop() final
643 {
644 if (!m_is_started)
645 ARCANE_FATAL("start() has not been called");
646 int retval = PAPI_stop(m_event_set, nullptr);
647 if (retval != PAPI_OK)
648 ARCANE_FATAL("Error in 'PAPI_stop' r={0}", retval);
649 m_is_started = false;
650 }
651 bool isStarted() const final
652 {
653 return m_is_started;
654 }
655
656 Integer getCounters(Int64ArrayView counters, bool do_substract) final
657 {
658 long_long values[MIN_COUNTER_SIZE];
659 int retval = PAPI_read(m_event_set, values);
660 if (retval != PAPI_OK) {
661 error() << "Error in 'PAPI_read' during getCounters() r=" << retval;
662 }
663 Integer n = m_nb_event;
664 if (do_substract) {
665 for (int i = 0; i < n; ++i)
666 counters[i] = (Int64)values[i] - counters[i];
667 }
668 else
669 for (int i = 0; i < n; ++i)
670 counters[i] = values[i];
671 return n;
672 }
673
675 {
676 std::array<Int64, MIN_COUNTER_SIZE> values;
677 Int64ArrayView view(values);
678 getCounters(view, false);
679 return view[0];
680 }
681
682 private:
683
684 int m_nb_event = 0;
685 int m_event_set = 0;
686 bool m_is_started = false;
687};
688
689/*---------------------------------------------------------------------------*/
690/*---------------------------------------------------------------------------*/
691
692ARCANE_REGISTER_SERVICE(PapiPerformanceCounterService,
693 ServiceProperty("PapiPerformanceCounterService", ST_Application),
694 ARCANE_SERVICE_INTERFACE(IPerformanceCounterService));
695
696/*---------------------------------------------------------------------------*/
697/*---------------------------------------------------------------------------*/
698
699} // End namespace Arcane
700
701/*---------------------------------------------------------------------------*/
702/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro returning the pointer ptr if it is not null or throwing an exception if it is null.
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro to declare an interface when registering a service.
Integer size() const
Number of elements in the vector.
Base class of a service.
Critical section in multi-thread.
virtual ITraceMng * traceMng() const =0
Trace manager.
Interface of a service for accessing performance counters.
static const int MIN_COUNTER_SIZE
Minimum size of the view for getCounters().
Interface of a profiling service.
Interface of a timer manager.
Definition ITimerMng.h:50
void stop() final
Stops tracking performance counters.
void start() final
Starts tracking performance counters.
void initialize() override
Initializes the service.
Integer getCounters(Int64ArrayView counters, bool do_substract) final
Retrieves the current values of the counters.
Int64 getCycles() final
Value of the counter for the number of CPU cycles.
bool isStarted() const final
Indicates if the service has started (start() has been called).
Profiling service using the PAPI library.
Real _getRealTime() override
Returns the real time.
void _setRealTime() override
Sets a real timer.
IApplication * application() const
Access to the associated IApplication.
Structure containing the information to create a service.
Service creation properties.
bool null() const
Returns true if the string is null.
Definition String.cc:306
const char * localstr() const
Returns the conversion of the instance into UTF-8 encoding.
Definition String.cc:229
void split(StringContainer &str_array, char c) const
Splits the string based on the character c.
TimerMng(ITraceMng *msg)
Constructs a timer linked to the manager mng.
Definition TimerMng.cc:39
TraceAccessor(ITraceMng *m)
Constructs an accessor via the trace manager m.
TraceMessage fatal() const
Flow for a fatal error message.
TraceMessage info() const
Flow for an information message.
TraceMessage error() const
Flow for an error message.
ITraceMng * traceMng() const
Trace manager.
TraceMessage pwarning() const
1D data vector with value semantics (STL style).
__host__ __device__ Real2 min(Real2 a, Real2 b)
Returns the minimum of two Real2.
Definition MathUtils.h:346
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro for registering a service.
String getEnvironmentVariable(const String &name)
Environment variable named name.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Array< Int64 > Int64Array
Dynamic one-dimensional array of 64-bit integers.
Definition UtilsTypes.h:125
ArrayView< Int64 > Int64ArrayView
C equivalent of a 1D array of 64-bit integers.
Definition UtilsTypes.h:451
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
@ ST_Application
The service is used at the application level.
double Real
Type representing a real number.
UniqueArray< String > StringUniqueArray
Dynamic 1D array of strings.
Definition UtilsTypes.h:359