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