33#include <boost/core/demangle.hpp>
35#include <gtest/gtest.h>
47 struct IColumnReaderXA;
52 std::shared_ptr<TestUtils::IColumnReaderXA> reader;
55 struct IColumnReaderXA
57 virtual ~IColumnReaderXA () =
default;
59 virtual void connect (ColumnDataXA& , std::unordered_map<std::string,ColumnDataXA>& ) {}
61 virtual void requestShallowCopy () {
62 throw std::runtime_error (
"shallow copy not supported for column reader");
65 virtual StatusCode retrieveContainer (xAOD::TEvent& , xAOD::TStore& , ColumnVectorData& ) {
return StatusCode::SUCCESS; };
67 virtual std::pair<const SG::AuxElement*,std::size_t> getObject ()
const {
68 throw std::runtime_error (
"getObject not implemented for this XAOD column reader"); }
70 virtual void retrieveAuxData (ColumnVectorData& ) {}
72 [[nodiscard]]
virtual BranchPerfData getPerfData (
float ) = 0;
75 struct ColumnDataXAEventInfo
final :
public IColumnReaderXA, asg::AsgMessaging
77 std::string m_name =
"EventInfo";
79 std::array<ColumnarOffsetType, 2> data = {0, 1};
81 Benchmark benchmarkFirstRetrieve;
82 Benchmark benchmarkSecondRetrieve;
84 ColumnDataXAEventInfo (
const ColumnInfo& info)
85 : AsgMessaging (
"ColumnDataXAEventInfo"), index (
info.index)
88 virtual StatusCode retrieveContainer (xAOD::TEvent& event, xAOD::TStore& , ColumnVectorData& columnData)
override
90 benchmarkFirstRetrieve.startTimer ();
92 benchmarkFirstRetrieve.stopTimer ();
93 benchmarkSecondRetrieve.startTimer ();
95 benchmarkSecondRetrieve.stopTimer ();
96 columnData.setColumn (index, data.size(), data.data());
97 return StatusCode::SUCCESS;
100 virtual std::pair<const SG::AuxElement*,std::size_t> getObject ()
const override
102 return {eventInfo, 1};
105 [[nodiscard]]
virtual BranchPerfData getPerfData (
float emptyTime)
override
108 result.name =
"EventInfo";
109 result.timeRead = benchmarkFirstRetrieve.getEntryTime(emptyTime);
110 result.timeReadAgain = benchmarkSecondRetrieve.getEntryTime(emptyTime);
111 benchmarkFirstRetrieve.setSilence();
112 benchmarkSecondRetrieve.setSilence();
117 template<
typename XAODObjectType>
118 struct ColumnDataXARetrieve
final :
public IColumnReaderXA, asg::AsgMessaging
120 std::string containerName;
122 bool shallowCopy =
false;
123 bool skipShallowCopies =
false;
124 std::array<ColumnarOffsetType, 2> data = {0, 1};
125 const XAODObjectType*
object =
nullptr;
126 Benchmark benchmarkFirstRetrieve;
127 Benchmark benchmarkSecondRetrieve;
128 Benchmark benchmarkShallowCopy;
129 Benchmark benchmarkShallowRegister;
131 ColumnDataXARetrieve (
const ColumnInfo& info,
const UserConfiguration& userConfiguration)
132 : AsgMessaging (
"ColumnDataXARetrieve_" +
info.
name), containerName (
info.
name), index (
info.index), skipShallowCopies (userConfiguration.skipShallowCopies)
135 virtual void requestShallowCopy()
override {
136 if (!skipShallowCopies)
140 virtual StatusCode retrieveContainer (xAOD::TEvent& event, xAOD::TStore& store, ColumnVectorData& columnData)
override
142 benchmarkFirstRetrieve.startTimer ();
144 benchmarkFirstRetrieve.stopTimer ();
145 benchmarkSecondRetrieve.startTimer ();
147 benchmarkSecondRetrieve.stopTimer ();
149 throw std::logic_error (
"no object retrieved for XAOD container (in retrieveContainer): " + containerName);
150 data[1] =
object->size();
151 columnData.setColumn (index, data.size(), data.data());
154 std::string shallowName = containerName +
"_shallowCopy";
155 std::string shallowAuxName = shallowName +
"Aux.";
156 benchmarkShallowCopy.startTimer ();
158 benchmarkShallowCopy.stopTimer ();
159 benchmarkShallowRegister.startTimer ();
162 benchmarkShallowRegister.stopTimer ();
163 object = shallowCopy.first;
165 return StatusCode::SUCCESS;
168 virtual std::pair<const SG::AuxElement*,std::size_t> getObject ()
const override
171 throw std::logic_error (
"no object retrieved for XAOD container: " + containerName);
172 if (object->size() == 0)
174 return {(*object)[0],
object->size()};
177 [[nodiscard]]
virtual BranchPerfData getPerfData (
float emptyTime)
override
180 result.name = containerName;
181 result.timeRead = benchmarkFirstRetrieve.getEntryTime(emptyTime);
182 result.timeReadAgain = benchmarkSecondRetrieve.getEntryTime(emptyTime);
185 result.timeShallowCopy = benchmarkShallowCopy.getEntryTime(emptyTime);
186 result.timeShallowRegister = benchmarkShallowRegister.getEntryTime(emptyTime);
188 benchmarkFirstRetrieve.setSilence();
189 benchmarkSecondRetrieve.setSilence();
190 benchmarkShallowCopy.setSilence();
191 benchmarkShallowRegister.setSilence();
197 struct ColumnDataXAAccessor
final :
public IColumnReaderXA
201 std::optional<SG::AuxElement::Accessor<T>> accessor;
202 std::optional<SG::AuxElement::Decorator<T>> decorator;
203 bool isOptional =
false;
204 bool measureNonAccessForEmpty =
false;
205 const IColumnReaderXA *objectReader =
nullptr;
206 Benchmark benchmarkFirstRetrieve;
207 Benchmark benchmarkSecondRetrieve;
209 ColumnDataXAAccessor (
const ColumnInfo& info,
const UserConfiguration& userConfiguration)
210 : m_name (
info.
name), index (
info.index), measureNonAccessForEmpty (userConfiguration.measureNonAccessForEmpty)
213 if (!
info.replacesColumn.empty())
214 name =
info.replacesColumn;
215 name =
name.substr (
name.find_last_of(
'.') + 1);
218 accessor.emplace (name);
219 isOptional = info.isOptional;
222 decorator.emplace (name);
225 throw std::runtime_error (
"unsupported access mode for XAOD accessor: " + info.name);
229 virtual void connect (ColumnDataXA& columnData, std::unordered_map<std::string,ColumnDataXA>& requestedColumns)
override
231 auto iter = requestedColumns.find (columnData.info.offsetName);
232 if (iter == requestedColumns.end())
233 throw std::runtime_error (
"missing offset column for XAOD accessor: " + columnData.info.offsetName);
234 objectReader =
iter->second.reader.get();
235 if (decorator.has_value())
236 iter->second.reader->requestShallowCopy();
239 virtual void retrieveAuxData (ColumnVectorData& columnData)
override
242 throw std::logic_error (
"no object reader for XAOD accessor");
243 auto [
object, size] = objectReader->getObject();
244 if (
object ==
nullptr)
246 if (measureNonAccessForEmpty)
248 benchmarkFirstRetrieve.startTimer ();
249 benchmarkFirstRetrieve.stopTimer ();
250 benchmarkSecondRetrieve.startTimer ();
251 benchmarkSecondRetrieve.stopTimer ();
253 if (accessor.has_value())
254 columnData.setColumn (index, 0,
static_cast<const T*
>(
nullptr));
255 else if (decorator.has_value())
256 columnData.setColumn (index, 0,
static_cast<T*
>(
nullptr));
258 throw std::logic_error (
"no accessor or decorator for XAOD accessor");
259 }
else if (accessor.has_value())
264 benchmarkFirstRetrieve.startTimer ();
265 (*accessor) (*object);
266 benchmarkFirstRetrieve.stopTimer ();
267 benchmarkSecondRetrieve.startTimer ();
268 value = &(*accessor) (*object);
269 benchmarkSecondRetrieve.stopTimer ();
270 columnData.setColumn (index, size, value);
274 benchmarkFirstRetrieve.startTimer ();
275 if (accessor->isAvailable (*
object))
276 (*accessor) (*object);
277 benchmarkFirstRetrieve.stopTimer ();
278 benchmarkSecondRetrieve.startTimer ();
279 if (accessor->isAvailable (*
object))
280 value = &(*accessor) (*object);
281 benchmarkSecondRetrieve.stopTimer ();
282 if (accessor->isAvailable (*
object))
283 columnData.setColumn (index, size, value);
285 }
else if (decorator.has_value())
288 benchmarkFirstRetrieve.startTimer ();
289 (*decorator) (*object);
290 benchmarkFirstRetrieve.stopTimer ();
291 benchmarkSecondRetrieve.startTimer ();
292 value = &(*decorator) (*object);
293 benchmarkSecondRetrieve.stopTimer ();
294 columnData.setColumn (index, size, value);
297 throw std::logic_error (
"no accessor or decorator for XAOD accessor");
301 [[nodiscard]]
virtual BranchPerfData getPerfData (
float emptyTime)
override
305 result.timeRead = benchmarkFirstRetrieve.getEntryTime(emptyTime);
306 result.timeReadAgain = benchmarkSecondRetrieve.getEntryTime(emptyTime);
307 benchmarkFirstRetrieve.setSilence();
308 benchmarkSecondRetrieve.setSilence();
318 using namespace asg::msgUserCode;
326 throw std::runtime_error (
"tool is not a ColumnarTool<ColumnarModeXAODArray>");
332 std::unordered_map<std::string,TestUtils::ColumnDataXA> requestedColumns;
333 for (
auto& column : myTool->getColumnInfo())
334 requestedColumns[column.name].info = std::move (column);
335 for (
auto& [name,
data] : requestedColumns)
337 if (
data.info.isOffset)
340 throw std::runtime_error (
"unexpected type for offset column: " + name +
" " +
data.info.type->name());
343 data.reader = std::make_shared<TestUtils::ColumnDataXAEventInfo> (
data.info);
346 if (name ==
"AnalysisMuons")
347 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::MuonContainer>> (
data.info, userConfiguration);
348 else if (name ==
"AnalysisElectrons")
349 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::ElectronContainer>> (
data.info, userConfiguration);
350 else if (name ==
"AnalysisPhotons")
351 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::PhotonContainer>> (
data.info, userConfiguration);
352 else if (name ==
"egammaClusters")
353 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::CaloClusterContainer>> (
data.info, userConfiguration);
354 else if (name ==
"GSFTrackParticles")
355 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::TrackParticleContainer>> (
data.info, userConfiguration);
356 else if (name ==
"GSFConversionVertices")
357 data.reader = std::make_shared<TestUtils::ColumnDataXARetrieve<xAOD::VertexContainer>> (
data.info, userConfiguration);
361 if (*
data.info.type ==
typeid(
float))
362 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<float>> (
data.info, userConfiguration);
363 else if (*
data.info.type ==
typeid(
double))
364 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<double>> (
data.info, userConfiguration);
365 else if (*
data.info.type ==
typeid(
char))
366 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<char>> (
data.info, userConfiguration);
367 else if (*
data.info.type ==
typeid(std::uint8_t))
368 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::uint8_t>> (
data.info, userConfiguration);
369 else if (*
data.info.type ==
typeid(std::uint16_t))
370 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::uint16_t>> (
data.info, userConfiguration);
371 else if (*
data.info.type ==
typeid(std::uint32_t))
372 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::uint32_t>> (
data.info, userConfiguration);
373 else if (*
data.info.type ==
typeid(std::uint64_t))
374 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::uint64_t>> (
data.info, userConfiguration);
375 else if (*
data.info.type ==
typeid(std::vector<float>))
376 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::vector<float>>> (
data.info, userConfiguration);
378 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::vector<ElementLink<xAOD::CaloClusterContainer>>>> (
data.info, userConfiguration);
380 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::vector<ElementLink<xAOD::TrackParticleContainer>>>> (
data.info, userConfiguration);
382 data.reader = std::make_shared<TestUtils::ColumnDataXAAccessor<std::vector<ElementLink<xAOD::VertexContainer>>>> (
data.info, userConfiguration);
386 bool allColumnsHandled =
true;
387 for (
auto& [name,
data] : requestedColumns)
391 allColumnsHandled =
false;
392 std::cout <<
"WARNING: no handling for requested column: name=" << name <<
" offset=" <<
data.info.offsetName <<
" type=" << boost::core::demangle(
data.info.type->name()) << std::endl;
395 if (!allColumnsHandled)
397 ADD_FAILURE() <<
"not all requested columns could be handled";
401 for (
auto& [name,
data]: requestedColumns)
403 data.reader->connect (
data, requestedColumns);
406 Benchmark benchmarkEmptyClear (testDefinition.
name +
" empty clear");
407 Benchmark benchmarkCallClear (testDefinition.
name +
" call clear");
408 Benchmark benchmarkGetEntry (testDefinition.
name +
" getEntry");
416 throw std::runtime_error (
"ColumnarPhysLiteTest: numberOfEvents == 0");
425 const auto startTime = std::chrono::high_resolution_clock::now();
426 for (; (std::chrono::high_resolution_clock::now() - startTime) < userConfiguration.
targetTime; ++entry)
436 for (
auto& [name,
data] : requestedColumns)
438 for (
auto& [name,
data] : requestedColumns)
439 data.reader->retrieveAuxData (columnData);
461 std::cout <<
"Total entries read: " << entry << std::endl;
462 const float emptyTime = benchmarkEmpty.
getEntryTime(0).value();
463 std::cout <<
"Empty benchmark time: " << emptyTime <<
"ns" << std::endl;
465 std::cout <<
"Average getEntry time: " << benchmarkGetEntry.
getEntryTime(emptyTime).value() <<
"ns" << std::endl;
467 std::cout <<
"Average clear time: " << benchmarkCallClear.
getEntryTime(emptyTime).value() <<
"ns" <<
" (empty=" << benchmarkEmptyClear.
getEntryTime(emptyTime).value() <<
"ns)" << std::endl;
473 std::vector<BranchPerfData> columnPerfData;
475 summary.name =
"total";
476 summary.timeRead = 0;
477 summary.timeReadAgain = 0;
478 summary.timeShallowCopy = 0;
479 summary.timeShallowRegister = 0;
480 for (
auto& [name,
data] : requestedColumns)
482 auto perfData =
data.reader->getPerfData(emptyTime);
483 if (perfData.timeRead.has_value() || perfData.timeReadAgain.has_value())
485 columnPerfData.push_back(perfData);
486 summary.timeRead.value() += perfData.timeRead.value_or(0);
487 summary.timeReadAgain.value() += perfData.timeReadAgain.value_or(0);
488 summary.timeShallowCopy.value() += perfData.timeShallowCopy.value_or(0);
489 summary.timeShallowRegister.value() += perfData.timeShallowRegister.value_or(0);
492 std::sort(columnPerfData.begin(), columnPerfData.end(), [](
const auto&
a,
const auto& b) { return a.name < b.name; });
493 columnPerfData.push_back(summary);
495 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();
496 std::string
header = std::format(
"{:{}} | 1st(ns) | 2nd(ns) | shallow copy(ns)",
"column name", nameWidth);
497 std::cout <<
"\n" <<
header << std::endl;
498 std::cout << std::string(
header.size(),
'-') << std::endl;
499 for (
auto&
data : columnPerfData)
501 if (
data.name ==
"total")
502 std::cout << std::string(
header.size(),
'-') << std::endl;
503 std::cout << std::format(
"{:{}} |",
data.name, nameWidth);
505 std::cout << std::format(
"{:>8.0f} |",
data.timeRead.value());
508 if (
data.timeReadAgain)
509 std::cout << std::format(
"{:>8.1f} |",
data.timeReadAgain.value());
512 if (
data.timeShallowCopy ||
data.timeShallowRegister)
513 std::cout << std::format(
"{:>10.0f} +{:>5.0f}",
data.timeShallowCopy.value_or(-1),
data.timeShallowRegister .value_or(-1));
514 std::cout << std::endl;
520 std::vector<ToolPerfData> toolPerfData;
521 toolPerfData.emplace_back();
522 toolPerfData.back().name = testDefinition.
name;
523 toolPerfData.back().timeCall = benchmarkCall.
getEntryTime(emptyTime);
525 toolPerfData.back().timeCall2 = benchmarkCall2.
getEntryTime(emptyTime);
526 toolPerfData.back().timeCheck = benchmarkCheck.
getEntryTime(emptyTime);
531 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();
532 std::string
header = std::format(
"{:{}} | call(ns) | call2(ns) | check(ns)",
"tool name", nameWidth);
533 std::cout <<
"\n" <<
header << std::endl;
534 std::cout << std::string(
header.size(),
'-') << std::endl;
535 for (
auto&
data : toolPerfData)
537 std::cout << std::format(
"{:{}} |",
data.name, nameWidth);
539 std::cout << std::format(
"{:>9.0f} |",
data.timeCall.value());
543 std::cout << std::format(
"{:>10.0f} |",
data.timeCall2.value());
547 std::cout << std::format(
"{:>10.1f}",
data.timeCheck.value());
548 std::cout << std::endl;
#define ATH_CHECK
Evaluate an expression and check for errors.
char data[hepevt_bytes_allocation_ATLAS]
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
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)
const std::string numberOfEventsName
the name used for the column containing the number of events
void renameContainers(IColumnarTool &tool, const std::vector< std::pair< std::string, std::string > > &renames)
rename containers in the columnar tool
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.
std::pair< std::unique_ptr< T >, std::unique_ptr< ShallowAuxContainer > > shallowCopyContainer(const T &cont, const EventContext &ctx)
Function making a shallow copy of a constant 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