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"
40#include "arcane/impl/ArcaneMain.h"
41#include "arcane/impl/ParallelReplication.h"
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"
64#include "arcane/ServiceUtils.h"
66#include "arcane/impl/ExecutionStatsDumper.h"
67#include "arcane/impl/TimeLoopReader.h"
69#include "arcane/accelerator/core/internal/RunnerInternal.h"
87 Int32 m_max_iteration = 0;
88 bool m_is_continue =
false;
110 : m_sub_domain(
nullptr), m_time_stats(
nullptr), m_want_print_stats(
false) {}
121 void operator=(
const SubInfo&) =
delete;
127 bool m_want_print_stats;
132 : m_arcane_main(arcane_main),
135 m_direct_test_name(m_arcane_main->m_direct_test_name),
139 m_direct_sub_domain_execute_functor(m_arcane_main->_directExecuteFunctor())
145 m_sub_infos.fill(
nullptr);
149 for( Integer i=0, n=m_sub_infos.size(); i<n; ++i )
150 delete m_sub_infos[i];
154 void executeRank(Int32 local_rank);
158 ArcaneMainBatch* m_arcane_main;
161 String m_direct_test_name;
179 : m_session_exec(session_exec), m_local_rank(local_rank)
185 m_session_exec->executeRank(m_local_rank);
197 void build()
override;
212 String m_direct_exec_name;
213 String m_direct_test_name;
235ArcaneMainBatch(
const ApplicationInfo& exe_info,IMainFactory* main_factory)
236: ArcaneMain(exe_info,main_factory)
238, m_check_case_only(false)
239, m_has_sub_domain_threads(false)
283 bool r = _sequentialParseArgs(args);
292bool ArcaneMainBatch::
322 trace->
info() <<
"Usage: programm input_data ; for more information: program -arcane_opt help";
323 trace->
pfatal() <<
"No input data specified.";
327 StringBuilder tool_args_xml;
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())
336 for( Integer i=1, s=nb_arg-1; i<s; ++i ){
338 if (args[i]!=us_arcane_opt){
339 unknown_args.add(args[i]);
342 bool is_valid_opt =
false;
347 if (str==us_init_only){
351 else if (str==us_check_case_only){
355 else if (str==us_continue){
359 else if (str==us_max_iteration){
367 trace->
pfatal() <<
"Option 'max_iteration' must specify the number of iterations";
370 else if (str==us_casename){
377 else if (str==us_direct_exec){
380 m_direct_exec_name = args[i];
385 else if (str==us_direct_test){
388 m_direct_test_name = args[i];
393 else if (str==us_tool_arg){
404 tool_args_xml += String::format(
"<{0}>{1}</{2}>\n",arg,value,arg);
407 else if (str==us_nb_sub_domain){
410 nb_sub_domain_str = args[i];
415 else if (str==us_nb_replication){
418 nb_replication_str = args[i];
423 else if (str==us_idle_service){
432 trace->
pfatal() <<
"Unknown Arcane option <" << str <<
">\n";
436 bool use_direct_test = (!m_direct_test_name.
null());
437 bool use_direct_exec = (!m_direct_exec_name.
null());
439 if (use_direct_test){
441 else if (use_direct_exec){
444 tool_mesh = args[nb_arg-1];
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;
461 trace->
info() <<
"Use '" << nb_sub_domain <<
"' subdomains";
462 _applicationBuildInfo().setNbProcessusSubDomain(nb_sub_domain);
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;
471 trace->
info() <<
"Use replication of subdomains nb_replication=" << nb_replication;
472 _applicationBuildInfo().setNbReplicationSubDomain(nb_replication);
475 if (_applicationBuildInfo().nbReplicationSubDomain()!=0 && _applicationBuildInfo().nbProcessusSubDomain()!=0)
476 trace->
pfatal() <<
"The subdomains number of replication and restriction options are incompatible.";
478 if (!use_direct_test){
479 String case_file = dataset_source.
fileName();
484 trace->
info() <<
"The file `" << case_file <<
"' is not a known file type.";
485 case_file = args[nb_arg-2];
489 trace->
pfatal() <<
"File extension not valid.";
494 if (use_direct_exec){
499 s +=
"<?xml version=\"1.0\"?>\n";
500 s +=
"<case codename=\"ArcaneDriver\" xml:lang=\"en\" codeversion=\"1.0\">";
502 s +=
" <title>DirectExec</title>\n";
503 s +=
" <description>DirectExec</description>\n";
504 s +=
" <timeloop>ArcaneDirectExecutionLoop</timeloop>\n";
507 s += String::format(
" <file>{0}</file>\n",tool_mesh);
509 s +=
" <arcane-direct-execution>\n";
510 s += String::format(
" <tool name='{0}'>\n",m_direct_exec_name);
513 s +=
" </arcane-direct-execution>\n";
518 trace->
info() <<
"Direct exec xml file=" << s;
527 if (!unknown_args.empty()){
528 trace->
info()<<
"Unknown command line option: " << unknown_args[0];
539struct LaunchThreadInfo
541 ArcaneMainBatch* arcane_main;
542 ArcaneMainBatch::SessionExec* session_exec;
543 IApplication* application;
554_ThreadWrapper(LaunchThreadInfo* lti)
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;
562 if (r!=0 && !clean_abort){
568 IParallelSuperMng* psm = main_app->parallelSuperMng();
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;
624 gths[i] =
new std::thread(_ThreadWrapper,&
thinfo[i]);
633 m_session_exec->executeRank(0);
648void ArcaneMainBatch::SessionExec::
649executeRank(Int32 local_rank)
662 if (!platform::getEnvironmentVariable(
"ARCANE_BIND_THREADS").null()){
664 tm->
info() <<
"Binding threads";
665 pas->bindThread(local_rank);
671 Ref<IParallelMng> world_pm = psm->internalCreateWorldParallelMng(local_rank);
672 sub_info->m_world_parallel_mng = world_pm;
674 if (!m_direct_test_name.
null()){
675 _execDirectTest(world_pm.get(),m_direct_test_name,
true);
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);
695 Ref<IParallelMng> pm = world_pm;
696 Ref<IParallelMng> all_replica_pm = pm;
701 if (world_pm->isParallel()){
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);
716 Ref<IParallelMng> replicate_pm;
717 trace->info() <<
"Building replicated parallel mng";
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];
725 Ref<IParallelMng> new_pm = world_pm->createSubParallelMngRef(kept_ranks);
727 replicate_pm = new_pm;
728 replicate_pm->setTimeStats(time_stat);
729 trace->info() <<
" Building own replicated parallel mng";
732 trace->info()<<
"!pm";
743 trace->info() <<
"Building sub-domain parallel mng";
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];
751 Ref<IParallelMng> new_pm = world_pm->createSubParallelMngRef(kept_ranks);
758 pm = new_pm->sequentialParallelMngRef();
760 trace->
info()<<
"pm: setting time_stat & m_rank_parallel_mng for replica rank=" << i_repl;
763 sub_info->m_rank_parallel_mng = new_pm;
764 auto pr =
new ParallelReplication(i_repl,nb_wanted_replication,replicate_pm);
768 trace->
info()<<
"!pm";
774 else if (nb_wanted_sub_domain!=0){
775 const Int32 nb_sub_part = nb_wanted_sub_domain;
777 for(
Int32 i=0; i<nb_sub_part; ++i )
781 trace->
info()<<
"pm: setting time_stat & m_rank_parallel_mng";
784 sub_info->m_rank_parallel_mng = pm;
788 trace->info()<<
"!pm";
794 bool print_stats =
false;
795 ISubDomain* sub_domain =
nullptr;
801 trace->info()<<
"The rank doesn't own any subdomain!";
803 trace->info() <<
"No idle service specified"; trace->flush();
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;
820 time_stat->endGatherStats();
822 if (print_stats && sub_domain){
826 IParallelMng* pm = sub_domain->parallelMng();
831 _printStats(sub_domain,trace,time_stat);
836 world_pm->broadcast(UniqueArray<unsigned long>(1,0xdfeb699fl).view(),0);
842void ArcaneMainBatch::SessionExec::
843_createAndRunSubDomain(SubInfo* sub_info,Ref<IParallelMng> pm,Ref<IParallelMng> all_replica_pm,
Int32 local_rank)
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;
853 ITraceMng* trace = _application()->
traceMng();
854 ITraceMng* sd_trace = sub_domain->
traceMng();
859 trace->
info() <<
"REPLICATION: rank=" << pm->
replication()->replicationRank();
862 trace->
info() <<
"Disable output curves for replicates.";
863 sub_domain->timeHistoryMng()->setDumpActive(
false);
873 String cpu_set = pas->cpuSetString();
874 trace->info() <<
" CpuSet=" << cpu_set;
877 if (m_arcane_main->m_check_case_only){
878 trace->info() <<
"Checking the input data";
881 TimeLoopReader stl(_application());
883 stl.registerTimeLoops(sub_domain);
884 stl.setUsedTimeLoop(sub_domain);
886 ICaseMng* cm = sub_domain->caseMng();
887 cm->readOptions(
true);
894 Timer::Action ts_action(sub_domain,
"Init");
895 Timer::Sentry ts(&init_timer);
901 trace->
info() <<
"Option 'max_iteration' activated with " <<
m_properties.m_max_iteration;
909 ArcaneMain::redirectSignals();
911 int ret_compute_loop = 0;
913 IDirectExecution* direct_exec = sub_domain->directExecution();
914 if (direct_exec && direct_exec->isActive()){
915 trace->info() <<
"Direct execution activated";
916 direct_exec->execute();
918 else if (m_arcane_main->m_init_only){
919 trace->info() <<
"Option 'init_only' activated";
920 sub_info->m_want_print_stats =
true;
923 sub_info->m_want_print_stats =
true;
924 Timer::Action ts_action(sub_domain,
"Loop");
925 Timer::Sentry ts(&loop_timer);
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();
935 ret_compute_loop = sub_domain->timeLoopMng()->doComputeLoop(
m_properties.m_max_iteration);
936 if (ret_compute_loop<0)
939 m_arcane_main->setErrorCode(8);
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 <<
" )";
950 Timer::Action ts_action(sub_domain,
"Exit");
951 trace_policy->setDefaultVerboseLevel(sd_trace,Trace::DEFAULT_VERBOSITY_LEVEL);
952 sub_domain->doExitModules();
960void ArcaneMainBatch::SessionExec::
961_printStats(ISubDomain* sub_domain,ITraceMng* trace,ITimeStats* time_stat)
963 ExecutionStatsDumper exec_dumper(trace);
964 exec_dumper.dumpStats(sub_domain,time_stat);
970void ArcaneMainBatch::SessionExec::
971_execDirectTest(IParallelMng* world_pm,
const String& test_name,
bool is_collective)
973 ITraceMng* trace = world_pm->traceMng();
974 trace->info() <<
"Direct test name=" << test_name;
976 ServiceFinder2T<IDirectExecution,IApplication> sf(_application(),_application());
977 Ref<IDirectExecution> exec(sf.createReference(test_name));
979 String msg = String::format(
"Can not find 'IDirectExecution' service name '{0}'",test_name);
981 throw ParallelFatalErrorException(A_FUNCINFO,msg);
983 throw FatalErrorException(A_FUNCINFO,msg);
986 trace->info() <<
"Begin execution of direct service";
989 exec->setParallelMng(world_pm);
1003 m_session =
nullptr;
1006 delete m_session_exec;
1007 m_session_exec =
nullptr;
1033template<
typename V>
void ArcaneMainBatchProperties::
1034_applyPropertyVisitor(
V&
p)
1036 auto b =
p.builder();
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; });
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; });
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; });
1060ARCANE_REGISTER_PROPERTY_CLASS(ArcaneMainBatchProperties,());
#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.
static void stopAllProfiling()
Stoppe toutes les activités de profiling.
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.
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.
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.
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.
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.
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.
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.
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.
Interface gérant les statistiques sur les temps d'exécution.
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Exception lorsqu'une erreur fatale 'parallèle' est générée.
@ TimerReal
Timer utilisant le temps réel.
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 "")
bool null() const
Retourne true si la chaîne est nulle.
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...
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
UniqueArray< Int32 > Int32UniqueArray
Tableau dynamique à une dimension d'entiers 32 bits.
List< String > StringList
Tableau de chaînes de caractères unicode.
Int32 Integer
Type représentant un entier.