Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
ArcaneMainBatch.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/* ArcaneMainBatch.cc (C) 2000-2024 */
9/* */
10/* Gestion de l'exécution en mode Batch. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/Iostream.h"
15#include "arcane/utils/Ptr.h"
16#include "arcane/utils/StdHeader.h"
17#include "arcane/utils/PlatformUtils.h"
18#include "arcane/utils/List.h"
19#include "arcane/utils/ApplicationInfo.h"
20#include "arcane/utils/NotSupportedException.h"
21#include "arcane/utils/FatalErrorException.h"
22#include "arcane/utils/String.h"
23#include "arcane/utils/ITraceMng.h"
24#include "arcane/utils/OStringStream.h"
25#include "arcane/utils/IMemoryInfo.h"
26#include "arcane/utils/Array.h"
27#include "arcane/utils/IFunctor.h"
28#include "arcane/utils/StringBuilder.h"
29#include "arcane/utils/ScopedPtr.h"
30#include "arcane/utils/ValueConvert.h"
31#include "arcane/utils/IProcessorAffinityService.h"
32#include "arcane/utils/ArgumentException.h"
33#include "arcane/utils/CStringUtils.h"
34#include "arcane/utils/ITraceMngPolicy.h"
35#include "arcane/utils/Property.h"
36#include "arcane/utils/ParameterListPropertyReader.h"
37#include "arcane/utils/CommandLineArguments.h"
38#include "arcane/utils/CriticalSection.h"
39
40#include "arcane/impl/ArcaneMain.h"
41#include "arcane/impl/ParallelReplication.h"
42
43#include "arcane/IIOMng.h"
44#include "arcane/ICodeService.h"
45#include "arcane/ISession.h"
46#include "arcane/Timer.h"
47#include "arcane/ISubDomain.h"
48#include "arcane/IApplication.h"
49#include "arcane/ITimeLoopMng.h"
50#include "arcane/ITimeStats.h"
51#include "arcane/SequentialSection.h"
52#include "arcane/IParallelSuperMng.h"
53#include "arcane/ITimeHistoryMng.h"
54#include "arcane/IDirectExecution.h"
55#include "arcane/IDirectSubDomainExecuteFunctor.h"
56#include "arcane/ICaseMng.h"
57#include "arcane/ServiceFinder2.h"
58#include "arcane/SubDomainBuildInfo.h"
59#include "arcane/IParallelMng.h"
60#include "arcane/IMainFactory.h"
61#include "arcane/ApplicationBuildInfo.h"
62#include "arcane/CaseDatasetSource.h"
63
64#include "arcane/ServiceUtils.h"
65
66#include "arcane/impl/ExecutionStatsDumper.h"
67#include "arcane/impl/TimeLoopReader.h"
68
69#include "arcane/accelerator/core/internal/RunnerInternal.h"
70
71#include <thread>
72
73/*---------------------------------------------------------------------------*/
74/*---------------------------------------------------------------------------*/
75
76namespace Arcane
77{
78
79/*---------------------------------------------------------------------------*/
80/*---------------------------------------------------------------------------*/
81
84{
85 ARCANE_DECLARE_PROPERTY_CLASS(ArcaneMainBatchProperties);
86 public:
87 Int32 m_max_iteration = 0;
88 bool m_is_continue = false;
90};
91
92/*---------------------------------------------------------------------------*/
93/*---------------------------------------------------------------------------*/
98: public ArcaneMain
99{
100 public:
101
104 {
107 {
108 public:
109 SubInfo()
110 : m_sub_domain(nullptr), m_time_stats(nullptr), m_want_print_stats(false) {}
111 ~SubInfo()
112 {
113 // Il faut d'abord détruire la ITimeStats car il utilise
114 // les TimerMng des IParallelMng.
115 delete m_time_stats;
116 //delete m_rank_parallel_mng;
117 //m_world_parallel_mng.reset();
118 // Le sous-domaine est détruit lorsque la session se termine
119 }
120 SubInfo(const SubInfo&) = delete;
121 void operator=(const SubInfo&) = delete;
122 public:
123 Ref<IParallelMng> m_world_parallel_mng;
124 Ref<IParallelMng> m_rank_parallel_mng;
125 ISubDomain* m_sub_domain;
126 ITimeStats* m_time_stats;
127 bool m_want_print_stats;
128 };
129
130 public:
131 SessionExec(ArcaneMainBatch* arcane_main,ISession* session,Int32 nb_local_rank)
132 : m_arcane_main(arcane_main),
133 m_session(session),
135 m_direct_test_name(m_arcane_main->m_direct_test_name),
136 m_properties(m_arcane_main->m_properties),
137 m_code_service(m_arcane_main->m_code_service),
138 m_sub_infos(nb_local_rank),
139 m_direct_sub_domain_execute_functor(m_arcane_main->_directExecuteFunctor())
140 {
142 m_case_file = dataset_source.fileName();
143 m_case_bytes = dataset_source.content();
144 // Les sub_infos de chaque thread sont créés executeRank()
145 m_sub_infos.fill(nullptr);
146 }
148 {
149 for( Integer i=0, n=m_sub_infos.size(); i<n; ++i )
150 delete m_sub_infos[i];
151 }
152 public:
153 // Collective sur les threads du processus
154 void executeRank(Int32 local_rank);
155 private:
156 IApplication* _application() { return m_arcane_main->application(); }
157 private:
158 ArcaneMainBatch* m_arcane_main;
159 ISession* m_session;
161 String m_direct_test_name;
166 UniqueArray<SubInfo*> m_sub_infos;
167 IDirectSubDomainExecuteFunctor* m_direct_sub_domain_execute_functor;
168 private:
169 void _execDirectTest(IParallelMng* pm,const String& test_name,bool is_collective);
170 void _printStats(ISubDomain* sd,ITraceMng* trace,ITimeStats* time_stat);
171 void _createAndRunSubDomain(SubInfo* sub_info,Ref<IParallelMng> pm,Ref<IParallelMng> all_replica_pm,Int32 local_rank);
172 };
173
175 : public IFunctor
176 {
177 public:
178 ExecFunctor(SessionExec* session_exec,Int32 local_rank)
179 : m_session_exec(session_exec), m_local_rank(local_rank)
180 {
181 }
182 public:
183 void executeFunctor() override
184 {
185 m_session_exec->executeRank(m_local_rank);
186 }
187 private:
188 SessionExec* m_session_exec;
189 Int32 m_local_rank;
190 };
191
192 public:
193
195 ~ArcaneMainBatch() override;
196
197 void build() override;
198 void initialize() override;
199 int execute() override;
200 void doAbort() override;
201 bool parseArgs(StringList args) override;
202 void finalize() override;
203
204 private:
205
206 ISession* m_session = nullptr;
212 String m_direct_exec_name;
213 String m_direct_test_name;
215 SessionExec* m_session_exec = nullptr;
216
217 private:
218
219 bool _sequentialParseArgs(StringList args);
220};
221
222/*---------------------------------------------------------------------------*/
223/*---------------------------------------------------------------------------*/
224
225extern "C++" ARCANE_IMPL_EXPORT IArcaneMain*
226createArcaneMainBatch(const ApplicationInfo& app_info,IMainFactory* main_factory)
227{
229}
230
231/*---------------------------------------------------------------------------*/
232/*---------------------------------------------------------------------------*/
233
234ArcaneMainBatch::
235ArcaneMainBatch(const ApplicationInfo& exe_info,IMainFactory* main_factory)
236: ArcaneMain(exe_info,main_factory)
237, m_init_only(false)
238, m_check_case_only(false)
239, m_has_sub_domain_threads(false)
240{
241}
242
243/*---------------------------------------------------------------------------*/
244/*---------------------------------------------------------------------------*/
245
247build()
248{
250}
251
252/*---------------------------------------------------------------------------*/
253/*---------------------------------------------------------------------------*/
254
260
261/*---------------------------------------------------------------------------*/
262/*---------------------------------------------------------------------------*/
263
264ArcaneMainBatch::
265~ArcaneMainBatch()
266{
267 // Normalement finalize() doit avoir été appelé pour libérer les
268 // différents objets (m_session, m_code_service, ...).
269 // Si ce n'est pas le cas, c'est probablement du à une exception et dans
270 // ce cas on ne fait rien pour éviter de détruire des objets dont on ne
271 // connait pas trop l'état interne.
272}
273
274/*---------------------------------------------------------------------------*/
275/*---------------------------------------------------------------------------*/
276
279{
280 if (ArcaneMain::parseArgs(args))
281 return true;
282
283 bool r = _sequentialParseArgs(args);
284 return r;
285}
286
287
288/*****************************************************************************
289 * Les variables ARCANE_NB_SUB_DOMAIN & ARCANE_IDLE_SERVICE sont moins prioritaires
290 * que les arguments passés à l'exécutable.
291 *****************************************************************************/
292bool ArcaneMainBatch::
293_sequentialParseArgs(StringList args)
294{
295 ITraceMng* trace = _application()->traceMng();
296
297 String us_arcane_opt("-arcane_opt");
298 String us_init_only("init_only");
299 String us_check_case_only("check_case_only");
300 String us_continue("continue");
301 String us_max_iteration("max_iteration");
302 String us_casename("casename");
303 String us_direct_exec("direct_exec");
304 String us_direct_test("direct_test");
305 String us_direct_mesh("direct_mesh");
306 String us_tool_arg("tool_arg");
307 String us_nb_sub_domain("nb_sub_domain");
308 String us_nb_replication("nb_replication");
309 String us_idle_service("idle_service");
310
311 // Remplit 'm_properties' en fonction des paramètres de la ligne de commande
312 // TODO: Ce mécanisme est disponible depuis janvier 2021. A terme, il faudra
313 // rendre obsolète et supprimer la possibilité de spécifier les options
314 // via '-arcane_opt'.
315 properties::readFromParameterList(applicationInfo().commandLineArguments().parameters(),m_properties);
316
317 CaseDatasetSource& dataset_source = _applicationBuildInfo().caseDatasetSource();
318 // Indique si on a un jeu de données.
319 bool has_case_dataset_content = !(dataset_source.fileName().empty() && dataset_source.content().empty());
320 Integer nb_arg = args.count();
322 trace->info() << "Usage: programm input_data ; for more information: program -arcane_opt help";
323 trace->pfatal() << "No input data specified.";
324 }
325
326 StringList unknown_args;
327 StringBuilder tool_args_xml;
328 String tool_mesh;
329
330 String nb_sub_domain_str;
331 String nb_replication_str;
332 String idle_service_name = platform::getEnvironmentVariable("ARCANE_IDLE_SERVICE");
333 if (!idle_service_name.null())
334 m_properties.m_idle_service_name = idle_service_name;
335
336 for( Integer i=1, s=nb_arg-1; i<s; ++i ){
337 // cerr << "** ARGS ARGS " << i << ' ' << args[i] << '\n';
338 if (args[i]!=us_arcane_opt){
339 unknown_args.add(args[i]);
340 continue;
341 }
342 bool is_valid_opt = false;
343 ++i;
344 String str;
345 if (i<s)
346 str = args[i];
347 if (str==us_init_only){
348 m_init_only = true;
349 is_valid_opt = true;
350 }
351 else if (str==us_check_case_only){
352 m_check_case_only = true;
353 is_valid_opt = true;
354 }
355 else if (str==us_continue){
356 m_properties.m_is_continue = true;
357 is_valid_opt = true;
358 }
359 else if (str==us_max_iteration){
360 ++i;
361 if (i<s){
362 m_properties.m_max_iteration = CStringUtils::toInteger(args[i].localstr());
363 //cerr << "** MAX ITER " << m_max_iteration << '\n';
364 is_valid_opt = true;
365 }
366 else
367 trace->pfatal() << "Option 'max_iteration' must specify the number of iterations";
368 }
369 // Nom du cas.
370 else if (str==us_casename){
371 ++i;
372 if (i<s){
373 m_case_name = args[i];
374 is_valid_opt = true;
375 }
376 }
377 else if (str==us_direct_exec){
378 ++i;
379 if (i<s){
380 m_direct_exec_name = args[i];
381 //trace->info()<<"[ArcaneMainBatch] m_direct_exec_name="<<args[i];
382 is_valid_opt = true;
383 }
384 }
385 else if (str==us_direct_test){
386 ++i;
387 if (i<s){
388 m_direct_test_name = args[i];
389 //trace->info()<<"[ArcaneMainBatch] m_direct_test_name="<<args[i];
390 is_valid_opt = true;
391 }
392 }
393 else if (str==us_tool_arg){
394 ++i;
395 String arg;
396 String value;
397 if (i<s){
398 arg = args[i];
399 }
400 ++i;
401 if (i<s){
402 value = args[i];
403 is_valid_opt = true;
404 tool_args_xml += String::format("<{0}>{1}</{2}>\n",arg,value,arg);
405 }
406 }
407 else if (str==us_nb_sub_domain){
408 ++i;
409 if (i<s){
410 nb_sub_domain_str = args[i];
411 //trace->info()<<"[ArcaneMainBatch] nb_sub_domain_str="<<args[i];
412 is_valid_opt = true;
413 }
414 }
415 else if (str==us_nb_replication){
416 ++i;
417 if (i<s){
418 nb_replication_str = args[i];
419 //trace->info()<<"[ArcaneMainBatch] nb_sub_domain_str="<<args[i];
420 is_valid_opt = true;
421 }
422 }
423 else if (str==us_idle_service){
424 ++i;
425 if (i<s){
427 //trace->info()<<"[ArcaneMainBatch] m_idle_service_name="<<args[i];
428 is_valid_opt = true;
429 }
430 }
431 if (!is_valid_opt){
432 trace->pfatal() << "Unknown Arcane option <" << str << ">\n";
433 }
434 }
435
436 bool use_direct_test = (!m_direct_test_name.null());
437 bool use_direct_exec = (!m_direct_exec_name.null());
438
439 if (use_direct_test){
440 }
441 else if (use_direct_exec){
442 // Dans ce cas, le dernier argument de la ligne de commande est
443 // le nom du maillage.
444 tool_mesh = args[nb_arg-1];
445 dataset_source.setFileName("Dummy.arc");
446 }
447 else{
448 // Le nom du cas est contenu dans le dernier argument de la ligne
449 // de commande. On prend cet argument sauf si un nom de fichier
450 // a déjà été positionné avant d'initialiser Arcane.
451 if (dataset_source.fileName().empty() && dataset_source.content().empty())
452 dataset_source.setFileName(args[nb_arg-1]);
453 }
454
455 if (!nb_sub_domain_str.null()){
456 Int32 nb_sub_domain = 0;
457 bool is_bad = builtInGetValue(nb_sub_domain,nb_sub_domain_str);
458 if (is_bad || nb_sub_domain<=0){
459 trace->pfatal() << "Invalid number of subdomains : " << nb_sub_domain;
460 }
461 trace->info() << "Use '" << nb_sub_domain << "' subdomains";
462 _applicationBuildInfo().setNbProcessusSubDomain(nb_sub_domain);
463 }
464
465 if (!nb_replication_str.null()){
466 Int32 nb_replication = 0;
467 bool is_bad = builtInGetValue(nb_replication,nb_replication_str);
468 if (is_bad || nb_replication<0){
469 trace->pfatal() << "Invalid number of replication : " << nb_replication;
470 }
471 trace->info() << "Use replication of subdomains nb_replication=" << nb_replication;
472 _applicationBuildInfo().setNbReplicationSubDomain(nb_replication);
473 }
474
475 if (_applicationBuildInfo().nbReplicationSubDomain()!=0 && _applicationBuildInfo().nbProcessusSubDomain()!=0)
476 trace->pfatal() << "The subdomains number of replication and restriction options are incompatible.";
477
478 if (!use_direct_test){
479 String case_file = dataset_source.fileName();
480 //trace->info()<<"[ArcaneMainBatch] !use_direct_test, getCodeService";
481 m_code_service = _application()->getCodeService(case_file);
482
483 if (!m_code_service){
484 trace->info() << "The file `" << case_file << "' is not a known file type.";
485 case_file = args[nb_arg-2];
486
487 m_code_service = _application()->getCodeService(case_file);
488 if (!m_code_service){
489 trace->pfatal() << "File extension not valid.";
490 }
491 }
492 }
493
494 if (use_direct_exec){
495 //trace->info()<<"[ArcaneMainBatch] use_direct_test!";
496 // Analyse les arguments qui correspondent aux options d'exécution directes
497 // et construit un fichier xml à partir de la.
498 StringBuilder s;
499 s += "<?xml version=\"1.0\"?>\n";
500 s += "<case codename=\"ArcaneDriver\" xml:lang=\"en\" codeversion=\"1.0\">";
501 s += " <arcane>\n";
502 s += " <title>DirectExec</title>\n";
503 s += " <description>DirectExec</description>\n";
504 s += " <timeloop>ArcaneDirectExecutionLoop</timeloop>\n";
505 s += " </arcane>\n";
506 s += " <mesh>\n";
507 s += String::format(" <file>{0}</file>\n",tool_mesh);
508 s += " </mesh>\n";
509 s += " <arcane-direct-execution>\n";
510 s += String::format(" <tool name='{0}'>\n",m_direct_exec_name);
511 s += tool_args_xml;
512 s += " </tool>\n";
513 s += " </arcane-direct-execution>\n";
514 s += "</case>\n";
515 dataset_source.setFileName("(None)");
516 String buf = s;
517 dataset_source.setContent(buf.utf8());
518 trace->info() << "Direct exec xml file=" << s;
519 }
520
521 if (m_code_service.get()){
522 bool is_bad = m_code_service->parseArgs(unknown_args);
523 if (is_bad)
524 return true;
525 }
526
527 if (!unknown_args.empty()){
528 trace->info()<< "Unknown command line option: " << unknown_args[0];
529 }
530
531 return false;
532}
533
534/*---------------------------------------------------------------------------*/
535/*---------------------------------------------------------------------------*/
536
537namespace
538{
539struct LaunchThreadInfo
540{
541 ArcaneMainBatch* arcane_main;
542 ArcaneMainBatch::SessionExec* session_exec;
543 IApplication* application;
544 Int32 thread_index;
545};
546}
547
548/*---------------------------------------------------------------------------*/
549/*---------------------------------------------------------------------------*/
550/*
551 * Cette fonction est celle appelée lors de la création d'un thread.
552 */
553void
554_ThreadWrapper(LaunchThreadInfo* lti)
555{
556 ArcaneMainBatch* amb = lti->arcane_main;
557 IApplication* main_app = lti->application;
558 ArcaneMainBatch::ExecFunctor functor(lti->session_exec,lti->thread_index);
559 bool clean_abort = false;
560 bool is_master = lti->thread_index == 0;
561 int r = ArcaneMain::callFunctorWithCatchedException(&functor,amb,&clean_abort,is_master);
562 if (r!=0 && !clean_abort){
563 // Le thread est terminé mais comme il est le seul à avoir planté,
564 // il est possible que les autres soient bloqués.
565 // Dans ce cas, on fait un abort pour éviter un blocage
566 // TODO: essayer de tuer les autres threads correctement.
567 if (main_app){
568 IParallelSuperMng* psm = main_app->parallelSuperMng();
569 psm->tryAbort();
570 }
571 }
572}
573
574/*---------------------------------------------------------------------------*/
575/*---------------------------------------------------------------------------*/
576
578execute()
579{
580 ITraceMng* trace = _application()->traceMng();
581
582 if (m_code_service.get())
583 m_session = m_code_service->createSession();
584
585 IParallelSuperMng* psm = _application()->parallelSuperMng();
586 Int32 nb_total_rank = psm->commSize();
587 const Integer nb_wanted_sub_domain = applicationBuildInfo().nbReplicationSubDomain();
588 CaseDatasetSource& dataset_source = _applicationBuildInfo().caseDatasetSource();
590 ARCANE_THROW(ArgumentException,"Number of subdomain '{0}' > number of allocated cores '{1}",
592
593 Integer nb_local_rank = psm->nbLocalSubDomain();
594 trace->info() << "NB_LOCAL_RANK=" << nb_local_rank;
595 if (nb_local_rank>=1)
597 int return_value = 0;
598
599 // Lecture des données du jeu de données.
600 if (dataset_source.content().empty() && m_direct_test_name.null()){
601 String case_file = dataset_source.fileName();
602 trace->info() << "Reading input data '" << case_file << "'";
603 IIOMng* io_mng = _application()->ioMng();
605 bool is_bad = io_mng->collectiveRead(case_file,case_bytes);
606 if (is_bad)
607 ARCANE_THROW(ParallelFatalErrorException,"Cannot read input data file '{0}'",case_file);
608 dataset_source.setContent(case_bytes);
609 }
610
611 m_session_exec = new SessionExec(this,m_session,nb_local_rank);
612
614 for( Integer i=0; i<nb_local_rank; ++i ){
615 thinfo[i].arcane_main = this;
616 thinfo[i].session_exec = m_session_exec;
617 thinfo[i].application = _application();
618 thinfo[i].thread_index = i;
619 }
620
621 if (nb_local_rank>1){
623 for( Integer i=0; i<nb_local_rank; ++i ){
624 gths[i] = new std::thread(_ThreadWrapper,&thinfo[i]);
625 }
626 for( Integer i=0; i<nb_local_rank; ++i ){
627 gths[i]->join();
628 delete gths[i];
629 }
630 }
631 else{
633 m_session_exec->executeRank(0);
634 }
635
636 // TODO: supprimer car inutile car vaut toujours 0.
637 return return_value;
638}
639
640/*---------------------------------------------------------------------------*/
641/*---------------------------------------------------------------------------*/
642/*
643 * En mode avec un sous-domaine par thread,cette fonction est appelée
644 * par chaque thread (de manière potentiellement concurrente) pour son sous-domaine.
645 * \a local_rank indique le rang local du thread, qui est compris
646 * entre 0 et \a nb_local_sub_domain (tèl que définit dans execute()).
647 */
648void ArcaneMainBatch::SessionExec::
649executeRank(Int32 local_rank)
650{
651 // ATTENTION:
652 // Cette fonction doit etre reentrente...
653
654 auto sub_info = new SubInfo();
655 m_sub_infos[local_rank] = sub_info;
656
659 // Il ne faut binder les CPU que si demandé et uniquement si
660 // le nombre de threads au total (sur l'ensemble des processus)
661 // ne dépasse pas le nombre de coeur de la machine.
662 if (!platform::getEnvironmentVariable("ARCANE_BIND_THREADS").null()){
663 ITraceMng* tm = _application()->traceMng();
664 tm->info() << "Binding threads";
665 pas->bindThread(local_rank);
666 }
667 }
668
669 // Création du gestionnaire de parallélisme pour l'ensemble des rangs alloués.
670 IParallelSuperMng* psm = _application()->parallelSuperMng();
671 Ref<IParallelMng> world_pm = psm->internalCreateWorldParallelMng(local_rank);
672 sub_info->m_world_parallel_mng = world_pm;
673
674 if (!m_direct_test_name.null()){
675 _execDirectTest(world_pm.get(),m_direct_test_name,true);
676 return;
677 }
678
679 // Regarde si on souhaite exécuter le calcul sur un sous-ensemble
680 // des ressources allouées. Pour l'instant, il est uniquement possible
681 // de choisir un nombre de sous-domaine. Si c'est le cas, seuls
682 // les rangs de 0 au nombre de sous-domaine souhaité moins 1 sont
683 // utilisés. Les rangs supérieurs n'ont pas de sous-domaines
684 // et à la place utilisent un service qui implémente IDirectExecution
685
686 // Création du gestionnaire des statistiques d'exécution.
687 ITraceMng* trace = world_pm->traceMng();
688 String stat_name = "Rank";
689 stat_name = stat_name + world_pm->commRank();
690 ITimeStats* time_stat = _application()->mainFactory()->createTimeStats(world_pm->timerMng(),trace,stat_name);
691 sub_info->m_time_stats = time_stat;
692 time_stat->beginGatherStats();
693 world_pm->setTimeStats(time_stat);
694
695 Ref<IParallelMng> pm = world_pm;
696 Ref<IParallelMng> all_replica_pm = pm;
697
698 const Integer nb_wanted_sub_domain = _application()->applicationBuildInfo().nbProcessusSubDomain();
699 const Integer nb_wanted_replication = _application()->applicationBuildInfo().nbReplicationSubDomain();
700 // On est en parallèle et on souhaite moins de sous-domaines que de processus alloués
701 if (world_pm->isParallel()){
702 // Pour l'instant, on ne peut pas mélanger la réplication de sous-domaines avec
703 // un nombre de sous-domaines différent du nombre de processeurs alloués.
704 // TODO: lorsque ce ne sera plus le cas, il faudra faire un all_replica_pm qui
705 // contiendra l'ensemble des sous-domaines et des réplica.
706
707 if (nb_wanted_replication>1){
708 Int32 comm_size = world_pm->commSize();
709 Int32 nb_sub_part = comm_size / nb_wanted_replication;
710 trace->info() << "Using sub-domain replication nb_sub_part=" << nb_sub_part;
711 if ((comm_size % nb_wanted_replication)!=0)
712 ARCANE_FATAL("The number of replication '{0}' must be a common factor of the number of allocated cores '{1}",
713 nb_wanted_replication,comm_size);
714 // D'abord, on créé un communicateur contenant les réplicats de chaque sous-domaine
715 // Ce communicateur contiendra donc \a m_nb_wanted_replication objets
716 Ref<IParallelMng> replicate_pm;
717 trace->info() << "Building replicated parallel mng";
718 {
719 Int32UniqueArray kept_ranks(nb_wanted_replication);
720 for( Integer i_sd=0; i_sd<nb_sub_part; ++i_sd ){
721 for( Int32 i=0; i<nb_wanted_replication; ++i ){
722 kept_ranks[i] = i_sd + (i*nb_sub_part);
723 trace->info() << "Rank r=" << kept_ranks[i];
724 }
725 Ref<IParallelMng> new_pm = world_pm->createSubParallelMngRef(kept_ranks);
726 if (new_pm.get()){
727 replicate_pm = new_pm;
728 replicate_pm->setTimeStats(time_stat);
729 trace->info() << " Building own replicated parallel mng";
730 }
731 else{
732 trace->info()<<"!pm";
733 }
734 trace->flush();
735 }
736 }
737 if (!replicate_pm)
738 ARCANE_FATAL("Null replicated parallel mng");
739
740 // Maintenant, on créé un IParallelMng qui correspond à l'ensemble
741 // des rangs d'un même réplica. Ce IParallelMng sera assigné au
742 // sous-domaine qui sera créé par la suite.
743 trace->info() << "Building sub-domain parallel mng";
744 {
745 Int32UniqueArray kept_ranks(nb_sub_part);
746 for( Integer i_repl=0; i_repl<nb_wanted_replication; ++i_repl ){
747 for( Int32 i=0; i<nb_sub_part; ++i ){
748 kept_ranks[i] = i + (i_repl*nb_sub_part);
749 trace->info() << "Rank r=" << kept_ranks[i];
750 }
751 Ref<IParallelMng> new_pm = world_pm->createSubParallelMngRef(kept_ranks);
752 if (new_pm.get()){
753 pm = new_pm;
754 if (nb_sub_part==1){
755 // Il faut prendre la version séquentielle pour faire comme si le calcul
756 // était séquentiel. Ce gestionnaire sera détruit en même temps
757 // que \a new_pm
758 pm = new_pm->sequentialParallelMngRef();
759 }
760 trace->info()<<"pm: setting time_stat & m_rank_parallel_mng for replica rank=" << i_repl;
761 trace->flush();
762 pm->setTimeStats(time_stat);
763 sub_info->m_rank_parallel_mng = new_pm;
764 auto pr = new ParallelReplication(i_repl,nb_wanted_replication,replicate_pm);
765 pm->setReplication(pr);
766 }
767 else{
768 trace->info()<<"!pm";
769 trace->flush();
770 }
771 }
772 }
773 }
774 else if (nb_wanted_sub_domain!=0){
775 const Int32 nb_sub_part = nb_wanted_sub_domain;
776 Int32UniqueArray kept_ranks(nb_sub_part);
777 for( Int32 i=0; i<nb_sub_part; ++i )
778 kept_ranks[i] = i;
779 pm = world_pm->createSubParallelMngRef(kept_ranks);
780 if (pm.get()){
781 trace->info()<<"pm: setting time_stat & m_rank_parallel_mng";
782 trace->flush();
783 pm->setTimeStats(time_stat);
784 sub_info->m_rank_parallel_mng = pm;
785 all_replica_pm = pm;
786 }
787 else{
788 trace->info()<<"!pm";
789 trace->flush();
790 }
791 }
792 }
793
794 bool print_stats = false;
795 ISubDomain* sub_domain = nullptr;
796
797 if (!pm){
798 // Si ici, il s'agit d'un rang qui ne possède pas de sous-domaine.
799 // Dans ce cas, exécute le service donnée par 'm_idle_service_name'
800 // (si spécifié, sinon ne fait rien)
801 trace->info()<<"The rank doesn't own any subdomain!";
803 trace->info() << "No idle service specified"; trace->flush();
804 }
805 else{
806 trace->info()<<"execDirectTest: "<< m_properties.m_idle_service_name;
807 trace->flush();
808 _execDirectTest(world_pm.get(),m_properties.m_idle_service_name,false);
809 // On sort de l'execute() du directTest grâce au broadcast(This is the end), il faut s'en retourner
810 return;
811 }
812 print_stats = true;
813 }
814 else {
815 _createAndRunSubDomain(sub_info,pm,all_replica_pm,local_rank);
816 sub_domain = sub_info->m_sub_domain;
817 print_stats = sub_info->m_want_print_stats;
818 }
819
820 time_stat->endGatherStats();
821
822 if (print_stats && sub_domain){
823 // S'assure que tout le monde est ici avant d'arêter le profiling
824 // TODO: Comme le profiling est local au processus, il suffirait
825 // a priori de faire la barrière sur les IParallelMng locaux.
826 IParallelMng* pm = sub_domain->parallelMng();
827 pm->barrier();
828 if (local_rank==0)
830 pm->barrier();
831 _printStats(sub_domain,trace,time_stat);
832 }
833
834 //BaseForm[Hash["This is the end", "CRC32"], 16]
835 // On informes les 'autres' capacités qu'il faut s'en aller, maintenant!
836 world_pm->broadcast(UniqueArray<unsigned long>(1,0xdfeb699fl).view(),0);
837}
838
839/*---------------------------------------------------------------------------*/
840/*---------------------------------------------------------------------------*/
841
842void ArcaneMainBatch::SessionExec::
843_createAndRunSubDomain(SubInfo* sub_info,Ref<IParallelMng> pm,Ref<IParallelMng> all_replica_pm,Int32 local_rank)
844{
845 // Il s'agit d'un rang qui a un sous-domaine.
846 // Celui-ci est créé et l'exécution commence.
847 SubDomainBuildInfo sdbi(pm,local_rank,all_replica_pm);
848 sdbi.setCaseFileName(m_case_file);
849 sdbi.setCaseContent(m_case_bytes);
850 ISubDomain* sub_domain = m_code_service->createAndLoadCase(m_session,sdbi);
851 sub_info->m_sub_domain = sub_domain;
852
853 ITraceMng* trace = _application()->traceMng();
854 ITraceMng* sd_trace = sub_domain->traceMng();
855 ITraceMngPolicy* trace_policy = _application()->getTraceMngPolicy();
856
857 // En cas de réplication, désactive les sorties courbes
858 // des réplicats.
859 trace->info() << "REPLICATION: rank=" << pm->replication()->replicationRank();
860
861 if (!pm->replication()->isMasterRank()){
862 trace->info() << "Disable output curves for replicates.";
863 sub_domain->timeHistoryMng()->setDumpActive(false);
864 }
865
866 // TODO:
867 // Détruire le sous-domaine à la fin de la fonction mais il
868 // faut pour cela modifier ISession pour supporter la suppression
869 // d'un sous-domaine (et ensuite détruire ISession).
870
871 IProcessorAffinityService* pas = platform::getProcessorAffinityService();
872 if (pas){
873 String cpu_set = pas->cpuSetString();
874 trace->info() << " CpuSet=" << cpu_set;
875 }
876
877 if (m_arcane_main->m_check_case_only){
878 trace->info() << "Checking the input data";
879 // Initialise les modules de la boucle en temps
880 {
881 TimeLoopReader stl(_application());
882 stl.readTimeLoops();
883 stl.registerTimeLoops(sub_domain);
884 stl.setUsedTimeLoop(sub_domain);
885 }
886 ICaseMng* cm = sub_domain->caseMng();
887 cm->readOptions(true);
888 }
889 else{
890 Timer init_timer(sub_domain,"InitTimer",Timer::TimerReal);
891 Timer loop_timer(sub_domain,"LoopTimer",Timer::TimerReal);
892
893 {
894 Timer::Action ts_action(sub_domain,"Init");
895 Timer::Sentry ts(&init_timer);
896
897 m_code_service->initCase(sub_domain,m_properties.m_is_continue);
898 }
899
900 if (m_properties.m_max_iteration>0)
901 trace->info() << "Option 'max_iteration' activated with " << m_properties.m_max_iteration;
902
903 // Redirige les signaux.
904 // Cela se fait aussi a l'initialisation mais ici on peut être dans un autre
905 // thread et de plus certaines bibliothèques ont pu rediriger les signaux
906 // lors de l'init
907 {
908 CriticalSection cs(pm->threadMng());
909 ArcaneMain::redirectSignals();
910 }
911 int ret_compute_loop = 0;
912
913 IDirectExecution* direct_exec = sub_domain->directExecution();
914 if (direct_exec && direct_exec->isActive()){
915 trace->info() << "Direct execution activated";
916 direct_exec->execute();
917 }
918 else if (m_arcane_main->m_init_only){
919 trace->info() << "Option 'init_only' activated";
920 sub_info->m_want_print_stats = true;
921 }
922 else{
923 sub_info->m_want_print_stats = true;
924 Timer::Action ts_action(sub_domain,"Loop");
925 Timer::Sentry ts(&loop_timer);
926 // Lors de la boucle de calcul, ne force pas l'affichage des traces à un niveau
927 // donné (ce qui est fait lors de l'initialisation de l'application.
928 trace_policy->setDefaultVerboseLevel(sd_trace,Trace::UNSPECIFIED_VERBOSITY_LEVEL);
929 if (m_direct_sub_domain_execute_functor){
930 m_direct_sub_domain_execute_functor->setSubDomain(sub_domain);
931 m_direct_sub_domain_execute_functor->execute();
932 sub_domain->parallelMng()->barrier();
933 }
934 else{
935 ret_compute_loop = sub_domain->timeLoopMng()->doComputeLoop(m_properties.m_max_iteration);
936 if (ret_compute_loop<0)
937 //TODO: NE PAS REMPLIR DIRECTEMENT CETTE FONCTION CAR CELA NE MARCHE
938 // PAS EN MULTI-THREAD
939 m_arcane_main->setErrorCode(8);
940 }
941 }
942 {
943 Real init_time = init_timer.totalTime();
944 Real loop_time = loop_timer.totalTime();
945 trace->info(0) << "TotalReel = " << (init_time+loop_time)
946 << " secondes (init: "
947 << init_time << " loop: " << loop_time << " )";
948 }
949 {
950 Timer::Action ts_action(sub_domain,"Exit");
951 trace_policy->setDefaultVerboseLevel(sd_trace,Trace::DEFAULT_VERBOSITY_LEVEL);
952 sub_domain->doExitModules();
953 }
954 }
955}
956
957/*---------------------------------------------------------------------------*/
958/*---------------------------------------------------------------------------*/
959
960void ArcaneMainBatch::SessionExec::
961_printStats(ISubDomain* sub_domain,ITraceMng* trace,ITimeStats* time_stat)
962{
963 ExecutionStatsDumper exec_dumper(trace);
964 exec_dumper.dumpStats(sub_domain,time_stat);
965}
966
967/*---------------------------------------------------------------------------*/
968/*---------------------------------------------------------------------------*/
969
970void ArcaneMainBatch::SessionExec::
971_execDirectTest(IParallelMng* world_pm,const String& test_name,bool is_collective)
972{
973 ITraceMng* trace = world_pm->traceMng();
974 trace->info() << "Direct test name=" << test_name;
975 trace->flush();
976 ServiceFinder2T<IDirectExecution,IApplication> sf(_application(),_application());
977 Ref<IDirectExecution> exec(sf.createReference(test_name));
978 if (!exec){
979 String msg = String::format("Can not find 'IDirectExecution' service name '{0}'",test_name);
980 if (is_collective)
981 throw ParallelFatalErrorException(A_FUNCINFO,msg);
982 else
983 throw FatalErrorException(A_FUNCINFO,msg);
984 }
985 else{
986 trace->info() << "Begin execution of direct service";
987 trace->flush();
988 }
989 exec->setParallelMng(world_pm);
990 exec->execute();
991}
992
993/*---------------------------------------------------------------------------*/
994/*---------------------------------------------------------------------------*/
995
997finalize()
998{
999 if (m_session){
1000 m_session->endSession(errorCode());
1001 _application()->removeSession(m_session);
1002 delete m_session;
1003 m_session = nullptr;
1004 }
1005 m_code_service.reset();
1006 delete m_session_exec;
1007 m_session_exec = nullptr;
1008
1009 ITraceMng* tm = _application()->traceMng();
1011}
1012
1013/*---------------------------------------------------------------------------*/
1014/*---------------------------------------------------------------------------*/
1015
1017doAbort()
1018{
1019 if (m_session)
1020 m_session->doAbort();
1021 else{
1022 // Pour finir proprement même si arrêt avant la création de la session
1023 // ou après la destruction de la session.
1025 if (psm)
1026 psm->tryAbort();
1027 }
1028}
1029
1030/*---------------------------------------------------------------------------*/
1031/*---------------------------------------------------------------------------*/
1032
1033template<typename V> void ArcaneMainBatchProperties::
1034_applyPropertyVisitor(V& p)
1035{
1036 auto b = p.builder();
1037
1038 p << b.addInt32("MaxIteration")
1039 .addDescription("Maximum number of iteration")
1040 .addCommandLineArgument("MaxIteration")
1041 .addGetter([](auto a) { return a.x.m_max_iteration; })
1042 .addSetter([](auto a) { a.x.m_max_iteration = a.v; });
1043
1044 p << b.addBool("Continue")
1045 .addDescription("True if continue from previous execution (restart)")
1046 .addCommandLineArgument("Continue")
1047 .addGetter([](auto a) { return a.x.m_is_continue; })
1048 .addSetter([](auto a) { a.x.m_is_continue = a.v; });
1049
1050 p << b.addString("IdleService")
1051 .addDescription("Name of the idle service for additionnal cores")
1052 .addCommandLineArgument("IdleService")
1053 .addGetter([](auto a) { return a.x.m_idle_service_name; })
1054 .addSetter([](auto a) { a.x.m_idle_service_name = a.v; });
1055}
1056
1057/*---------------------------------------------------------------------------*/
1058/*---------------------------------------------------------------------------*/
1059
1060ARCANE_REGISTER_PROPERTY_CLASS(ArcaneMainBatchProperties,());
1061
1062/*---------------------------------------------------------------------------*/
1063/*---------------------------------------------------------------------------*/
1064
1065} // End namespace Arcane
1066
1067/*---------------------------------------------------------------------------*/
1068/*---------------------------------------------------------------------------*/
#define ARCANE_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
static void finalize(ITraceMng *tm)
Finalise l'exécution.
Definition Runner.cc:504
static void stopAllProfiling()
Stoppe toutes les activités de profiling.
Definition Runner.cc:492
CaseDatasetSource & caseDatasetSource()
Source du jeu de données.
Informations sur une application.
Propriétés associées à ArcaneMain.
String m_idle_service_name
Nom du service pour les CPU non utilisés.
void executeFunctor() override
Exécute la méthode associé
Infos par sous-domaine qui doivent être détruites à la fin de l'exécution.
Informations d'exécution pour une session.
Ref< ICodeService > m_code_service
Service du code.
bool m_has_sub_domain_threads
indique si on utilise des threads pour gérer des sous-domaines
UniqueArray< std::byte > m_case_bytes
Contenu du jeu de données du cas sous forme d'un document XML.
String m_case_file
Nom du fichier contenant le cas.
const ArcaneMainBatchProperties m_properties
Propriétés d'exécution.
Exécution en mode batch d'un code.
String m_case_name
Nom du cas.
void doAbort() override
Effectue un abort.
Ref< ICodeService > m_code_service
Service du code.
bool m_init_only
true si on ne fait que l'initialisation.
ArcaneMainBatchProperties m_properties
Session.
void initialize() override
Initialise l'instance. L'instance n'est pas utilisable tant que cette méthode n'a pas été appelée.
int execute() override
Lance l'exécution. Cette méthode ne retourne que lorsqu'on quitte le programme.
bool m_check_case_only
true si on ne fait que vérifier le jeu de données.
void finalize() override
Effectue les dernières opérations avant destruction de l'instance.
bool m_has_sub_domain_threads
indique si on utilise des threads pour gérer des sous-domaines
void build() override
Construit les membres la classe. L'instance n'est pas utilisable tant que cette méthode n'a pas été a...
bool parseArgs(StringList args) override
Analyse les arguments.
Classe de gestion de l'exécution.
Definition ArcaneMain.h:80
const ApplicationInfo & applicationInfo() const override
Informations sur l'éxécutable.
static int callFunctorWithCatchedException(IFunctor *functor, IArcaneMain *amain, bool *clean_abort, bool is_print=true)
Appelle le fonctor functor en récupérant les éventuelles exceptions.
IApplication * application() const override
Application.
Definition ArcaneMain.h:321
void build() override
Construit les membres la classe. L'instance n'est pas utilisable tant que cette méthode n'a pas été a...
const ApplicationBuildInfo & applicationBuildInfo() const override
Informations pour construire l'instance IApplication.
int errorCode() const override
Code d'erreur de l'exécution.
Definition ArcaneMain.h:311
void initialize() override
Initialise l'instance. L'instance n'est pas utilisable tant que cette méthode n'a pas été appelée.
bool parseArgs(StringList args) override
Analyse les arguments.
Source d'un jeu de données d'un cas.
void setFileName(const String &name)
Positionne le nom du fichier du jeu de données.
String fileName() const
Nom du fichier du jeu de données.
void setContent(Span< const std::byte > bytes)
Positionne le contenu du jeu de données.
ByteConstSpan content() const
Contenu du jeu de données.
Integer count() const
Nombre d'éléments de la collection.
Definition Collection.h:70
Interface de l'application.
virtual ITraceMngPolicy * getTraceMngPolicy()=0
Politique de configuration d'un gestionnaire de trace.
virtual IMainFactory * mainFactory() const =0
Manufacture principale.
virtual IIOMng * ioMng()=0
Gestionnaire des entrées/sorties.
virtual const ApplicationBuildInfo & applicationBuildInfo() const =0
Informations sur les paramètres de construction de l'instance.
virtual void removeSession(ISession *session)=0
Supprime la session session.
virtual Ref< ICodeService > getCodeService(const String &file_name)=0
Retourne le chargeur de cas correspondant au fichier donné par file_name.
virtual IParallelSuperMng * parallelSuperMng()=0
Gestionnaire superviseur du parallélisme.
Interface de la classe de gestion du code.
Definition IArcaneMain.h:54
virtual ITraceMng * traceMng() const =0
Gestionnaire de traces.
Interface d'un fonctor pour exécuter du code directement après la création d'un sous-domaine sans pas...
virtual int execute()=0
Exécute l'opération du fonctor.
virtual void setSubDomain(ISubDomain *sd)=0
Positionne le sous-domaine associé. Cette méthode doit être appelée avant execute()
Interface du gestionnaire des entrées sorties.
Definition IIOMng.h:42
Manufacture des classes d'Arcane.
Interface du gestionnaire de parallélisme pour un sous-domaine.
virtual IParallelReplication * replication() const =0
Informations sur la réplication.
virtual IThreadMng * threadMng() const =0
Gestionnaire de threads.
virtual void setReplication(IParallelReplication *v)=0
Positionne les Informations sur la réplication.
virtual Ref< IParallelMng > createSubParallelMngRef(Int32ConstArrayView kept_ranks)=0
Créé un nouveau gestionnaire de parallélisme pour un sous-ensemble des rangs.
virtual void setTimeStats(ITimeStats *time_stats)=0
Positionne le gestionnaire de statistiques.
virtual void barrier()=0
Effectue une barière.
Classe abstraite du superviseur de parallélisme.
Interface d'un service de de trace des appels de fonctions.
Interface d'une session d'exécution d'un cas.
Definition ISession.h:44
virtual void doAbort()=0
Effectue un abort.
virtual void endSession(int ret_val)=0
Termine la session avec le code de retour ret_val.
Interface du gestionnaire d'un sous-domaine.
Definition ISubDomain.h:74
Interface gérant les statistiques sur les temps d'exécution.
Definition ITimeStats.h:48
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
Exception lorsqu'une erreur fatale 'parallèle' est générée.
@ TimerReal
Timer utilisant le temps réel.
Definition Timer.h:76
Exception lorsqu'un argument est invalide.
Interface du gestionnaire de traces.
virtual TraceMessage info()=0
Flot pour un message d'information.
virtual TraceMessage pfatal()=0
Flot pour un message d'erreur fatale parallèle.
Chaîne de caractères unicode.
bool empty() const
Vrai si la chaîne est vide (nulle ou "")
Definition String.cc:315
bool null() const
Retourne true si la chaîne est nulle.
Definition String.cc:304
ITraceMng * traceMng() const
Gestionnaire de trace.
TraceMessage info() const
Flot pour un message d'information.
Integer toInteger(const char *str, bool *is_ok=0)
Converti la chaîne str en un entier non signé. Si is_ok n'est pas nul, il vaut true en retour si la c...
IProcessorAffinityService * getProcessorAffinityService()
Service utilisé pour la gestion de l'affinité des processeurs.
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
UniqueArray< Int32 > Int32UniqueArray
Tableau dynamique à une dimension d'entiers 32 bits.
Definition UtilsTypes.h:515
List< String > StringList
Tableau de chaînes de caractères unicode.
Definition UtilsTypes.h:667
Int32 Integer
Type représentant un entier.