34#include <boost/core/demangle.hpp>
36#include <gtest/gtest.h>
48 struct IColumnReaderXA;
53 std::shared_ptr<TestUtils::IColumnReaderXA> reader;
56 struct IColumnReaderXA
58 virtual ~IColumnReaderXA () =
default;
60 virtual void connect (ColumnDataXA& , std::unordered_map<std::string,ColumnDataXA>& ) {}
62 virtual void requestShallowCopy () {
63 throw std::runtime_error (
"shallow copy not supported for column reader");
66 virtual StatusCode retrieveContainer (xAOD::TEvent& , xAOD::TStore& , ColumnVectorData& ) {
return StatusCode::SUCCESS; };
68 virtual std::pair<const SG::AuxElement*,std::size_t> getObject ()
const {
69 throw std::runtime_error (
"getObject not implemented for this XAOD column reader"); }
71 virtual void retrieveAuxData (ColumnVectorData& ) {}
73 [[nodiscard]]
virtual BranchPerfData getPerfData (
float ) = 0;
76 struct ColumnDataXAEventInfo
final :
public IColumnReaderXA, asg::AsgMessaging
80 std::array<ColumnarOffsetType, 2> data = {0, 1};
82 Benchmark benchmarkFirstRetrieve;
83 Benchmark benchmarkSecondRetrieve;
85 ColumnDataXAEventInfo (
const ColumnInfo& info)
86 : AsgMessaging (
"ColumnDataXAEventInfo"), index (
info.index)
89 virtual StatusCode retrieveContainer (xAOD::TEvent& event, xAOD::TStore& , ColumnVectorData& columnData)
override
91 benchmarkFirstRetrieve.startTimer ();
93 benchmarkFirstRetrieve.stopTimer ();
94 benchmarkSecondRetrieve.startTimer ();
96 benchmarkSecondRetrieve.stopTimer ();
97 columnData.setColumn (index, data.size(), data.data());
98 return StatusCode::SUCCESS;
101 virtual std::pair<const SG::AuxElement*,std::size_t> getObject ()
const override
103 return {eventInfo, 1};
106 [[nodiscard]]
virtual BranchPerfData getPerfData (
float emptyTime)
override
109 result.name =
"EventInfo";
110 result.timeRead = benchmarkFirstRetrieve.getEntryTime(emptyTime);
111 result.timeReadAgain = benchmarkSecondRetrieve.getEntryTime(emptyTime);
112 benchmarkFirstRetrieve.setSilence();
113 benchmarkSecondRetrieve.setSilence();
118 template<
typename XAODObjectType>
119 struct ColumnDataXARetrieve
final :
public IColumnReaderXA, asg::AsgMessaging
121 std::string containerName;
123 bool shallowCopy =
false;
124 bool skipShallowCopies =
false;
125 std::array<ColumnarOffsetType, 2> data = {0, 1};
126 const XAODObjectType*
object =
nullptr;
127 Benchmark benchmarkFirstRetrieve;
128 Benchmark benchmarkSecondRetrieve;
129 Benchmark benchmarkShallowCopy;
130 Benchmark benchmarkShallowRegister;
132 ColumnDataXARetrieve (
const ColumnInfo& info,
const UserConfiguration& userConfiguration)
133 : AsgMessaging (
"ColumnDataXARetrieve_" +
info.
name), containerName (
info.
name), index (
info.index), skipShallowCopies (userConfiguration.skipShallowCopies)
136 virtual void requestShallowCopy()
override {
137 if (!skipShallowCopies)
141 virtual StatusCode retrieveContainer (xAOD::TEvent& event, xAOD::TStore& store, ColumnVectorData& columnData)
override
143 benchmarkFirstRetrieve.startTimer ();
145 benchmarkFirstRetrieve.stopTimer ();
146 benchmarkSecondRetrieve.startTimer ();
148 benchmarkSecondRetrieve.stopTimer ();
150 throw std::logic_error (
"no object retrieved for XAOD container (in retrieveContainer): " + containerName);
151 data[1] =
object->size();
152 columnData.setColumn (index, data.size(), data.data());
155 std::string shallowName = containerName +
"_shallowCopy";
156 std::string shallowAuxName = shallowName +
"Aux.";
157 benchmarkShallowCopy.startTimer ();
159 benchmarkShallowCopy.stopTimer ();
160 benchmarkShallowRegister.startTimer ();
161 object = shallowCopy.first.get();
164 benchmarkShallowRegister.stopTimer ();
166 return StatusCode::SUCCESS;
169 virtual std::pair<const SG::AuxElement*,std::size_t> getObject ()
const override
172 throw std::logic_error (
"no object retrieved for XAOD container: " + containerName);
173 if (object->size() == 0)
175 return {(*object)[0],
object->size()};
178 [[nodiscard]]
virtual BranchPerfData getPerfData (
float emptyTime)
override
181 result.name = containerName;
182 result.timeRead = benchmarkFirstRetrieve.getEntryTime(emptyTime);
183 result.timeReadAgain = benchmarkSecondRetrieve.getEntryTime(emptyTime);
186 result.timeShallowCopy = benchmarkShallowCopy.getEntryTime(emptyTime);
187 result.timeShallowRegister = benchmarkShallowRegister.getEntryTime(emptyTime);
189 benchmarkFirstRetrieve.setSilence();
190 benchmarkSecondRetrieve.setSilence();
191 benchmarkShallowCopy.setSilence();
192 benchmarkShallowRegister.setSilence();
198 struct ColumnDataXAAccessor
final :
public IColumnReaderXA
202 std::optional<SG::AuxElement::Accessor<T>> accessor;
203 std::optional<SG::AuxElement::Decorator<T>> decorator;
204 bool isOptional =
false;
205 bool measureNonAccessForEmpty =
false;
206 const IColumnReaderXA *objectReader =
nullptr;
207 Benchmark benchmarkFirstRetrieve;
208 Benchmark benchmarkSecondRetrieve;
210 ColumnDataXAAccessor (
const ColumnInfo& info,
const UserConfiguration& userConfiguration)
211 : m_name (
info.
name), index (
info.index), measureNonAccessForEmpty (userConfiguration.measureNonAccessForEmpty)
214 if (!
info.replacesColumn.empty())
215 name =
info.replacesColumn;
216 name =
name.substr (
name.find_last_of(
'.') + 1);
219 accessor.emplace (name);
220 isOptional = info.isOptional;
223 decorator.emplace (name);
226 throw std::runtime_error (
"unsupported access mode for XAOD accessor: " + info.name);
230 virtual void connect (ColumnDataXA& columnData, std::unordered_map<std::string,ColumnDataXA>& requestedColumns)
override
232 auto iter = requestedColumns.find (columnData.info.offsetName);
233 if (iter == requestedColumns.end())
234 throw std::runtime_error (
"missing offset column for XAOD accessor: " + columnData.info.offsetName);
235 objectReader =
iter->second.reader.get();
236 if (decorator.has_value())
237 iter->second.reader->requestShallowCopy();
240 virtual void retrieveAuxData (ColumnVectorData& columnData)
override
243 throw std::logic_error (
"no object reader for XAOD accessor");
244 auto [
object,
size] = objectReader->getObject();
245 if (
object ==
nullptr)
247 if (measureNonAccessForEmpty)
249 benchmarkFirstRetrieve.startTimer ();
250 benchmarkFirstRetrieve.stopTimer ();
251 benchmarkSecondRetrieve.startTimer ();
252 benchmarkSecondRetrieve.stopTimer ();
254 if (accessor.has_value())
255 columnData.setColumn (index, 0,
static_cast<const T*
>(
nullptr));
256 else if (decorator.has_value())
257 columnData.setColumn (index, 0,
static_cast<T*
>(
nullptr));
259 throw std::logic_error (
"no accessor or decorator for XAOD accessor");
260 }
else if (accessor.has_value())
265 benchmarkFirstRetrieve.startTimer ();
266 (*accessor) (*object);
267 benchmarkFirstRetrieve.stopTimer ();
268 benchmarkSecondRetrieve.startTimer ();
269 value = &(*accessor) (*object);
270 benchmarkSecondRetrieve.stopTimer ();
271 columnData.setColumn (index,
size, value);
275 benchmarkFirstRetrieve.startTimer ();
276 if (accessor->isAvailable (*
object))
277 (*accessor) (*object);
278 benchmarkFirstRetrieve.stopTimer ();
279 benchmarkSecondRetrieve.startTimer ();
280 if (accessor->isAvailable (*
object))
281 value = &(*accessor) (*object);
282 benchmarkSecondRetrieve.stopTimer ();
283 if (accessor->isAvailable (*
object))
284 columnData.setColumn (index,
size, value);
286 }
else if (decorator.has_value())
289 benchmarkFirstRetrieve.startTimer ();
290 (*decorator) (*object);
291 benchmarkFirstRetrieve.stopTimer ();
292 benchmarkSecondRetrieve.startTimer ();
293 value = &(*decorator) (*object);
294 benchmarkSecondRetrieve.stopTimer ();
295 columnData.setColumn (index,
size, value);
298 throw std::logic_error (
"no accessor or decorator for XAOD accessor");
302 [[nodiscard]]
virtual BranchPerfData getPerfData (
float emptyTime)
override
306 result.timeRead = benchmarkFirstRetrieve.getEntryTime(emptyTime);
307 result.timeReadAgain = benchmarkSecondRetrieve.getEntryTime(emptyTime);
308 benchmarkFirstRetrieve.setSilence();
309 benchmarkSecondRetrieve.setSilence();
319 using namespace asg::msgUserCode;
327 throw std::runtime_error (
"tool is not a ColumnarTool<ColumnarModeXAODArray>");
333 std::unordered_map<std::string,TestUtils::ColumnDataXA> requestedColumns;
334 for (
auto& column : myTool->getColumnInfo())
335 requestedColumns[column.name].info = std::move (column);
336 for (
auto& [name,
data] : requestedColumns)
338 if (
data.info.isOffset)
341 throw std::runtime_error (
"unexpected type for offset column: " + name +
" " +
data.info.type->name());
344 data.reader = std::make_shared<TestUtils::ColumnDataXAEventInfo> (
data.info);
347 if (name ==
"AnalysisMuons")
348 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::MuonContainer>> (
data.info, userConfiguration);
349 else if (name ==
"AnalysisElectrons")
350 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::ElectronContainer>> (
data.info, userConfiguration);
351 else if (name ==
"AnalysisPhotons")
352 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::PhotonContainer>> (
data.info, userConfiguration);
353 else if (name ==
"egammaClusters")
354 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::CaloClusterContainer>> (
data.info, userConfiguration);
355 else if (name ==
"GSFTrackParticles")
356 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::TrackParticleContainer>> (
data.info, userConfiguration);
357 else if (name ==
"GSFConversionVertices")
358 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::VertexContainer>> (
data.info, userConfiguration);
362 if (*
data.info.type ==
typeid(
float))
363 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<float>> (
data.info, userConfiguration);
364 else if (*
data.info.type ==
typeid(
double))
365 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<double>> (
data.info, userConfiguration);
366 else if (*
data.info.type ==
typeid(
char))
367 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<char>> (
data.info, userConfiguration);
368 else if (*
data.info.type ==
typeid(std::uint8_t))
369 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::uint8_t>> (
data.info, userConfiguration);
370 else if (*
data.info.type ==
typeid(std::uint16_t))
371 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::uint16_t>> (
data.info, userConfiguration);
372 else if (*
data.info.type ==
typeid(std::uint32_t))
373 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::uint32_t>> (
data.info, userConfiguration);
374 else if (*
data.info.type ==
typeid(std::uint64_t))
375 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::uint64_t>> (
data.info, userConfiguration);
376 else if (*
data.info.type ==
typeid(std::vector<float>))
377 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::vector<float>>> (
data.info, userConfiguration);
379 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::vector<ElementLink<xAOD::CaloClusterContainer>>>> (
data.info, userConfiguration);
381 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::vector<ElementLink<xAOD::TrackParticleContainer>>>> (
data.info, userConfiguration);
383 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::vector<ElementLink<xAOD::VertexContainer>>>> (
data.info, userConfiguration);
387 bool allColumnsHandled =
true;
388 for (
auto& [name,
data] : requestedColumns)
392 allColumnsHandled =
false;
393 std::cout <<
"WARNING: no handling for requested column: name=" << name <<
" offset=" <<
data.info.offsetName <<
" type=" << boost::core::demangle(
data.info.type->name()) << std::endl;
396 if (!allColumnsHandled)
398 ADD_FAILURE() <<
"not all requested columns could be handled";
402 for (
auto& [name,
data]: requestedColumns)
404 data.reader->connect (
data, requestedColumns);
407 Benchmark benchmarkEmptyClear (testDefinition.
name +
" empty clear");
408 Benchmark benchmarkCallClear (testDefinition.
name +
" call clear");
409 Benchmark benchmarkGetEntry (testDefinition.
name +
" getEntry");
417 throw std::runtime_error (
"ColumnarPhysLiteTest: numberOfEvents == 0");
426 const auto startTime = std::chrono::high_resolution_clock::now();
427 for (; (std::chrono::high_resolution_clock::now() - startTime) < userConfiguration.
targetTime; ++entry)
437 for (
auto& [name,
data] : requestedColumns)
439 for (
auto& [name,
data] : requestedColumns)
440 data.reader->retrieveAuxData (columnData);
462 std::cout <<
"Total entries read: " << entry << std::endl;
463 const float emptyTime = benchmarkEmpty.
getEntryTime(0).value();
466 std::cout <<
"Average getEntry time: " << benchmarkGetEntry.
getEntryTime(emptyTime).value() <<
"ns" << std::endl;
468 std::cout <<
"Average clear time: " << benchmarkCallClear.
getEntryTime(emptyTime).value() <<
"ns" <<
" (empty=" << benchmarkEmptyClear.
getEntryTime(emptyTime).value() <<
"ns)" << std::endl;
474 std::vector<BranchPerfData> columnPerfData;
476 summary.name =
"total";
477 summary.timeRead = 0;
478 summary.timeReadAgain = 0;
479 summary.timeShallowCopy = 0;
480 summary.timeShallowRegister = 0;
481 for (
auto& [name,
data] : requestedColumns)
483 auto perfData =
data.reader->getPerfData(emptyTime);
484 if (perfData.timeRead.has_value() || perfData.timeReadAgain.has_value())
486 columnPerfData.push_back(perfData);
487 summary.timeRead.value() += perfData.timeRead.value_or(0);
488 summary.timeReadAgain.value() += perfData.timeReadAgain.value_or(0);
489 summary.timeShallowCopy.value() += perfData.timeShallowCopy.value_or(0);
490 summary.timeShallowRegister.value() += perfData.timeShallowRegister.value_or(0);
493 std::sort(columnPerfData.begin(), columnPerfData.end(), [](
const auto&
a,
const auto& b) { return a.name < b.name; });
494 columnPerfData.push_back(summary);
496 const std::size_t nameWidth = std::max_element(columnPerfData.begin(), columnPerfData.end(), [](
const auto&
a,
const auto& b) { return a.name.size() < b.name.size(); })->name.size();
497 std::string
header = std::format(
"{:{}} | 1st(ns) | 2nd(ns) | shallow copy(ns)",
"column name", nameWidth);
498 std::cout <<
"\n" <<
header << std::endl;
499 std::cout << std::string(
header.size(),
'-') << std::endl;
500 for (
auto&
data : columnPerfData)
502 if (
data.name ==
"total")
503 std::cout << std::string(
header.size(),
'-') << std::endl;
504 std::cout << std::format(
"{:{}} |",
data.name, nameWidth);
506 std::cout << std::format(
"{:>8.0f} |",
data.timeRead.value());
509 if (
data.timeReadAgain)
510 std::cout << std::format(
"{:>8.1f} |",
data.timeReadAgain.value());
513 if (
data.timeShallowCopy ||
data.timeShallowRegister)
514 std::cout << std::format(
"{:>10.0f} +{:>5.0f}",
data.timeShallowCopy.value_or(-1),
data.timeShallowRegister .value_or(-1));
515 std::cout << std::endl;
521 std::vector<ToolPerfData> toolPerfData;
522 toolPerfData.emplace_back();
523 toolPerfData.back().name = testDefinition.
name;
524 toolPerfData.back().timeCall = benchmarkCall.
getEntryTime(emptyTime);
526 toolPerfData.back().timeCall2 = benchmarkCall2.
getEntryTime(emptyTime);
527 toolPerfData.back().timeCheck = benchmarkCheck.
getEntryTime(emptyTime);
532 const std::size_t nameWidth = std::max_element(toolPerfData.begin(), toolPerfData.end(), [](
const auto&
a,
const auto& b) { return a.name.size() < b.name.size(); })->name.size();
533 std::string
header = std::format(
"{:{}} | call(ns) | call2(ns) | check(ns)",
"tool name", nameWidth);
534 std::cout <<
"\n" <<
header << std::endl;
535 std::cout << std::string(
header.size(),
'-') << std::endl;
536 for (
auto&
data : toolPerfData)
538 std::cout << std::format(
"{:{}} |",
data.name, nameWidth);
540 std::cout << std::format(
"{:>9.0f} |",
data.timeCall.value());
544 std::cout << std::format(
"{:>10.0f} |",
data.timeCall2.value());
548 std::cout << std::format(
"{:>10.1f}",
data.timeCheck.value());
549 std::cout << std::endl;
#define ATH_CHECK
Evaluate an expression and check for errors.
char data[hepevt_bytes_allocation_ATLAS]
size_t size() const
Number of registered mappings.
ElementLink implementation for ROOT usage.
void record(const T *p, const std::string &key)
a class that holds the columnar data for a single call
void checkData() const
do a basic check of the data vector
void callNoCheck(const IColumnarTool &tool)
call the tool with the assembled data, without performing any checks on the data
the header information for the entire columnar data vector
this is a simple benchmarking helper class wrapping timers from std::chrono
static float getTickDuration()
std::optional< float > getEntryTime(float emptyTime) const
Tool for accessing xAOD files outside of Athena.
A relatively simple transient store for objects created in analysis.
::StatusCode StatusCode
StatusCode definition for legacy code.
void runXaodArrayTest(const UserConfiguration &userConfiguration, const TestDefinition &testDefinition, TFile *file)
void renameContainers(IColumnarTool &tool, const std::vector< std::pair< std::string, std::string > > &renames)
rename containers in the columnar tool
const std::string eventRangeColumnName
the default name for the column containing the event range
std::size_t ColumnarOffsetType
the type used for the size and offsets in the columnar data
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
EventInfo_v1 EventInfo
Definition of the latest event info version.
ShallowCopyResult_t< T > shallowCopy(const T &cont, const EventContext &ctx)
Create a shallow copy of an existing container.
the performance data for reading a single branch/column
the general configuration for a single test
std::string name
the name identifier for the test
std::vector< std::pair< std::string, std::string > > containerRenames
the container name remappings to apply
asg::AsgTool * tool
the tool being tested
a struct holding user configuration for the PHYSLITE tests
std::chrono::seconds targetTime