Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
PapiPerformanceService.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2024 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/* Informations de performances utilisant 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/FactoryService.h"
23#include "arcane/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 * Vérifie si PAPI_library_init() a été appelé et si ce n'est pas le cas,
41 * appelle cette méthode.
42 *
43 * A noter que PAPI_library_init() ne peut être appelé qu'une seule fois.
44 */
45void
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}
58/*---------------------------------------------------------------------------*/
59/*---------------------------------------------------------------------------*/
60
61ARCANE_REGISTER_SERVICE(PapiPerformanceService,
62 ServiceProperty("PapiProfilingService",ST_Application),
63 ARCANE_SERVICE_INTERFACE(IProfilingService));
64
65/*---------------------------------------------------------------------------*/
66/*---------------------------------------------------------------------------*/
67
68/*
69 * TODO: Avec les threads, utiliser une instance par sous-domaine
70 * et utiliser PAPI_register_thread et PAPI_unregister_thread.
71 * (voir exemple 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{
100ProfInfos* global_infos = nullptr;
101int global_nb_total_call = 0;
102}
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 // Sous Linux avec gcc, les exceptions utilisent la libunwind contenue
113 // dans gcc et cela peut provoquer des deadlocks avec notre utilisation
114 // si cet handler est appelé lors du dépilement d'une exception.
115 // Pour éviter ce problème, on ne fait rien tant qu'une exception est
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
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;
182
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
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;
201
202 // L'évènement 0 doit toujours être le PAPI_TOT_CYC car on s'en sert
203 // pour les statistiques
205 // TODO: regarder si ses évènements sont supportés par le proc
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 }
230
231 for( Integer i=0; i<nb_event; ++i )
232 is_valid_event[i] = _addEvent(papi_events[i],i);
233
234
235 //int period = 500000;
236 String period_str = platform::getEnvironmentVariable("ARCANE_PROFILING_PERIOD");
237 if (!period_str.null()){
238 bool is_bad = builtInGetValue(m_period,period_str);
239 if (is_bad){
240 pwarning() << "Can not convert '" << period_str << "' to int";
241 }
242 }
243 String only_str = platform::getEnvironmentVariable("ARCANE_PROFILING_ONLYFLOPS");
244 if (!only_str.null())
245 m_only_flops = true;
246
247 if (m_only_flops){
248 _printFlops();
249 }
250 else{
251 // Il ne faut pas que la période soit trop petite sinon on passe tout
252 // le temps dans le traitement de 'arcane_papi_handler'.
253 if (m_period<100000)
254 m_period = 100000;
255 for( Integer i=0; i<nb_event; ++i ){
256 if (is_valid_event[i]){
257 retval = PAPI_overflow(m_event_set, papi_events[i], m_period, 0, arcane_papi_handler);
258 if (retval!=PAPI_OK){
259 // L'évènement PAPI_TOT_CYC est indispensable
260 if (i==0){
261 fatal() << "** ERROR in papi_overflow i=" << i << " r=" << retval
262 << " msg=" << PAPI_strerror(retval);
263 }
264 else
265 pwarning() << "** ERROR in papi_overflow i=" << i << " r=" << retval
266 << " msg=" << PAPI_strerror(retval);
267 is_valid_event[i] = false;
268 }
269 }
270 }
271 info() << "Période de sampling: (en évènements) " << m_period;
272 }
273}
274
275
276/*---------------------------------------------------------------------------*/
277/*---------------------------------------------------------------------------*/
278
279void PapiPerformanceService::
280startProfiling()
281{
282 if (m_only_flops)
283 return;
285 global_infos->startProfiling();
286 if (!m_is_running){
287 int retval = PAPI_start(m_event_set);
288 if (retval!=PAPI_OK){
289 ARCANE_FATAL("** ERROR in papi_start r={0}",retval);
290 }
291 m_is_running = true;
292 }
293}
294
295/*---------------------------------------------------------------------------*/
296/*---------------------------------------------------------------------------*/
297
298void PapiPerformanceService::
299switchEvent()
300{
301}
302
303/*---------------------------------------------------------------------------*/
304/*---------------------------------------------------------------------------*/
305
306void PapiPerformanceService::
307_printFlops()
308{
309 float real_time = 0.0f;
310 float proc_time = 0.0f;
311 long long flpins = 0.0;
312 float mflops = 0.0f;
313 // A partir de PAPI 6.0 il n'y a plus PAPI_flops mais à la place
314 // c'est 'PAPI_flops_rate' mais il y a un argument supplémentaire
315 // à mettre pour spécifier le type de flop à calculer (simple précision,
316 // double précision, ...)
317#if PAPI_VERSION >= PAPI_VERSION_NUMBER(6,0,0,0)
318 int retval = PAPI_flops_rate(PAPI_DP_OPS, &real_time, &proc_time, &flpins, &mflops);
319#else
320 int retval = PAPI_flops(&real_time, &proc_time, &flpins, &mflops);
321#endif
322 if (retval!=PAPI_OK)
323 error() << "** ERROR in PAPI_flops r=" << retval;
324 else{
325 info() << "PAPI_Flops: real_time=" << real_time
326 << " proc_time=" << proc_time
327 << " flpins=" << flpins
328 << " mflips=" << mflops;
329 }
330}
331
332/*---------------------------------------------------------------------------*/
333/*---------------------------------------------------------------------------*/
334
335void PapiPerformanceService::
336stopProfiling()
337{
338 if (!global_infos)
339 return;
340
341 if (m_only_flops){
342 _printFlops();
343 return;
344 }
345 CriticalSection cs(m_application->parallelSuperMng()->threadMng());
346
347 //info() << "PROFILING: stop profiling nb_call=" << global_nb_total_call;
348 if (m_is_running){
349 int retval = PAPI_stop(m_event_set,0);
350 if (retval!=PAPI_OK){
351 ARCANE_FATAL("** ERROR in papi_stop r={0}",retval);
352 }
353 m_is_running = false;
354 }
355 global_infos->stopProfiling();
356}
357
358/*---------------------------------------------------------------------------*/
359/*---------------------------------------------------------------------------*/
360
361void PapiPerformanceService::
362printInfos(bool dump_file)
363{
364 if (global_infos)
365 global_infos->printInfos(dump_file);
366}
367
368/*---------------------------------------------------------------------------*/
369/*---------------------------------------------------------------------------*/
370
371void PapiPerformanceService::
372dumpJSON(JSONWriter& writer)
373{
374 if (global_infos)
375 global_infos->dumpJSON(writer);
376}
377
378/*---------------------------------------------------------------------------*/
379/*---------------------------------------------------------------------------*/
380
381void PapiPerformanceService::
382getInfos(Int64Array &array)
383{
384 if (global_infos)
385 global_infos->getInfos(array);
386}
387
388/*---------------------------------------------------------------------------*/
389/*---------------------------------------------------------------------------*/
390
391void PapiPerformanceService::
392reset()
393{
394 if (global_infos)
395 global_infos->reset();
396}
397
398/*---------------------------------------------------------------------------*/
399/*---------------------------------------------------------------------------*/
400
401/*---------------------------------------------------------------------------*/
402/*---------------------------------------------------------------------------*/
403
405: public TimerMng
406{
407 public:
408 explicit PapiTimerMng(ITraceMng* tm)
409 : TimerMng(tm), m_nb_event(0), m_event_set(PAPI_NULL), m_is_started(false),
410 m_is_init(false), m_elapsed_us(0), m_elapsed_cycle()
411 {}
413 {
414 if (m_is_started)
415 PAPI_stop(m_event_set,m_values.data());
416 }
417 public:
418 void init();
419 void _addEvent(int event);
420 void start();
421 Real stop(const char* msg);
422
424 Real _getRealTime() override
425 {
426 return stop("test");
427 }
428
430 void _setRealTime() override
431 {
432 if (!m_is_init){
433 m_is_init = true;
434 init();
435 start();
436 }
437 }
438
439 private:
440 int m_nb_event;
441 int m_event_set;
442 bool m_is_started;
443 bool m_is_init;
444 UniqueArray<long_long> m_values;
445 UniqueArray<long_long> m_start_values;
446 long_long m_elapsed_us;
447 long_long m_elapsed_cycle;
448};
449
450/*---------------------------------------------------------------------------*/
451/*---------------------------------------------------------------------------*/
452
453void PapiTimerMng::
454init()
455{
456 _checkInitPAPI();
457
458 int retval = PAPI_thread_init((unsigned long (*)(void)) (pthread_self));
459 if (retval != PAPI_OK)
460 ARCANE_FATAL("PAPI_thread_init r={0}",retval);
461
462 retval = PAPI_create_eventset(&m_event_set);
463 if (retval!=PAPI_OK)
464 ARCANE_FATAL("PAPI_create_eventset r={0}",retval);
465
466 _addEvent(PAPI_TOT_CYC);
467 _addEvent(PAPI_DP_OPS);
468 m_values.resize(m_nb_event);
469 m_values.fill(0);
470 m_start_values.resize(m_nb_event);
471 m_start_values.fill(0);
472
473 retval = PAPI_start(m_event_set);
474 if (retval != PAPI_OK)
475 ARCANE_FATAL("PAPI_start r={0}",retval);
476 m_is_started = true;
477}
478
479/*---------------------------------------------------------------------------*/
480/*---------------------------------------------------------------------------*/
481
482void PapiTimerMng::
483_addEvent(int event)
484{
485 int retval = PAPI_add_event(m_event_set,event);
486 if (retval!=PAPI_OK){
487 cerr << "** CAN NOT FIND EVENT " << event << '\n';
488 return;
489 }
490 ++m_nb_event;
491}
492
493/*---------------------------------------------------------------------------*/
494/*---------------------------------------------------------------------------*/
495
496void PapiTimerMng::
497start()
498{
499 int retval = PAPI_read(m_event_set,m_start_values.data());
500 if (retval!=PAPI_OK){
501 cerr << "** CAN NOT START EVENT\n";
502 }
503 m_elapsed_us = PAPI_get_real_usec();
504 m_elapsed_cycle = PAPI_get_real_cyc();
505}
506
507/*---------------------------------------------------------------------------*/
508/*---------------------------------------------------------------------------*/
509
510Real PapiTimerMng::
511stop(const char* msg)
512{
513 int retval = PAPI_read(m_event_set,m_values.data());
514 if (retval!=PAPI_OK){
515 cerr << "** CAN NOT STOP EVENT\n";
516 }
517 long_long elapsed_us = PAPI_get_real_usec() - m_elapsed_us;
518 //long_long elapsed_cycle = PAPI_get_real_cyc() - m_elapsed_cycle;
519
520 double sec_time = ((double)elapsed_us) / 1.0e6;
521
522 std::cout << " -- -- Time: ";
523 std::cout.width(60);
524 std::cout << msg << " = ";
525 std::cout.width(10);
526 std::cout << sec_time << " ";
527
528 //cout << "** TIME: " << (elapsed_us) << " CYCLE=" << elapsed_cycle << '\n';
529
530 long_long nb_cycle = m_values[0]-m_start_values[0];
531 long_long nb_flop = m_values[1]-m_start_values[1];
532
533 std::cout.width(15);
534 std::cout << nb_cycle << " ";
535 std::cout.width(12);
536 std::cout << nb_flop;
537
538 std::cout << " (";
539 std::cout.width(5);
540 std::cout << nb_flop/elapsed_us;
541 std::cout << ")";
542
543 std::cout << '\n';
544
545 //for( Integer i=0, is=m_values.size() ; i<is; ++i ){
546 // long_long diff = (m_values[i]-m_start_values[i]);
547 // cout << "** EVENT: " << diff << " FLOP=" << (diff/elapsed_us) << '\n';
548 //}
549 return sec_time;
550}
551
552/*---------------------------------------------------------------------------*/
553/*---------------------------------------------------------------------------*/
554
555/*---------------------------------------------------------------------------*/
556/*---------------------------------------------------------------------------*/
557
558ITimerMng* PapiPerformanceService::
559timerMng()
560{
561 if (!m_timer_mng)
562 m_timer_mng = new PapiTimerMng(traceMng());
563 return m_timer_mng;
564}
565
566/*---------------------------------------------------------------------------*/
567/*---------------------------------------------------------------------------*/
568
569/*---------------------------------------------------------------------------*/
570/*---------------------------------------------------------------------------*/
571
573: public TraceAccessor
575{
576 public:
578 : TraceAccessor(sbi.application()->traceMng()), m_nb_event(0),
579 m_event_set(PAPI_NULL), m_is_started(false)
580 {
581 }
583 {
584 if (m_is_started)
585 (void)PAPI_stop(m_event_set,nullptr);
586 }
587 public:
588
589 void build()
590 {
591 }
592
593 void initialize() override
594 {
595 int retval = 0;
596
598
599 retval = PAPI_thread_init((unsigned long (*)(void)) (pthread_self));
600 if (retval != PAPI_OK)
601 ARCANE_FATAL("Error in 'PAPI_thread_init' r={0}",retval);
602
603 //int event_mask = MASK_FP_OPS | MASK_L2_TCM | MASK_TOT_CYC;
604 //m_event_set = _makeEventSet(&num_events,&event_mask);
605 retval = PAPI_create_eventset(&m_event_set);
606 if (retval!=PAPI_OK)
607 ARCANE_FATAL("Error in 'PAPI_createeventset' r={0}",retval);
608
609 _addEvent(PAPI_TOT_CYC);
610 _addEvent(PAPI_RES_STL);
611 _addEvent(PAPI_L2_TCM);
612 }
613 void _addEvent(int event)
614 {
615 int retval = PAPI_add_event(m_event_set,event);
616 if (retval!=PAPI_OK){
617 error() << "** CAN NOT FIND EVENT " << event << '\n';
618 return;
619 }
620 ++m_nb_event;
621 }
623 {
624 if (m_is_started)
625 ARCANE_FATAL("start() has alredy been called");
626 int retval = PAPI_start(m_event_set);
627 if (retval != PAPI_OK)
628 ARCANE_FATAL("Error in 'PAPI_start' r={0}",retval);
629 m_is_started = true;
630 }
631 void stop() final
632 {
633 if (!m_is_started)
634 ARCANE_FATAL("start() has not been called");
635 int retval = PAPI_stop(m_event_set,nullptr);
636 if (retval != PAPI_OK)
637 ARCANE_FATAL("Error in 'PAPI_stop' r={0}",retval);
638 m_is_started = false;
639 }
640 bool isStarted() const final
641 {
642 return m_is_started;
643 }
644
645 Integer getCounters(Int64ArrayView counters,bool do_substract) final
646 {
647 long_long values[MIN_COUNTER_SIZE];
648 int retval = PAPI_read(m_event_set,values);
649 if (retval!=PAPI_OK){
650 error() << "Error in 'PAPI_read' during getCounters() r=" << retval;
651 }
652 Integer n = m_nb_event;
653 if (do_substract){
654 for( int i=0; i<n; ++i )
655 counters[i] = (Int64)values[i] - counters[i];
656 }
657 else
658 for( int i=0; i<n; ++i )
659 counters[i] = values[i];
660 return n;
661 }
662
664 {
665 std::array<Int64,MIN_COUNTER_SIZE> values;
666 Int64ArrayView view(values);
667 getCounters(view,false);
668 return view[0];
669 }
670
671 private:
672
673 int m_nb_event = 0;
674 int m_event_set = 0;
675 bool m_is_started = false;
676};
677
678/*---------------------------------------------------------------------------*/
679/*---------------------------------------------------------------------------*/
680
681ARCANE_REGISTER_SERVICE(PapiPerformanceCounterService,
682 ServiceProperty("PapiPerformanceCounterService",ST_Application),
683 ARCANE_SERVICE_INTERFACE(IPerformanceCounterService));
684
685/*---------------------------------------------------------------------------*/
686/*---------------------------------------------------------------------------*/
687
688} // End namespace Arcane
689
690/*---------------------------------------------------------------------------*/
691/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro retournant le pointeur ptr s'il est non nul ou lancant une exception s'il est nul.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
#define ARCANE_SERVICE_INTERFACE(ainterface)
Macro pour déclarer une interface lors de l'enregistrement d'un service.
Tableau d'items de types quelconques.
Section critique en multi-thread.
Interface d'un service d'accès aux compteurs de performance.
Interface d'un gestionnaire de timer.
Definition ITimerMng.h:53
Ecrivain au format JSON.
Definition JSONWriter.h:33
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
void stop() final
Arrête le suivi des compteurs de performance.
void start() final
Débute le suivi des compteurs de performance.
void initialize() override
Initialise le service.
Integer getCounters(Int64ArrayView counters, bool do_substract) final
Récupère les valeurs actuelles des compteurs.
Int64 getCycles() final
Valeur du compteur pour le nombre de cycles du CPU.
bool isStarted() const final
Indique si le service a démarré (start() a été appelé)
Real _getRealTime() override
Retourne le temps réel.
void _setRealTime() override
Positionne un timer réel.
Structure contenant les informations pour créer un service.
Gestionnaire de timer.
Definition TimerMng.h:39
Vue modifiable d'un tableau d'un type T.
Interface du gestionnaire de traces.
Chaîne de caractères unicode.
ITraceMng * traceMng() const
Gestionnaire de trace.
Vecteur 1D de données avec sémantique par valeur (style STL).
#define ARCANE_REGISTER_SERVICE(aclass, a_service_property,...)
Macro pour enregistrer un service.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
@ ST_Application
Le service s'utilise au niveau de l'application.