|
|
|||
File indexing completed on 2026-11-13 10:31:07
0001 //------------------------------- -*- C++ -*- -------------------------------// 0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details 0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT) 0004 //---------------------------------------------------------------------------// 0005 //! \file celeritas/global/Stepper.hh 0006 //! \sa Stepper.test.cc 0007 //---------------------------------------------------------------------------// 0008 #pragma once 0009 0010 #include <array> 0011 #include <memory> 0012 #include <vector> 0013 0014 #include "corecel/Types.hh" 0015 #include "corecel/cont/Span.hh" 0016 #include "corecel/data/PinnedAllocator.hh" 0017 #include "corecel/data/StateDataStore.hh" 0018 #include "corecel/random/params/RngParamsFwd.hh" 0019 #include "corecel/sys/DeviceEvent.hh" 0020 #include "celeritas/Types.hh" 0021 #include "celeritas/geo/GeoFwd.hh" 0022 #include "celeritas/phys/Primary.hh" 0023 #include "celeritas/track/TrackInitData.hh" 0024 #include "celeritas/user/ActionTimes.hh" 0025 0026 #include "ActionSequence.hh" 0027 #include "CoreState.hh" 0028 #include "CoreTrackData.hh" 0029 0030 namespace celeritas 0031 { 0032 //---------------------------------------------------------------------------// 0033 class CoreParams; 0034 struct Primary; 0035 class ExtendFromPrimariesAction; 0036 0037 //---------------------------------------------------------------------------// 0038 /*! 0039 * State-specific options for the stepper. 0040 * 0041 * - \c params : Problem definition 0042 * - \c actions : Ordered sequence of transport actions 0043 * - \c stream_id : Unique thread or task ID for this state 0044 * - \c num_track_slots : Maximum number of tracks transported in parallel 0045 * (optional, defaults to the per-stream value from \c params) 0046 * - \c primary_capacity : Maximum primaries in either owned host buffer 0047 * (optional, defaults to the total primary capacity in \c params divided by 0048 * the number of streams) 0049 */ 0050 struct StepperInput 0051 { 0052 std::shared_ptr<CoreParams const> params; 0053 std::shared_ptr<ActionSequence> actions; 0054 StreamId stream_id{}; 0055 size_type num_track_slots{}; 0056 size_type primary_capacity{}; 0057 0058 //! True if defined 0059 explicit operator bool() const { return params && actions && stream_id; } 0060 }; 0061 0062 //---------------------------------------------------------------------------// 0063 /*! 0064 * Track counters for a step. 0065 */ 0066 struct StepperResult 0067 { 0068 size_type generated{}; //!< New primaries added 0069 size_type queued{}; //!< Pending track initializers at end of step 0070 size_type active{}; //!< Active tracks at start of step 0071 size_type alive{}; //!< Active and alive at end of step 0072 size_type cut{}; //!< Tracks killed by tracking cuts during step 0073 size_type errored{}; //!< Tracks killed due to errors during step 0074 0075 //! True if more steps need to be run 0076 explicit operator bool() const { return queued > 0 || alive > 0; } 0077 }; 0078 0079 //---------------------------------------------------------------------------// 0080 /*! 0081 * Interface class for stepper classes. 0082 * 0083 * This allows higher-level classes not to care whether the stepper operates on 0084 * host or device. 0085 * 0086 * A stepper initially has no asynchronous result, so \c has_outstanding_result 0087 * returns false. Calling \c async, with or without primaries, starts one step 0088 * and creates an outstanding result. No other step can be started until \c get 0089 * returns the result and restores the initial state. The \c ready function 0090 * queries completion without blocking, \c wait blocks until completion without 0091 * retrieving the result, and \c get waits if necessary before retrieving the 0092 * result and restoring the initial state. All three require an outstanding 0093 * result. An outstanding result does not imply that execution is still in 0094 * progress: the step may already be complete and waiting for its result to be 0095 * retrieved. 0096 * 0097 * Primary input is accumulated in a fixed-capacity producer buffer owned by 0098 * the stepper. Calling \c stage_primaries transfers that batch into the core 0099 * state and makes it available to the next call to \c async. The producer can 0100 * then accept another batch, including while a previous step has an 0101 * outstanding result. At most one unsubmitted batch can be staged. The span 0102 * overload copies its input into stepper-owned storage, so the caller's span 0103 * need not remain valid after the call returns. The span returned by \c 0104 * staged_primaries represents only the current unsubmitted batch and should 0105 * not be retained after a call that changes the staging state. 0106 * 0107 * Calling \c async submits staged primaries, if present, and otherwise 0108 * advances existing tracks without changing the producer buffer. This allows 0109 * prior transport to be drained while a later primary batch remains buffered. 0110 * Primaries can be pushed and staged while a previous result is valid, but the 0111 * next step cannot start until \c get consumes that result. Thus result 0112 * completion and primary production have independent lifecycles. 0113 * 0114 * Host steps execute synchronously and are immediately ready. The deprecated 0115 * call operators preserve synchronous behavior by calling \c async followed 0116 * by \c get. 0117 * 0118 * Before destroying a device Stepper, the caller must ensure that any 0119 * outstanding asynchronous operation has completed by calling \c wait or \c 0120 * get, and that any staged primary batch has been submitted with \c async. 0121 * Destruction does not add hidden synchronization. 0122 * 0123 * \note This interface and its implementations may be removed soon to 0124 * facilitate step gathering. 0125 */ 0126 class StepperInterface 0127 { 0128 public: 0129 //!@{ 0130 //! \name Type aliases 0131 using Input = StepperInput; 0132 using SpanConstPrimary = Span<Primary const>; 0133 using result_type = StepperResult; 0134 using SPState = std::shared_ptr<CoreStateInterface>; 0135 //!@} 0136 0137 public: 0138 // Default virtual destructor 0139 virtual ~StepperInterface(); 0140 0141 // Warm up before stepping 0142 virtual void warm_up() = 0; 0143 0144 // Start a step with existing states and any staged primary batch 0145 virtual void async() = 0; 0146 0147 // Copy new primaries into owned storage and start a step with them 0148 virtual void async(SpanConstPrimary primaries) = 0; 0149 0150 //! Whether an asynchronous step result can be retrieved 0151 virtual bool has_outstanding_result() const noexcept = 0; 0152 0153 // Whether the asynchronous step has completed 0154 virtual bool ready() const = 0; 0155 0156 // Wait for the asynchronous step to complete 0157 virtual void wait() const = 0; 0158 0159 // Wait for and return the asynchronous step result 0160 virtual StepperResult get() = 0; 0161 0162 //! Fixed capacity of each stepper-owned primary buffer 0163 virtual size_type primary_capacity() const noexcept = 0; 0164 0165 //! Fixed capacity of the track initializer queue 0166 virtual size_type initializer_capacity() const noexcept = 0; 0167 0168 //! Fixed capacity of the per-step secondary stack 0169 virtual size_type secondary_capacity() const noexcept = 0; 0170 0171 //! Number of primaries accumulated in the producer buffer 0172 virtual size_type num_buffered_primaries() const noexcept = 0; 0173 0174 // Add a primary to the producer buffer; a prior result may remain valid 0175 virtual void push_primary(Primary const& primary) = 0; 0176 0177 // Stage the nonempty producer buffer for the next step 0178 virtual void stage_primaries() = 0; 0179 0180 //! Access the unsubmitted primary batch staged for the next step 0181 virtual SpanConstPrimary staged_primaries() const noexcept = 0; 0182 0183 //! \deprecated Transport existing states 0184 virtual StepperResult operator()() = 0; 0185 0186 // Copy primaries into owned storage and stage them for the next step 0187 virtual void stage_primaries(SpanConstPrimary primaries) = 0; 0188 0189 // Transport existing states and these new primaries 0190 //! \deprecated Transport existing states and these new primaries 0191 virtual StepperResult operator()(SpanConstPrimary primaries) = 0; 0192 0193 // Kill all tracks in flight to debug "stuck" tracks 0194 virtual void kill_active() = 0; 0195 0196 // Reseed the RNGs at the start of an event for reproducibility 0197 virtual void reseed(UniqueEventId event_id) = 0; 0198 0199 //! Get action sequence for timing diagnostics 0200 virtual ActionSequence const& actions() const = 0; 0201 0202 //! Get the core state interface 0203 virtual CoreStateInterface const& state() const = 0; 0204 0205 //! Get a shared pointer to the state (TEMPORARY) 0206 virtual SPState sp_state() = 0; 0207 0208 protected: 0209 StepperInterface() = default; 0210 CELER_DEFAULT_COPY_MOVE(StepperInterface); 0211 }; 0212 0213 //---------------------------------------------------------------------------// 0214 /*! 0215 * Manage a state vector and execute a single step on all of them. 0216 * 0217 * \note This is likely to be removed and refactored since we're changing how 0218 * primaries are created and how multithread state ownership is managed. 0219 * 0220 * Device steps have separate start and result phases. Calling \c async 0221 * enqueues the action sequence, a counter snapshot, and a completion event on 0222 * the state stream. Diagnostic action or step timing can still synchronize the 0223 * stream. Other synchronization within the action sequence is being removed 0224 * separately. 0225 * 0226 * The two primary buffers are reserved to \c primary_capacity at construction, 0227 * so accumulating and staging primaries within capacity does not reallocate. 0228 * Device buffers use pinned host memory so inserting a staged batch can enqueue 0229 * its host-to-device copy. The submitted source storage is retained until that 0230 * copy completes; same-stream ordering ensures the step actions see the copied 0231 * input. Reusing a submitted source may wait for its copy event, but does not 0232 * wait for the full step to complete. 0233 * 0234 * The following sequence overlaps production of a later primary batch with an 0235 * outstanding result: 0236 * \code 0237 Stepper<MemSpace::device> step(input); 0238 0239 step.async(make_span(first_primaries)); 0240 for (Primary primary : second_primaries) 0241 { 0242 step.push_primary(std::move(primary)); 0243 } 0244 step.stage_primaries(); 0245 StepperResult result = step.get(); 0246 do 0247 { 0248 // Submit the staged batch while continuing any active tracks 0249 step.async(); 0250 result = step.get(); 0251 } while (result); 0252 \endcode 0253 * 0254 * \internal 0255 * 0256 * \par Asynchronous state 0257 * 0258 * The \c has_outstanding_result_ flag tracks whether an asynchronous step has 0259 * a result that has not yet been retrieved with \c get, whereas \c step_done_ 0260 * tracks completion of device work. Their states after successful calls are: 0261 * 0262 * | Lifecycle point | \c has_outstanding_result_ | CPU \c step_done_ | GPU \c 0263 step_done_ | 0264 * | --------------- | -------------------------- | ----------------- | 0265 ----------------- | 0266 * | Construction or after \c get | false | Null | Allocated and ready | 0267 * | After \c async | true | Null and ready | Recorded; pending or ready | 0268 * | After \c ready returns false | true | Not possible | Recorded and pending | 0269 * | After \c ready is true or \c wait | true | Null/ready | Recorded/ready | 0270 * 0271 * A host step executes synchronously, so its null event is always ready. A 0272 * device event is allocated once and re-recorded after each counter snapshot. 0273 * Calling \c get first waits for completion and then clears \c 0274 * has_outstanding_result_; it does not reset or replace the event. Primary 0275 * buffering has an independent state, tracked by \c primary_phase_ and \c 0276 * primary_copy_done_. The staged storage remains internal while a copy source 0277 * is submitted, but the public \c staged_primaries accessor returns only an 0278 * unsubmitted batch: 0279 * 0280 * | Primary phase | Producer | Staged storage | Accessor | Copy event | 0281 * | ------------- | -------- | -------------- | -------- | ---------- | 0282 * | \c empty | May fill | Empty | Empty | Null/ready or allocated/ready | 0283 * | \c staged | May fill | Next input | Next input | Recorded after H2D copy | 0284 * | \c submitted | May fill | Prior copy source | Empty | Pending or ready | 0285 * 0286 * Calling \c stage_primaries changes \c empty to \c staged. Calling \c async 0287 * changes \c staged to \c submitted after the actions have been enqueued. A 0288 * later \c stage_primaries call may reclaim a submitted source after waiting 0289 * only for its copy event, then stage the producer buffer. Calling \c get also 0290 * reclaims a submitted source, but leaves a next \c staged batch unchanged. 0291 * Host copies complete synchronously, so \c primary_copy_done_ is null and 0292 * reclaiming the source does not wait. 0293 * 0294 * The expected state transitions are 0295 * \code 0296 * no result + producer -- async() --> outstanding result + same producer 0297 * no result + no queued input -- async(primaries) --> outstanding result 0298 * outstanding result -- ready() or wait() --> outstanding result 0299 * outstanding result -- get() --> no result 0300 * 0301 * producer -- stage_primaries() --> staged 0302 * outstanding result + producer -- stage_primaries() --> outstanding result 0303 + staged 0304 * staged + no result -- async() --> submitted + outstanding result 0305 * submitted + producer -- stage_primaries() --> staged 0306 * \endcode 0307 * Calling \c ready or \c wait repeatedly with an outstanding result is 0308 * allowed. The next \c async call is allowed only after \c get consumes the 0309 * previous result. Primaries may be pushed and staged while a result is 0310 * outstanding, and the producer may begin filling again while that next batch 0311 * is staged. The staged batch cannot be submitted until the prior result is 0312 * consumed. Calls to \c warm_up, \c reset_state, and \c reseed are rejected 0313 * while an outstanding result or queued primary batch exists. Calling \c 0314 * kill_active permits buffered primaries but rejects an outstanding result or 0315 * staged batch. The synchronous call operators perform \c async followed 0316 * immediately by \c get. 0317 */ 0318 template<MemSpace M> 0319 class Stepper final : public StepperInterface 0320 { 0321 public: 0322 //!@{ 0323 //! \name Type aliases 0324 using StateRef = CoreStateData<Ownership::reference, M>; 0325 //!@} 0326 0327 public: 0328 // Construct with problem parameters and setup options 0329 explicit Stepper(Input input); 0330 0331 // Default destructor 0332 ~Stepper() final; 0333 0334 // Warm up before stepping 0335 void warm_up() final; 0336 0337 // Start a step with existing states and any staged primary batch 0338 void async() final; 0339 0340 // Copy new primaries into owned storage and start a step with them 0341 void async(SpanConstPrimary primaries) final; 0342 0343 //! Whether an asynchronous step result can be retrieved 0344 bool has_outstanding_result() const noexcept final 0345 { 0346 return has_outstanding_result_; 0347 } 0348 0349 // Whether the asynchronous step has completed 0350 bool ready() const final; 0351 0352 // Wait for the asynchronous step to complete 0353 void wait() const final; 0354 0355 // Wait for and return the asynchronous step result 0356 StepperResult get() final; 0357 0358 //! Fixed capacity of each stepper-owned primary buffer 0359 size_type primary_capacity() const noexcept final 0360 { 0361 return primary_capacity_; 0362 } 0363 0364 // Fixed capacity of the track initializer queue 0365 size_type initializer_capacity() const noexcept final; 0366 0367 //! Fixed capacity of the per-step secondary stack 0368 size_type secondary_capacity() const noexcept final 0369 { 0370 return state_->ref().physics.secondaries.capacity(); 0371 } 0372 0373 //! Number of primaries accumulated in the producer buffer 0374 size_type num_buffered_primaries() const noexcept final 0375 { 0376 return primary_buffer_.size(); 0377 } 0378 0379 // Add a primary to the producer buffer; a prior result may remain valid 0380 void push_primary(Primary const& primary) final; 0381 0382 // Stage the nonempty producer buffer for the next step 0383 void stage_primaries() final; 0384 0385 //! Access the unsubmitted primary batch staged for the next step 0386 SpanConstPrimary staged_primaries() const noexcept final; 0387 0388 // Transport existing states 0389 StepperResult operator()() final; 0390 0391 // Copy primaries into owned storage and stage them for the next step 0392 void stage_primaries(SpanConstPrimary primaries) final; 0393 0394 // Transport existing states and these new primaries 0395 StepperResult operator()(SpanConstPrimary primaries) final; 0396 0397 // Kill all tracks in flight to debug "stuck" tracks 0398 void kill_active() final; 0399 0400 // Reseed the RNGs at the start of an event for reproducibility 0401 void reseed(UniqueEventId event_id) final; 0402 0403 //! Get action sequence for timing diagnostics 0404 ActionSequence const& actions() const final { return *actions_; } 0405 0406 //! Access core data, primarily for debugging 0407 StateRef const& state_ref() const { return state_->ref(); } 0408 0409 //! Get the core state interface for diagnostic output 0410 CoreStateInterface const& state() const final { return *state_; } 0411 0412 // Reset the core state counters and data so it can be reused 0413 void reset_state(); 0414 0415 //! Get a shared pointer to the state (TEMPORARY, DO NOT USE) 0416 SPState sp_state() final { return state_; } 0417 0418 private: 0419 enum class PrimaryPhase 0420 { 0421 empty, 0422 staged, 0423 submitted 0424 }; 0425 0426 using VecPrimary = std::vector<Primary>; 0427 using PinnedVecPrimary = std::vector<Primary, PinnedAllocator<Primary>>; 0428 using PrimaryStorage = MemSpaceCond_t<M, VecPrimary, PinnedVecPrimary>; 0429 0430 // Params data 0431 std::shared_ptr<CoreParams const> params_; 0432 // Call sequence 0433 std::shared_ptr<ActionSequence> actions_; 0434 // Primary initialization 0435 std::shared_ptr<ExtendFromPrimariesAction const> primaries_action_; 0436 // State data 0437 std::shared_ptr<CoreState<M>> state_; 0438 // Maximum number of primaries in each host buffer 0439 size_type primary_capacity_{}; 0440 // Primaries being accumulated by the producer 0441 PrimaryStorage primary_buffer_; 0442 // Host source for a staged or submitted primary copy 0443 PrimaryStorage staged_primaries_; 0444 // Completion of the H2D copy that uses staged_primaries_ as its source 0445 DeviceEvent primary_copy_done_{nullptr}; 0446 // Logical state of staged_primaries_ 0447 PrimaryPhase primary_phase_{PrimaryPhase::empty}; 0448 // Completion of device work and the result-counter snapshot 0449 DeviceEvent step_done_{nullptr}; 0450 // Whether an asynchronous step result can be retrieved 0451 bool has_outstanding_result_{false}; 0452 0453 // Whether an operation would conflict with queued primaries 0454 bool has_queued_primaries() const noexcept; 0455 0456 // Release a submitted primary source after its copy completes 0457 void reclaim_submitted_primaries(); 0458 0459 // Calculate an upper bound on the number of initializers required 0460 size_type calc_max_initializers() const; 0461 }; 0462 0463 //---------------------------------------------------------------------------// 0464 // EXPLICIT INSTANTIATION 0465 //---------------------------------------------------------------------------// 0466 0467 extern template class Stepper<MemSpace::host>; 0468 extern template class Stepper<MemSpace::device>; 0469 0470 //---------------------------------------------------------------------------// 0471 } // namespace celeritas
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|