Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Main/Walker.cpp
4 : : \copyright 2012-2015 J. Bakosi,
5 : : 2016-2018 Los Alamos National Security, LLC.,
6 : : 2019-2021 Triad National Security, LLC.
7 : : All rights reserved. See the LICENSE file for details.
8 : : \brief Random walker Charm++ main chare
9 : : \details Random walker Charm++ main chare. This file contains the definition
10 : : of the Charm++ main chare, equivalent to main() in Charm++-land.
11 : : */
12 : : // *****************************************************************************
13 : :
14 : : #include <map>
15 : : #include <iostream>
16 : : #include <utility>
17 : : #include <vector>
18 : :
19 : : #include "NoWarning/format.hpp"
20 : :
21 : : #include "NoWarning/charm.hpp"
22 : : #include "NoWarning/pup.hpp"
23 : :
24 : : #include "Print.hpp"
25 : : #include "Timer.hpp"
26 : : #include "Types.hpp"
27 : : #include "Init.hpp"
28 : : #include "WalkerBuildConfig.hpp"
29 : : #include "Tags.hpp"
30 : : #include "ProcessException.hpp"
31 : : #include "RNG.hpp"
32 : : #include "RNGStack.hpp"
33 : : #include "DiffEq.hpp"
34 : : #include "DiffEqStack.hpp"
35 : : #include "Options/RNG.hpp"
36 : : #include "WalkerPrint.hpp"
37 : : #include "WalkerDriver.hpp"
38 : : #include "Walker/CmdLine/Parser.hpp"
39 : : #include "Walker/CmdLine/CmdLine.hpp"
40 : : #include "Walker/InputDeck/InputDeck.hpp"
41 : : #include "ChareStateCollector.hpp"
42 : :
43 : : #include "NoWarning/walker.decl.h"
44 : :
45 : : #if defined(__clang__)
46 : : #pragma clang diagnostic push
47 : : #pragma clang diagnostic ignored "-Wmissing-variable-declarations"
48 : : #endif
49 : :
50 : : //! \brief Charm handle to the main proxy, facilitates call-back to finalize,
51 : : //! etc., must be in global scope, unique per executable
52 : : CProxy_Main mainProxy;
53 : :
54 : : //! Chare state collector Charm++ chare group proxy
55 : : tk::CProxy_ChareStateCollector stateProxy;
56 : :
57 : : //! If true, call and stack traces are to be output with exceptions
58 : : //! \note This is true by default so that the trace is always output between
59 : : //! program start and the Main ctor in which the user-input from command line
60 : : //! setting for this overrides this true setting.
61 : : bool g_trace = true;
62 : :
63 : : #if defined(__clang__)
64 : : #pragma clang diagnostic pop
65 : : #endif
66 : :
67 : : //! Walker declarations and definitions
68 : : namespace walker {
69 : :
70 : : //! Global-scope data. Initialized by the main chare and distibuted to all PEs
71 : : //! by the Charm++ runtime system. Though semantically not const, all these
72 : : //! global data should be considered read-only. See also
73 : : //! http://charm.cs.illinois.edu/manuals/html/charm++/manual.html. The data
74 : : //! below is global-scope because they must be available to all PEs which could
75 : : //! be on different machines.
76 : :
77 : : #if defined(__clang__)
78 : : #pragma clang diagnostic push
79 : : #pragma clang diagnostic ignored "-Wmissing-variable-declarations"
80 : : #endif
81 : :
82 : : //! Defaults of input deck, facilitates detection what is set by user
83 : : //! \details This object is in global scope, it contains the default of all
84 : : //! possible user input, and thus it is made available to all PEs for
85 : : //! convenience reasons. The runtime system distributes it to all PEs during
86 : : //! initialization. Once distributed, the object does not change.
87 : : ctr::InputDeck g_inputdeck_defaults;
88 : : //! Input deck filled by parser, containing all input data
89 : : //! \details This object is in global scope, it contains all of user input, and
90 : : //! thus it is made available to all PEs for convenience reasons. The runtime
91 : : //! system distributes it to all PEs during initialization. Once distributed,
92 : : //! the object does not change.
93 : : ctr::InputDeck g_inputdeck;
94 : : //! Random number generators selected by user
95 : : //! \details This map is in global scope, because it holds polymorphic
96 : : //! objects, and thus must be distributed to all PEs during initialization.
97 : : //! Once distributed by the runtime system, the objects do not change.
98 : : std::map< tk::ctr::RawRNGType, tk::RNG > g_rng;
99 : : //! Differential equations selected by user
100 : : //! \details This vector is in global scope, because it holds polymorphic
101 : : //! objects, and thus must be distributed to all PEs during initialization.
102 : : //! Once distributed by the runtime system, the objects do not change.
103 : : std::vector< DiffEq > g_diffeqs;
104 : :
105 : : //! Distributor Charm++ proxy facilitating call-back to Distributor by the
106 : : //! individual integrators
107 : : CProxy_Distributor g_DistributorProxy;
108 : :
109 : : #if defined(__clang__)
110 : : #pragma clang diagnostic pop
111 : : #endif
112 : :
113 : : //! Pack/Unpack selected RNGs. This Pack/Unpack method (re-)creates the full RNG
114 : : //! stack since it needs to (re-)bind function pointers on different processing
115 : : //! elements. Therefore we circumvent Charm's usual pack/unpack for this type,
116 : : //! and thus sizing does not make sense: sizing is a no-op. We could initialize
117 : : //! the stack in RNGTestDriver's constructor and let this function re-create the
118 : : //! stack only when unpacking, but that leads to repeating the same code twice:
119 : : //! once in RNGTestDriver's constructor, once here. Another option is to use
120 : : //! this pack/unpack routine to both initially create (when packing) and to
121 : : //! re-create (when unpacking) the stack, which eliminates the need for
122 : : //! pre-creating the object in RNGTestDriver's constructor and therefore
123 : : //! eliminates the repeated code. This explains the guard for sizing: the code
124 : : //! below is called for packing only (in serial) and packing and unpacking (in
125 : : //! parallel).
126 : : inline
127 : 374 : void operator|( PUP::er& p, std::map< tk::ctr::RawRNGType, tk::RNG >& rng ) {
128 : : try {
129 [ + + ]: 374 : if (!p.isSizing()) {
130 : : tk::RNGStack stack(
131 : : #ifdef HAS_MKL
132 : 281 : g_inputdeck.get< tag::param, tag::rngmkl >(),
133 : : #endif
134 : : #ifdef HAS_RNGSSE2
135 : 281 : g_inputdeck.get< tag::param, tag::rngsse >(),
136 : : #endif
137 [ + - ]: 562 : g_inputdeck.get< tag::param, tag::rng123 >() );
138 [ + - ]: 281 : rng = stack.selected( g_inputdeck.get< tag::selected, tag::rng >() );
139 : : }
140 [ - - ]: 0 : } catch (...) { tk::processExceptionCharm(); }
141 : 374 : }
142 : :
143 : : //! Pack/Unpack selected differential equations. This Pack/Unpack method
144 : : //! (re-)creates the DiffEq factory since it needs to (re-)bind function
145 : : //! pointers on different processing elements. Therefore we circumvent Charm's
146 : : //! usual pack/unpack for this type, and thus sizing does not make sense: sizing
147 : : //! is a no-op. We could initialize the factory in WalkerDriver's constructor
148 : : //! and let this function re-create the stack only when unpacking, but that
149 : : //! leads to repeating the same code twice: once in WalkerDriver's constructor,
150 : : //! once here. Another option is to use this pack/unpack routine to both
151 : : //! initially create (when packing) and to re-create (when unpacking) the
152 : : //! factory, which eliminates the need for pre-creating the object in
153 : : //! WalkerDriver's constructor and therefore eliminates the repeated code. This
154 : : //! explains the guard for sizing: the code below is called for packing only (in
155 : : //! serial) and packing and unpacking (in parallel).
156 : : inline
157 : 374 : void operator|( PUP::er& p, std::vector< DiffEq >& eqs ) {
158 : : try {
159 [ + + ][ + - ]: 374 : if (!p.isSizing()) eqs = DiffEqStack().selected();
[ + - ]
160 [ - - ]: 0 : } catch (...) { tk::processExceptionCharm(); }
161 : 374 : }
162 : :
163 : : } // walker::
164 : :
165 : : //! \brief Charm++ main chare for the random walker executable, walker.
166 : : //! \details Note that this object should not be in a namespace.
167 : : // cppcheck-suppress noConstructor
168 : : class Main : public CBase_Main {
169 : :
170 : : public:
171 : : //! \brief Constructor
172 : : //! \details Walker's main chare constructor is the entry point of the
173 : : //! program, called by the Charm++ runtime system. The constructor does
174 : : //! basic initialization steps, e.g., parser the command-line, prints out
175 : : //! some useful information to screen (in verbose mode), and instantiates
176 : : //! a driver. Since Charm++ is fully asynchronous, the constructor
177 : : //! usually spawns asynchronous objects and immediately exits. Thus in the
178 : : //! body of the main chare constructor we fire up an 'execute' chare,
179 : : //! which then calls back to Main::execute(). Finishing the main chare
180 : : //! constructor the Charm++ runtime system then starts the
181 : : //! network-migration of all global-scope data (if any). The execute chare
182 : : //! calling back to Main::execute() signals the end of the migration of
183 : : //! the global-scope data. Then we are ready to execute the driver. Since
184 : : //! the random walker is parallel and asynchronous, its driver fires up
185 : : //! additional Charm++ chare objects which then call back to
186 : : //! Main::finalize() at some point in the future when all work has been
187 : : //! finished. finalize() then exits by calling Charm++'s CkExit(),
188 : : //! shutting down the runtime system.
189 : : //! \see http://charm.cs.illinois.edu/manuals/html/charm++/manual.html
190 : 93 : Main( CkArgMsg* msg )
191 : 93 : try :
192 : 186 : m_signal( tk::setSignalHandlers() ),
193 : : m_cmdline(),
194 : : // Parse command line into m_cmdline using default simple pretty printer
195 [ + - ]: 186 : m_cmdParser( msg->argc, msg->argv, tk::Print(), m_cmdline ),
196 : : // Create Walker driver
197 : : m_driver( tk::Main< walker::WalkerDriver >
198 : : ( msg->argc, msg->argv,
199 : 93 : m_cmdline,
200 : : tk::HeaderType::WALKER,
201 [ + - ]: 186 : tk::walker_executable(),
202 : : walker::g_inputdeck_defaults.get< tag::cmd, tag::io,
203 : 93 : tag::screen >(),
204 : : walker::g_inputdeck_defaults.get< tag::cmd, tag::io,
205 [ + - ]: 186 : tag::nrestart >() ) ),
206 : : m_timer(1), // start new timer measuring the total runtime
207 [ + - ][ + - ]: 465 : m_timestamp()
[ + - ][ + - ]
[ + - ]
208 : : {
209 [ + - ][ + - ]: 93 : delete msg;
210 : 93 : g_trace = m_cmdline.get< tag::trace >();
211 [ + - ]: 93 : tk::MainCtor( mainProxy, thisProxy, m_timer, m_cmdline,
212 [ + - ][ + - ]: 186 : CkCallback( CkIndex_Main::quiescence(), thisProxy ) );
213 : : // If quiescence detection is on or user requested it, create chare state
214 : : // collector Charm++ chare group
215 [ + - ][ + - ]: 93 : if ( m_cmdline.get< tag::chare >() || m_cmdline.get< tag::quiescence >() )
[ + - ]
216 [ + - ][ + - ]: 93 : stateProxy = tk::CProxy_ChareStateCollector::ckNew();
217 : : // Fire up an asynchronous execute object, which when created at some
218 : : // future point in time will call back to this->execute(). This is
219 : : // necessary so that this->execute() can access already migrated
220 : : // global-scope data.
221 [ + - ]: 93 : CProxy_execute::ckNew();
222 [ - - ]: 93 : } catch (...) { tk::processExceptionCharm(); }
223 : :
224 : : //! Execute driver created and initialized by constructor
225 : 93 : void execute() {
226 : : try {
227 [ + - ][ + - ]: 93 : m_timestamp.emplace_back("Migrate global-scope data", m_timer[1].hms());
228 : 93 : m_driver.execute(); // fires up async chares
229 [ - - ]: 0 : } catch (...) { tk::processExceptionCharm(); }
230 : 93 : }
231 : :
232 : : //! Towards normal exit but collect chare state first (if any)
233 : 93 : void finalize() {
234 [ + - ]: 93 : tk::finalize( m_cmdline, m_timer, stateProxy, m_timestamp,
235 : 93 : walker::g_inputdeck_defaults.get< tag::cmd, tag::io, tag::screen >(),
236 : 93 : walker::g_inputdeck.get< tag::cmd, tag::io, tag::nrestart >(),
237 : 186 : CkCallback( CkIndex_Main::dumpstate(nullptr), thisProxy ) );
238 : 93 : }
239 : :
240 : : //! Entry method triggered when quiescence is detected
241 : 0 : void quiescence() {
242 : : try {
243 [ - - ]: 0 : stateProxy.collect( /* error= */ true,
244 [ - - ][ - - ]: 0 : CkCallback( CkIndex_Main::dumpstate(nullptr), thisProxy ) );
245 [ - - ]: 0 : } catch (...) { tk::processExceptionCharm(); }
246 : 0 : }
247 : :
248 : : //! Dump chare state
249 : 93 : void dumpstate( CkReductionMsg* msg ) {
250 : 93 : tk::dumpstate( m_cmdline,
251 : 93 : walker::g_inputdeck_defaults.get< tag::cmd, tag::io, tag::screen >(),
252 : 93 : walker::g_inputdeck.get< tag::cmd, tag::io, tag::nrestart >(),
253 : : msg );
254 : 0 : }
255 : :
256 : : //! Add time stamp contributing to final timers output
257 : 0 : void timestamp( std::string label, tk::real stamp ) {
258 : : try{
259 [ - - ][ - - ]: 0 : m_timestamp.emplace_back( label, tk::hms( stamp ) );
260 [ - - ]: 0 : } catch (...) { tk::processExceptionCharm(); }
261 : 0 : }
262 : :
263 : : private:
264 : : int m_signal; //!< Used to set signal handlers
265 : : walker::ctr::CmdLine m_cmdline; //!< Command line
266 : : walker::CmdLineParser m_cmdParser; //!< Command line parser
267 : : walker::WalkerDriver m_driver; //!< Driver
268 : : std::vector< tk::Timer > m_timer; //!< Timers
269 : :
270 : : //! Time stamps in h:m:s with labels
271 : : std::vector< std::pair< std::string, tk::Timer::Watch > > m_timestamp;
272 : : };
273 : :
274 : : //! \brief Charm++ chare execute
275 : : //! \details By the time this object is constructed, the Charm++ runtime system
276 : : //! has finished migrating all global-scoped read-only objects which happens
277 : : //! after the main chare constructor has finished.
278 : : class execute : public CBase_execute {
279 [ + - ]: 93 : public: execute() { mainProxy.execute(); }
280 : : };
281 : :
282 : : #include "NoWarning/walker.def.h"
|