Geant4 11.1.1
Toolkit for the simulation of the passage of particles through matter
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
G4Profiler.cc
Go to the documentation of this file.
1//
2// ********************************************************************
3// * License and Disclaimer *
4// * *
5// * The Geant4 software is copyright of the Copyright Holders of *
6// * the Geant4 Collaboration. It is provided under the terms and *
7// * conditions of the Geant4 Software License, included in the file *
8// * LICENSE and available at http://cern.ch/geant4/license . These *
9// * include a list of copyright holders. *
10// * *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work make any representation or warranty, express or implied, *
14// * regarding this software system or assume any liability for its *
15// * use. Please see the license in the file LICENSE and URL above *
16// * for the full disclaimer and the limitation of liability. *
17// * *
18// * This code implementation is the result of the scientific and *
19// * technical work of the GEANT4 collaboration. *
20// * By using, copying, modifying or distributing the software (or *
21// * any work based on the software) you agree to acknowledge its *
22// * use in resulting scientific publications, and indicate your *
23// * acceptance of all terms of the Geant4 Software license. *
24// ********************************************************************
25//
26// G4Profiler class implementation
27//
28// Author: J.Madsen (LBL), Aug 2020
29// --------------------------------------------------------------------
30
31#include "G4Profiler.hh"
32#include "G4TiMemory.hh"
33
34#if defined(GEANT4_USE_TIMEMORY)
35# include <timemory/runtime/configure.hpp>
36#endif
37
38#include <regex>
39#include <thread>
40
41//---------------------------------------------------------------------------//
42
44{
45 static array_type _instance = []() {
46 array_type _tmp{};
47 _tmp.fill(false);
48 // environment settings introduce the defaults
49 // but will be overridden by messenger and/or command line
50 _tmp.at(G4ProfileType::Run) = G4GetEnv<bool>("G4PROFILE_RUN", true);
51 _tmp.at(G4ProfileType::Event) = G4GetEnv<bool>("G4PROFILE_EVENT", false);
52 _tmp.at(G4ProfileType::Track) = G4GetEnv<bool>("G4PROFILE_TRACK", false);
53 _tmp.at(G4ProfileType::Step) = G4GetEnv<bool>("G4PROFILE_STEP", false);
54 _tmp.at(G4ProfileType::User) = G4GetEnv<bool>("G4PROFILE_USER", true);
55 return _tmp;
56 }();
57 return _instance;
58}
59
60//---------------------------------------------------------------------------//
61
62void G4Profiler::Configure(int argc, char** argv)
63{
64#if defined(GEANT4_USE_TIMEMORY)
65 tim::timemory_init(argc, argv);
66 std::vector<std::string> _args;
67 for(int i = 0; i < argc; ++i)
68 _args.push_back(argv[i]);
69 Configure(_args);
70#else
71 G4Impl::consume_parameters(argc, argv);
72#endif
73}
74//---------------------------------------------------------------------------//
75
76void G4Profiler::Configure(ArgumentParser& parser, int argc, char** argv)
77{
78#if defined(GEANT4_USE_TIMEMORY)
79 tim::timemory_init(argc, argv);
80 std::vector<std::string> _args;
81 for(int i = 0; i < argc; ++i)
82 _args.push_back(argv[i]);
83 Configure(parser, _args);
84#else
85 G4Impl::consume_parameters(parser, argc, argv);
86#endif
87}
88
89//---------------------------------------------------------------------------//
90
91void G4Profiler::Configure(const std::vector<std::string>& args)
92{
93#if defined(GEANT4_USE_TIMEMORY)
94 if(args.empty())
95 return;
96 ArgumentParser p{ args.at(0) };
97 Configure(p, args);
98#else
99 G4Impl::consume_parameters(args);
100#endif
101}
102
103//---------------------------------------------------------------------------//
104
106 const std::vector<std::string>& args)
107{
108#if defined(GEANT4_USE_TIMEMORY)
109 using parser_t = ArgumentParser;
110 using parser_err_t = typename parser_t::result_type;
111
112 if(args.empty())
113 return;
114
115 static std::mutex mtx;
116 std::unique_lock<std::mutex> lk(mtx);
117
118 static auto tid = std::this_thread::get_id();
119 if(std::this_thread::get_id() != tid)
120 return;
121
122 // std::cout << "Arguments:";
123 // for(auto itr : args)
124 // std::cout << " " << itr;
125 // std::cout << std::endl;
126
127 auto help_action = [](parser_t& p) {
128 p.print_help();
129 exit(EXIT_FAILURE);
130 };
131
132 parser.enable_help();
133 parser.on_error([=](parser_t& p, parser_err_t _err) {
134 std::cerr << _err << std::endl;
135 help_action(p);
136 });
137
138 auto get_bool = [](const std::string& _str, bool _default) {
139 if(_str.empty())
140 return _default;
141 using namespace std::regex_constants;
142 const auto regex_config = egrep | icase;
143 if(std::regex_match(_str, std::regex("on|true|yes|1", regex_config)))
144 return true;
145 else if(std::regex_match(_str, std::regex("off|false|no|0", regex_config)))
146 return false;
147 return _default;
148 };
149 //
150 // Collection control
151 //
152 parser.add_argument()
153 .names({ "-p", "--profile" })
154 .description("Profiler modes")
155 .choices({ "run", "event", "track", "step", "user" })
156 .action([&](parser_t& p) {
157 using namespace std::regex_constants;
158 const auto regex_config = egrep | icase;
159 for(auto&& itr : p.get<std::vector<std::string>>("profile"))
160 {
161 if(std::regex_match(itr, std::regex("run", regex_config)))
163 else if(std::regex_match(itr, std::regex("event", regex_config)))
165 else if(std::regex_match(itr, std::regex("track", regex_config)))
167 else if(std::regex_match(itr, std::regex("step", regex_config)))
169 else if(std::regex_match(itr, std::regex("user", regex_config)))
171 }
172 });
173 //
174 // Component controls
175 //
176 parser.add_argument()
177 .names({ "-r", "--run-components" })
178 .description("Components for run profiling (see 'timemory-avail -s')")
179 .action([&](parser_t& p) {
181 tim::configure<G4RunProfiler>(
182 p.get<std::vector<std::string>>("run-components"));
183 });
184 parser.add_argument()
185 .names({ "-e", "--event-components" })
186 .description("Components for event profiling (see 'timemory-avail -s')")
187 .action([&](parser_t& p) {
189 tim::configure<G4EventProfiler>(
190 p.get<std::vector<std::string>>("event-components"));
191 });
192 parser.add_argument()
193 .names({ "-t", "--track-components" })
194 .description("Components for track profiling (see 'timemory-avail -s')")
195 .action([&](parser_t& p) {
197 tim::configure<G4TrackProfiler>(
198 p.get<std::vector<std::string>>("track-components"));
199 });
200 parser.add_argument()
201 .names({ "-s", "--step-components" })
202 .description("Components for step profiling (see 'timemory-avail -s')")
203 .action([&](parser_t& p) {
205 tim::configure<G4StepProfiler>(
206 p.get<std::vector<std::string>>("step-components"));
207 });
208 parser.add_argument()
209 .names({ "-u", "--user-components" })
210 .description("Components for user profiling (see 'timemory-avail -s')")
211 .action([&](parser_t& p) {
213 tim::configure<G4UserProfiler>(
214 p.get<std::vector<std::string>>("user-components"));
215 });
216 //
217 // Display controls
218 //
219 parser.add_argument()
220 .names({ "-H", "--hierarchy", "--tree" })
221 .description("Display the results as a call-stack hierarchy.")
222 .max_count(1)
223 .action([&](parser_t& p) {
224 tim::settings::flat_profile() =
225 !get_bool(p.get<std::string>("tree"), true);
226 });
227 parser.add_argument()
228 .names({ "-F", "--flat" })
229 .description("Display the results as a flat call-stack")
230 .max_count(1)
231 .action([&](parser_t& p) {
232 tim::settings::flat_profile() =
233 get_bool(p.get<std::string>("flat"), true);
234 });
235 parser.add_argument()
236 .names({ "-T", "--timeline" })
237 .description(
238 "Do not merge duplicate entries at the same call-stack position")
239 .max_count(1)
240 .action([&](parser_t& p) {
241 tim::settings::timeline_profile() =
242 get_bool(p.get<std::string>("timeline"), true);
243 });
244 parser.add_argument()
245 .names({ "--per-thread" })
246 .description(
247 "Display the results for each individual thread (default: aggregation)")
248 .max_count(1)
249 .action([&](parser_t& p) {
250 tim::settings::flat_profile() =
251 get_bool(p.get<std::string>("per-thread"), true);
252 });
253 parser.add_argument()
254 .names({ "--per-event" })
255 .description("Each G4Event is a unique entry")
256 .max_count(1)
257 .action([&](parser_t& p) {
258 tim::settings::timeline_profile() =
259 get_bool(p.get<std::string>("per-event"), true);
260 });
261 //
262 // Output controls
263 //
264 parser.add_argument()
265 .names({ "-D", "--dart" })
266 .description("Enable Dart output (CTest/CDash data tracking)")
267 .max_count(1)
268 .action([&](parser_t& p) {
269 tim::settings::dart_output() = get_bool(p.get<std::string>("dart"), true);
270 });
271 parser.add_argument()
272 .names({ "-J", "--json" })
273 .description("Enable JSON output")
274 .max_count(1)
275 .action([&](parser_t& p) {
276 tim::settings::json_output() = get_bool(p.get<std::string>("json"), true);
277 });
278 parser.add_argument()
279 .names({ "-P", "--plot" })
280 .description("Plot the JSON output")
281 .max_count(1)
282 .action([&](parser_t& p) {
283 tim::settings::plot_output() = get_bool(p.get<std::string>("plot"), true);
284 });
285 parser.add_argument()
286 .names({ "-X", "--text" })
287 .description("Enable TEXT output")
288 .max_count(1)
289 .action([&](parser_t& p) {
290 tim::settings::text_output() = get_bool(p.get<std::string>("text"), true);
291 });
292 parser.add_argument()
293 .names({ "-C", "--cout" })
294 .description("Enable output to terminal")
295 .max_count(1)
296 .action([&](parser_t& p) {
297 tim::settings::cout_output() = get_bool(p.get<std::string>("cout"), true);
298 });
299 parser.add_argument()
300 .names({ "-O", "--output-path" })
301 .description("Set the output directory")
302 .count(1)
303 .action([&](parser_t& p) {
304 tim::settings::output_path() = p.get<std::string>("output-path");
305 });
306 parser.add_argument()
307 .names({ "-W", "--hw-counters" })
308 .description(
309 "Set the hardware counters to collect (see 'timemory-avail -H')")
310 .action([&](parser_t& p) {
311 tim::settings::papi_events() = p.get<std::string>("hw-counters");
312 });
313
314 tim::settings::time_output() = true;
315 tim::settings::time_format() = "%F_%I.%M_%p";
316
317 auto err = parser.parse(args);
318 if(err)
319 {
320 std::cout << "Error! " << err << std::endl;
321 help_action(parser);
322 }
323
324#else
325 G4Impl::consume_parameters(parser, args);
326#endif
327}
328
329//---------------------------------------------------------------------------//
330
332{
333#if defined(GEANT4_USE_TIMEMORY)
334 // generally safe to call multiple times but just in case
335 // it is not necessary to call from worker threads, calling
336 // once on master will merge any threads that accumulated
337 // results. If a thread is destroyed, before finalize is
338 // called on master thread, then it will merge itself into
339 // the master thread before terminating. The biggest issue
340 // is when finalize is never called on any threads (including
341 // the master) so finalization happens after main exits. When
342 // this happens, the order that data gets deleted is very
343 // hard to predict so it commonly results in a segfault. Thus,
344 // if the user does not call this function and does not
345 // delete G4RunManager, a segfault is quite likely.
346 static thread_local bool _once = false;
347 if(!_once)
348 tim::timemory_finalize();
349 _once = true;
350#endif
351}
352
353//---------------------------------------------------------------------------//
354
355template <size_t Ct>
356template <int Idx>
357typename G4ProfilerConfig<Ct>::template PersistentSettings<Idx>&
359{
360 // G4RunManager::ConfigureProfilers() assigns defaults to these lambdas
361 // based on environment variables. The users should (generally) not modify
362 // these.
363 static PersistentSettings<Idx> _instance = PersistentSettings<Idx>{};
364 return _instance;
365}
366
367//---------------------------------------------------------------------------//
368
369template <size_t Ct>
370template <int Idx>
371typename G4ProfilerConfig<Ct>::template PersistentSettings<Idx>&
373{
374 //
375 // The pointers here automatically initialize on the first
376 // invocation on a thread and a reference is returned so they
377 // can be cleanly "leaked" at the application termination. Assignment
378 // is thread-safe and this scheme avoids having to deal with getters
379 // and setters. It is designed such that we can set defaults but
380 // the user can override them at any time.
381 //
382 // the first global instance copies from the fallback rountines, which are
383 // assigned defaults in G4RunManager::ConfigureProfilers() but if the user
384 // assigns new settings before creating G4RunManager, those defaults will
385 // be ignored
386 static auto* _instance =
387 new PersistentSettings<Idx>(GetPersistentFallback<Idx>());
388 static thread_local PersistentSettings<Idx>* _tlinstance = [=]() {
389 static std::mutex mtx;
390 std::unique_lock<std::mutex> lk(mtx);
391 // If this is the primary thread, just return the global instance from
392 // above. Modifying that instance will result in all threads created later
393 // to inherit those settings
394 static bool _first = true;
395 if(_first)
396 { // below uses comma operator to assign to false before return
397 return ((_first = false), _instance);
398 }
399 // if not first, make a copy from the primary thread
400 return new PersistentSettings<Idx>(*_instance);
401 }();
402 return *_tlinstance;
403}
404
405// ----------------------------------------------------------------------
406
407template <size_t Cat>
409{
410 delete m_bundle;
411}
412
413//----------------------------------------------------------------------------//
414// primary (utilized) versions
415//
416template <size_t Cat>
419{
420 return QueryHandler_t{ GetPersistent<G4ProfileOp::Query>().m_functor };
421}
422
423//----------------------------------------------------------------------------//
424
425template <size_t Cat>
428{
429 return LabelHandler_t{ GetPersistent<G4ProfileOp::Label>().m_functor };
430}
431
432//----------------------------------------------------------------------------//
433
434template <size_t Cat>
437{
438 return ToolHandler_t{ GetPersistent<G4ProfileOp::Tool>().m_functor };
439}
440
441//----------------------------------------------------------------------------//
442// fallback versions
443//
444template <size_t Cat>
447{
448 return QueryHandler_t{
449 GetPersistentFallback<G4ProfileOp::Query>().m_functor
450 };
451}
452
453//----------------------------------------------------------------------------//
454
455template <size_t Cat>
458{
459 return LabelHandler_t{
460 GetPersistentFallback<G4ProfileOp::Label>().m_functor
461 };
462}
463
464//----------------------------------------------------------------------------//
465
466template <size_t Cat>
469{
470 return ToolHandler_t{ GetPersistentFallback<G4ProfileOp::Tool>().m_functor };
471}
472
473//---------------------------------------------------------------------------//
474
475class G4Run;
476class G4Event;
477class G4Track;
478class G4Step;
479
486
487//---------------------------------------------------------------------------//
488
489// force instantiations
500 const std::string&);
501
502#define G4PROFILE_INSTANTIATION(TYPE) \
503 template TYPE::PersistentSettings<G4ProfileOp::Query>& \
504 TYPE::GetPersistentFallback<G4ProfileOp::Query>(); \
505 template TYPE::PersistentSettings<G4ProfileOp::Label>& \
506 TYPE::GetPersistentFallback<G4ProfileOp::Label>(); \
507 template TYPE::PersistentSettings<G4ProfileOp::Tool>& \
508 TYPE::GetPersistentFallback<G4ProfileOp::Tool>(); \
509 \
510 template TYPE::PersistentSettings<G4ProfileOp::Query>& \
511 TYPE::GetPersistent<G4ProfileOp::Query>(); \
512 template TYPE::PersistentSettings<G4ProfileOp::Label>& \
513 TYPE::GetPersistent<G4ProfileOp::Label>(); \
514 template TYPE::PersistentSettings<G4ProfileOp::Tool>& \
515 TYPE::GetPersistent<G4ProfileOp::Tool>();
516
522
523//---------------------------------------------------------------------------//
524
525#if !defined(GEANT4_USE_TIMEMORY)
526# define TIMEMORY_WEAK_PREFIX
527# define TIMEMORY_WEAK_POSTFIX
528#endif
529
530//---------------------------------------------------------------------------//
531
532extern "C"
533{
534 // this allows the default setup to be overridden by linking
535 // in an custom extern C function into the application
538
539 // this gets executed when the library gets loaded
540 void G4ProfilerInit(void)
541 {
542#ifdef GEANT4_USE_TIMEMORY
543
544 // guard against re-initialization
545 static bool _once = false;
546 if(_once)
547 return;
548 _once = true;
549
550 puts(">>> G4ProfilerInit <<<");
551
552 //
553 // the default settings
554 //
555 // large profiles can take a very long time to plot
556 tim::settings::plot_output() = false;
557 // large profiles can take quite a bit of console space
558 tim::settings::cout_output() = false;
559 // this creates a subdirectory with the timestamp of the run
560 tim::settings::time_output() = false;
561 // see `man 3 strftime` for formatting keys
562 tim::settings::time_format() = "%F_%I.%M_%p";
563 // set the default precision for timing
564 tim::settings::timing_precision() = 6;
565 // set the minimum width for outputs
566 tim::settings::width() = 12;
567 // set dart reports (when enabled) to only print the first entry
568 tim::settings::dart_count() = 1;
569 // set dart reports (when enabled) to use the component label
570 // instead of the string identifer of the entry, e.g.
571 // >>> G4Run/0 ... peak_rss ... 50 MB would report
572 // 'peak_rss 50 MB' not 'G4Run/0 50 MB'
573 tim::settings::dart_label() = true;
574
575 // allow environment overrides of the defaults
576 tim::settings::parse();
577#endif
578 }
579} // extern "C"
580
581//---------------------------------------------------------------------------//
582
583#ifdef GEANT4_USE_TIMEMORY
584namespace
585{
586 static bool profiler_is_initialized = (G4ProfilerInit(), true);
587}
588#endif
589
590//---------------------------------------------------------------------------//
TIMEMORY_WEAK_PREFIX void G4ProfilerInit(void) TIMEMORY_WEAK_POSTFIX
Definition: G4Profiler.cc:540
#define G4PROFILE_INSTANTIATION(TYPE)
Definition: G4Profiler.cc:502
#define TIMEMORY_WEAK_PREFIX
Definition: G4Profiler.cc:526
#define TIMEMORY_WEAK_POSTFIX
Definition: G4Profiler.cc:527
FuncHandler< this_type, LabelFunc_t, std::string > LabelHandler_t
Definition: G4Profiler.hh:398
static QueryHandler_t GetFallbackQueryFunctor()
Definition: G4Profiler.cc:446
FuncHandler< this_type, ToolFunc_t, type * > ToolHandler_t
Definition: G4Profiler.hh:399
static ToolHandler_t GetToolFunctor()
Definition: G4Profiler.cc:436
static ToolHandler_t GetFallbackToolFunctor()
Definition: G4Profiler.cc:468
static QueryHandler_t GetQueryFunctor()
Definition: G4Profiler.cc:418
static LabelHandler_t GetLabelFunctor()
Definition: G4Profiler.cc:427
static LabelHandler_t GetFallbackLabelFunctor()
Definition: G4Profiler.cc:457
G4ProfilerConfig()=default
FuncHandler< this_type, QueryFunc_t, bool > QueryHandler_t
Definition: G4Profiler.hh:397
static void Configure(const std::vector< std::string > &args)
Definition: G4Profiler.cc:91
static void SetEnabled(size_t v, bool val)
Definition: G4Profiler.hh:113
std::array< bool, G4ProfileType::TypeEnd > array_type
Definition: G4Profiler.hh:95
static bool GetEnabled(size_t v)
Definition: G4Profiler.hh:112
static void Finalize()
Definition: G4Profiler.cc:331
Definition: G4Run.hh:49
Definition: G4Step.hh:62