Geant4-11
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
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
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
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
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.
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 PersistentSettings<Idx>* _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) // below uses comma operator to assign to false before return
396 return ((_first = false), _instance);
397 // if not first, make a copy from the primary thread
398 return new PersistentSettings<Idx>(*_instance);
399 }();
400 return *_tlinstance;
401}
402
403// ----------------------------------------------------------------------
404
405template <size_t Cat>
407{
408 delete m_bundle;
409}
410
411//----------------------------------------------------------------------------//
412// primary (utilized) versions
413//
414template <size_t Cat>
417{
418 return QueryHandler_t{ GetPersistent<G4ProfileOp::Query>().m_functor };
419}
420
421//----------------------------------------------------------------------------//
422
423template <size_t Cat>
426{
427 return LabelHandler_t{ GetPersistent<G4ProfileOp::Label>().m_functor };
428}
429
430//----------------------------------------------------------------------------//
431
432template <size_t Cat>
435{
436 return ToolHandler_t{ GetPersistent<G4ProfileOp::Tool>().m_functor };
437}
438
439//----------------------------------------------------------------------------//
440// fallback versions
441//
442template <size_t Cat>
445{
446 return QueryHandler_t{
447 GetPersistentFallback<G4ProfileOp::Query>().m_functor
448 };
449}
450
451//----------------------------------------------------------------------------//
452
453template <size_t Cat>
456{
457 return LabelHandler_t{
458 GetPersistentFallback<G4ProfileOp::Label>().m_functor
459 };
460}
461
462//----------------------------------------------------------------------------//
463
464template <size_t Cat>
467{
468 return ToolHandler_t{ GetPersistentFallback<G4ProfileOp::Tool>().m_functor };
469}
470
471//---------------------------------------------------------------------------//
472
473class G4Run;
474class G4Event;
475class G4Track;
476class G4Step;
477
484
485//---------------------------------------------------------------------------//
486
487// force instantiations
498 const std::string&);
499
500#define G4PROFILE_INSTANTIATION(TYPE) \
501 template TYPE::PersistentSettings<G4ProfileOp::Query>& \
502 TYPE::GetPersistentFallback<G4ProfileOp::Query>(); \
503 template TYPE::PersistentSettings<G4ProfileOp::Label>& \
504 TYPE::GetPersistentFallback<G4ProfileOp::Label>(); \
505 template TYPE::PersistentSettings<G4ProfileOp::Tool>& \
506 TYPE::GetPersistentFallback<G4ProfileOp::Tool>(); \
507 \
508 template TYPE::PersistentSettings<G4ProfileOp::Query>& \
509 TYPE::GetPersistent<G4ProfileOp::Query>(); \
510 template TYPE::PersistentSettings<G4ProfileOp::Label>& \
511 TYPE::GetPersistent<G4ProfileOp::Label>(); \
512 template TYPE::PersistentSettings<G4ProfileOp::Tool>& \
513 TYPE::GetPersistent<G4ProfileOp::Tool>();
514
520
521//---------------------------------------------------------------------------//
522
523#if !defined(GEANT4_USE_TIMEMORY)
524# define TIMEMORY_WEAK_PREFIX
525# define TIMEMORY_WEAK_POSTFIX
526#endif
527
528//---------------------------------------------------------------------------//
529
530extern "C"
531{
532 // this allows the default setup to be overridden by linking
533 // in an custom extern C function into the application
536
537 // this gets executed when the library gets loaded
538 void G4ProfilerInit(void)
539 {
540#ifdef GEANT4_USE_TIMEMORY
541
542 // guard against re-initialization
543 static bool _once = false;
544 if(_once)
545 return;
546 _once = true;
547
548 puts(">>> G4ProfilerInit <<<");
549
550 //
551 // the default settings
552 //
553 // large profiles can take a very long time to plot
554 tim::settings::plot_output() = false;
555 // large profiles can take quite a bit of console space
556 tim::settings::cout_output() = false;
557 // this creates a subdirectory with the timestamp of the run
558 tim::settings::time_output() = false;
559 // see `man 3 strftime` for formatting keys
560 tim::settings::time_format() = "%F_%I.%M_%p";
561 // set the default precision for timing
562 tim::settings::timing_precision() = 6;
563 // set the minimum width for outputs
564 tim::settings::width() = 12;
565 // set dart reports (when enabled) to only print the first entry
566 tim::settings::dart_count() = 1;
567 // set dart reports (when enabled) to use the component label
568 // instead of the string identifer of the entry, e.g.
569 // >>> G4Run/0 ... peak_rss ... 50 MB would report
570 // 'peak_rss 50 MB' not 'G4Run/0 50 MB'
571 tim::settings::dart_label() = true;
572
573 // allow environment overrides of the defaults
574 tim::settings::parse();
575#endif
576 }
577} // extern "C"
578
579//---------------------------------------------------------------------------//
580
581#ifdef GEANT4_USE_TIMEMORY
582namespace
583{
584 static bool profiler_is_initialized = (G4ProfilerInit(), true);
585}
586#endif
587
588//---------------------------------------------------------------------------//
TIMEMORY_WEAK_PREFIX void G4ProfilerInit(void) TIMEMORY_WEAK_POSTFIX
Definition: G4Profiler.cc:538
#define G4PROFILE_INSTANTIATION(TYPE)
Definition: G4Profiler.cc:500
#define TIMEMORY_WEAK_PREFIX
Definition: G4Profiler.cc:524
#define TIMEMORY_WEAK_POSTFIX
Definition: G4Profiler.cc:525
static char ** args
Definition: G4Xt.cc:51
FuncHandler< this_type, LabelFunc_t, std::string > LabelHandler_t
Definition: G4Profiler.hh:398
static QueryHandler_t GetFallbackQueryFunctor()
Definition: G4Profiler.cc:444
FuncHandler< this_type, ToolFunc_t, type * > ToolHandler_t
Definition: G4Profiler.hh:399
static PersistentSettings< Idx > & GetPersistentFallback()
static ToolHandler_t GetToolFunctor()
Definition: G4Profiler.cc:434
static ToolHandler_t GetFallbackToolFunctor()
Definition: G4Profiler.cc:466
static PersistentSettings< Idx > & GetPersistent()
static QueryHandler_t GetQueryFunctor()
Definition: G4Profiler.cc:416
static LabelHandler_t GetLabelFunctor()
Definition: G4Profiler.cc:425
static LabelHandler_t GetFallbackLabelFunctor()
Definition: G4Profiler.cc:455
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 void Finalize()
Definition: G4Profiler.cc:331
static array_type & GetEnabled()
Definition: G4Profiler.cc:43
Definition: G4Run.hh:49
Definition: G4Step.hh:62
void consume_parameters(Args &&...)
Definition: Globals.hh:80
def exit()
Definition: g4zmq.py:99