ATLAS Offline Software
Loading...
Searching...
No Matches
TracccTritonRunner.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
11
12#include "TracccTritonRunner.h"
13
15
16#include <chrono>
17#include <iostream>
18#include <mutex>
19#include <optional>
20#include <sstream>
21#include <stdexcept>
22#include <string>
23
24#include <cuda_runtime.h>
25
26// Gaudi / Athena.
31#include "GaudiKernel/EventContext.h"
32#include "GaudiKernel/ThreadLocalContext.h"
33#include "GaudiKernel/IAlgExecStateSvc.h"
34#include "GaudiKernel/IAlgManager.h"
35#include "GaudiKernel/IAlgorithm.h"
36#include "GaudiKernel/IHiveWhiteBoard.h"
37#include "GaudiKernel/IIncidentSvc.h"
38#include "GaudiKernel/Incident.h"
39#include "GaudiKernel/ISvcLocator.h"
40#include "GaudiKernel/ServiceHandle.h"
41#include "GaudiKernel/SmartIF.h"
43
44// traccc / vecmem EDM
47
48#include "vecmem/memory/cuda/device_memory_resource.hpp"
49#include "vecmem/memory/host_memory_resource.hpp"
50#include "vecmem/utils/copy.hpp"
51#include "vecmem/utils/cuda/async_copy.hpp"
52
53namespace triton { namespace backend { namespace traccc {
54
55namespace {
58 struct SharedRunState {
59 std::mutex mutex;
60 std::optional<EventIDBase::number_type> currentRun;
61 };
62 SharedRunState sharedRunState ATLAS_THREAD_SAFE;
63
65 template <typename DEVICE_BUFFER, typename HOST_CONTAINER>
66 void copyJaggedToHost(const vecmem::copy& deviceCopy,
67 const DEVICE_BUFFER& device, HOST_CONTAINER& host,
68 vecmem::memory_resource& host_mr) {
69 auto staged = deviceCopy.to(vecmem::get_data(device), host_mr, nullptr,
70 vecmem::copy::type::device_to_host);
71 vecmem::copy{}(vecmem::get_data(staged), host,
72 vecmem::copy::type::host_to_host)->wait();
73 }
74} // namespace
75
77
79
80 vecmem::host_memory_resource host_mr;
81 vecmem::cuda::device_memory_resource device_mr;
83 cudaStream_t stream = nullptr;
84 std::optional<vecmem::cuda::async_copy> copy;
85
87 SmartIF<IHiveWhiteBoard> whiteboard;
88
89 const std::size_t slot;
90 std::size_t eventCounter = 0;
91
93 SmartIF<IAlgorithm> chain;
94 SmartIF<IAlgorithm> condChain;
95 SmartIF<IAlgExecStateSvc> algExecStates;
96 SmartIF<IIncidentSvc> incidentSvc;
97 SmartIF<Athena::IConditionsCleanerSvc> conditionsCleaner;
98
99 Impl(TracccTritonInitializer& init, std::size_t theSlot)
100 : initializer(init)
101 , host_mr()
102 , device_mr(init.deviceId())
103 , eventStore("StoreGateSvc", "TracccTritonRunner")
104 , slot(theSlot) {}
105};
106
108 std::size_t slot)
109 : m_impl(std::make_unique<Impl>(initializer, slot)) {
110
111 if (!initializer.isReady()) {
112 throw std::runtime_error(
113 "TracccTritonRunner: initializer is not ready");
114 }
115
116 if (cudaSetDevice(initializer.deviceId()) != cudaSuccess) {
117 throw std::runtime_error(
118 "TracccTritonRunner: cudaSetDevice(" +
119 std::to_string(initializer.deviceId()) + ") failed");
120 }
121
122 if (m_impl->eventStore.retrieve().isFailure() || !m_impl->eventStore) {
123 throw std::runtime_error(
124 "TracccTritonRunner: could not retrieve StoreGateSvc");
125 }
126
127 SmartIF<ISvcLocator> svcLocator(&initializer.serviceLocator());
128
129 // plumbing to allow multi-model-inst. per GPU
130 m_impl->whiteboard =
131 svcLocator->service<IHiveWhiteBoard>("EventDataSvc", /*createIf*/ false);
132 if (!m_impl->whiteboard) {
133 throw std::runtime_error(
134 "TracccTritonRunner: EventDataSvc is not an IHiveWhiteBoard");
135 }
136 if (slot >= m_impl->whiteboard->getNumberOfStores()) {
137 throw std::runtime_error(
138 "TracccTritonRunner: slot " + std::to_string(slot) +
139 " is out of range, the whiteboard has only " +
140 std::to_string(m_impl->whiteboard->getNumberOfStores()) +
141 " store(s)");
142 }
143
144 // The device chain takes its stream from this service, one per slot
145 SmartIF<AthCUDA::IStreamSvc> streamSvc =
146 svcLocator->service<AthCUDA::IStreamSvc>(
147 initializer.config().streamSvcName, /*createIf*/ false);
148 if (!streamSvc) {
149 throw std::runtime_error(
150 "TracccTritonRunner: could not retrieve the CUDA stream service '" +
151 initializer.config().streamSvcName +
152 "'; TracccTritonBootstrap.py has to set "
153 "flags.CUDA.Stream = CUDAStream.PerEvent");
154 }
155 m_impl->stream = static_cast<cudaStream_t>(
156 streamSvc->stream(EventContext(0, slot)));
157 m_impl->copy.emplace(m_impl->stream);
158
159 IAlgManager* algMgr = svcLocator.as<IAlgManager>();
160 if (!algMgr) {
161 throw std::runtime_error(
162 "TracccTritonRunner: no IAlgManager in the embedded application");
163 }
164 m_impl->chain = algMgr->algorithm(initializer.config().sequenceName,
165 /*createIf*/ false);
166 if (!m_impl->chain) {
167 throw std::runtime_error(
168 "TracccTritonRunner: could not retrieve the device chain '" +
169 initializer.config().sequenceName +
170 "' from the embedded Athena application");
171 }
172 m_impl->condChain = algMgr->algorithm(initializer.config().condSequenceName,
173 /*createIf*/ false);
174
175 m_impl->incidentSvc =
176 svcLocator->service<IIncidentSvc>("IncidentSvc", /*createIf*/ false);
177 if (!m_impl->incidentSvc) {
178 throw std::runtime_error(
179 "TracccTritonRunner: could not retrieve IncidentSvc");
180 }
181
182 // Drops conditions objects no longer needed by any event, as the event
183 // loop manager does at the start of each event; without it a long-running server
184 // keeps every IOV it has ever seen
185 if (m_impl->condChain) {
186 m_impl->conditionsCleaner =
187 svcLocator->service<Athena::IConditionsCleanerSvc>(
188 "Athena::ConditionsCleanerSvc", /*createIf*/ true);
189 if (!m_impl->conditionsCleaner) {
190 throw std::runtime_error(
191 "TracccTritonRunner: could not retrieve "
192 "Athena::ConditionsCleanerSvc");
193 }
194 }
195
196 // AthSequencer::execute() skips itself if its AlgExecState for this
197 // context is already Done, so the states have to be reset once per
198 // request the way AthenaEventLoopMgr does between events
199 m_impl->algExecStates = svcLocator->service<IAlgExecStateSvc>(
200 "AlgExecStateSvc", /*createIf*/ false);
201 if (!m_impl->algExecStates) {
202 throw std::runtime_error(
203 "TracccTritonRunner: could not retrieve AlgExecStateSvc");
204 }
205}
206
207TracccTritonRunner::~TracccTritonRunner() = default;
208
209void TracccTritonRunner::run(const uint8_t* buffer, std::size_t byteSize,
210 const EventIDBase& eventID,
211 EventIDBase::number_type conditionsRun,
212 bool printStats, Output& output) {
213 const auto& keys = m_impl->initializer.config();
214
215 auto t0 = std::chrono::high_resolution_clock::now();
216
217 // Deserialize the CELLS tensor into a host cell collection
218 ::traccc::edm::silicon_cell_collection::host cells(m_impl->host_mr);
219 if (buffer == nullptr) {
220 throw std::runtime_error("TracccTritonRunner: no CELLS buffer");
221 }
222 TracccTriton::deserialize(buffer, byteSize, cells);
223
224 auto t1 = std::chrono::high_resolution_clock::now();
225
226 if (m_impl->whiteboard->selectStore(m_impl->slot).isFailure()) {
227 throw std::runtime_error(
228 "TracccTritonRunner: could not select store for slot " +
229 std::to_string(m_impl->slot));
230 }
231
232 // Copy cells to the device and record them in StoreGate
233 if (m_impl->eventStore->clearStore().isFailure()) {
234 throw std::runtime_error("TracccTritonRunner: clearStore() failed");
235 }
236
237 // Conditions handles are resolved against the event's IOV, so the
238 // context carries the client event's id and conditions run, as the
239 // event loop manager would set them up
240 const std::size_t eventNumber = m_impl->eventCounter++;
241 EventContext ctx(eventNumber, m_impl->slot);
242 EventIDBase ctxEventID(eventID);
243 ctxEventID.set_event_number(eventNumber);
244 ctx.setEventID(ctxEventID);
245 ctx.setExtension(Atlas::ExtendedEventContext(
246 m_impl->eventStore->hiveProxyDict(), conditionsRun));
247 Gaudi::Hive::setCurrentContext(ctx);
248 {
249 auto deviceCells =
250 std::make_unique<::traccc::edm::silicon_cell_collection::buffer>(
251 static_cast<unsigned int>(cells.size()), m_impl->device_mr);
252 m_impl->copy->setup(*deviceCells)->ignore();
253 (*m_impl->copy)(vecmem::get_data(cells), *deviceCells)->wait();
254
255 if (m_impl->eventStore
256 ->record(std::move(deviceCells), keys.cellsKey)
257 .isFailure()) {
258 throw std::runtime_error(
259 "TracccTritonRunner: failed to record cells under '" +
260 keys.cellsKey + "'");
261 }
262 }
263
264 auto t2 = std::chrono::high_resolution_clock::now();
265
266 // Run the device chain as the python configuration declared it.
267 // Clear the previous request's execution states first, otherwise the
268 // sequencer sees itself as Done and silently runs nothing.
269 m_impl->algExecStates->reset(ctx);
270
271 if (m_impl->condChain) {
272 std::lock_guard<std::mutex> lock(sharedRunState.mutex);
273
274 // IOVDbSvc resolves conditions folders at the time it is given on
275 // BeginRun (there is no BeginEvent/EventInfo here), so fire it
276 // whenever the run changes, as the event loop manager does
277 const EventIDBase::number_type run = ctx.eventID().run_number();
278 if (sharedRunState.currentRun != run) {
279 sharedRunState.currentRun = run;
280 m_impl->incidentSvc->fireIncident(
281 Incident("TracccTritonRunner", IncidentType::BeginRun, ctx));
282 }
283
284 // Drop the conditions objects no event in flight needs any more,
285 // as the event loop manager does at the start of each event.
286 // Synchronously, since there is no scheduler to run it in a task.
287 if (m_impl->conditionsCleaner->event(ctx, /*allowAsync*/ false)
288 .isFailure()) {
289 throw std::runtime_error(
290 "TracccTritonRunner: conditions cleaning failed");
291 }
292
293 // Make the conditions for this IOV available; the conditions algs
294 // skip themselves once their object is valid for the IOV, so this is
295 // cheap for every request after the first one of an IOV
296 if (m_impl->condChain->sysExecute(ctx).isFailure()) {
297 std::ostringstream states;
298 m_impl->algExecStates->dump(states, ctx);
299 throw std::runtime_error(
300 "TracccTritonRunner: conditions sequence '" +
301 m_impl->condChain->name() +
302 "' failed during execute(). Algorithm states:\n" + states.str());
303 }
304 }
305
306 if (m_impl->chain->sysExecute(ctx).isFailure()) {
307 std::ostringstream states;
308 m_impl->algExecStates->dump(states, ctx);
309 throw std::runtime_error(
310 "TracccTritonRunner: device chain '" + m_impl->chain->name() +
311 "' failed during execute(). Algorithm states:\n" + states.str());
312 }
313 // The algorithms return with work still queued on the stream
314 if (cudaStreamSynchronize(m_impl->stream) != cudaSuccess) {
315 throw std::runtime_error(
316 "TracccTritonRunner: cudaStreamSynchronize failed after the "
317 "device chain");
318 }
319
320 auto t3 = std::chrono::high_resolution_clock::now();
321
322 // Copy the fitted tracks and the measurements their states point at back to the host
323 const traccc_track_container::buffer* tracksDevice = nullptr;
324 if (m_impl->eventStore->retrieve(tracksDevice, keys.tracksKey).isFailure() ||
325 tracksDevice == nullptr) {
326 throw std::runtime_error(
327 "TracccTritonRunner: could not retrieve tracks under '" +
328 keys.tracksKey + "'");
329 }
330
331 const ::traccc::edm::measurement_collection::buffer* measurementsDevice =
332 nullptr;
333 if (m_impl->eventStore
334 ->retrieve(measurementsDevice, keys.measurementsKey)
335 .isFailure() ||
336 measurementsDevice == nullptr) {
337 throw std::runtime_error(
338 "TracccTritonRunner: could not retrieve measurements under '" +
339 keys.measurementsKey + "'");
340 }
341
342 const ::traccc::edm::silicon_cluster_collection::buffer* clustersDevice =
343 nullptr;
344 if (m_impl->eventStore->retrieve(clustersDevice, keys.clustersKey)
345 .isFailure() ||
346 clustersDevice == nullptr) {
347 throw std::runtime_error(
348 "TracccTritonRunner: could not retrieve clusters under '" +
349 keys.clustersKey + "'");
350 }
351
352 const vecmem::copy& copy = *m_impl->copy;
353 copyJaggedToHost(copy, tracksDevice->tracks, output.tracks.tracks,
354 m_impl->host_mr);
355 copy(tracksDevice->states, output.tracks.states,
356 vecmem::copy::type::device_to_host)->wait();
357 copy(*measurementsDevice, output.measurements,
358 vecmem::copy::type::device_to_host)->wait();
359 copyJaggedToHost(copy, *clustersDevice, output.clusters, m_impl->host_mr);
360
361 auto t4 = std::chrono::high_resolution_clock::now();
362
363 if (printStats) {
364 auto ms = [](auto a, auto b) {
365 return std::chrono::duration_cast<std::chrono::milliseconds>(b - a)
366 .count();
367 };
368
369 const std::string tag =
370 "[TIMING][slot " + std::to_string(m_impl->slot) + "] ";
371 std::cout << tag << "Cell deserialization : " << ms(t0, t1) << " ms\n"
372 << tag << "Cell H2D + record : " << ms(t1, t2) << " ms\n"
373 << tag << "Device chain : " << ms(t2, t3) << " ms\n"
374 << tag << "Output D2H : " << ms(t3, t4) << " ms\n"
375 << tag << "Tracks: " << output.tracks.tracks.size()
376 << std::endl;
377 }
378}
379
380}}} // namespace triton::backend::traccc
Interface for doing garbage collection of conditions objects.
virtual void lock()=0
Interface to allow an object to lock itself when made const in SG.
static Double_t a
static Double_t t0
if(pathvar)
std::unique_ptr< StringPoolImpl > m_impl
Definition StringPool.h:137
Byte (de)serialization of vecmem SoA host containers, used to ship traccc EDM collections between the...
Initialization of GPU tracking algs. for use in Triton as-a-Service implementation.
Call the device reconstruction algs. once per-event in the Triton backend.
Define macros for attributes used to control the static checker.
#define ATLAS_THREAD_SAFE
Interface for services providing CUDA streams to (reentrant) algorithms.
Owns the embedded Athena application that backs the Traccc Triton backend, plus the process-wide devi...
bool isReady() const
Whether initialize has completed successfully.
ISvcLocator & serviceLocator() const
The service locator of the embedded Athena application (never null once ready).
const Config & config() const
The construction parameters the application was configured with.
int deviceId() const
The CUDA device id this instance is pinned to.
TracccTritonRunner(TracccTritonInitializer &initializer, std::size_t slot)
TracccTritonInitializer::acquireSlot().
void run(const uint8_t *buffer, std::size_t byteSize, const EventIDBase &eventID, EventIDBase::number_type conditionsRun, bool printStats, Output &output)
Run the full chain for one request.
void deserialize(const std::uint8_t *data, std::size_t size, vecmem::edm::host< vecmem::edm::schema< VARTYPES... >, INTERFACE > &container)
Fill container from size bytes at data.
STL namespace.
std::string sequenceName
Name of the AthSequencer holding the per-request device chain.
std::string condSequenceName
Conditions sequence run before the chain on every request.
Impl(TracccTritonInitializer &init, std::size_t theSlot)
vecmem::cuda::device_memory_resource device_mr
SmartIF< Athena::IConditionsCleanerSvc > conditionsCleaner
std::optional< vecmem::cuda::async_copy > copy
SmartIF< IAlgorithm > chain
The AthSequencer configured by TracccTritonDeviceRecoCfg.
cudaStream_t stream
The stream the device chain runs on for this instance owned by the stream service.
The host-resident reconstruction result of one request: the track container, the measurements its sta...