Skip to content

Commit a170050

Browse files
committed
DPL Analysis: bind partitions without walking every task member
invokeProcess walked all members of the task seven times per process function to bind partitions and grouped combinations, instantiating the binding lambdas for every member, although only those two kinds act on them; for everything else the calls are empty overloads. Select the partitions and combination generators once per task type, with a named selector so that the full member walk is shared by all process functions, and run the binding lambdas over those only. The calls that do something happen in the same order as before. StructToTuple gains apply_refs_tuple_cat_sized, a member walk whose callable returns a tuple per member, concatenated into one. Full -O2 compile, Apple clang 21, before -> after: - femtoUniverseProducerTask: 66.5 s -> 58.4 s (macro path, as for GCC 14), 67.6 s -> 58.7 s (structured binding packs) - dataCreatorCharmResoToDstarReduced: 44.6 s -> 41.7 s - derivedDataWriter: 79.9 s -> 74.8 s In femtoUniverseProducerTask the instantiation of the member walks goes from 5.9 s to 1.4 s.
1 parent f2a8c28 commit a170050

2 files changed

Lines changed: 102 additions & 63 deletions

File tree

‎Framework/Core/include/Framework/AnalysisTask.h‎

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,25 @@ struct AnalysisDataProcessorBuilder {
315315
(std::get<As>(dest).bindInternalIndicesTo(&std::get<As>(src)), ...);
316316
}
317317

318+
/// The members that binding a process function's arguments can affect.
319+
struct BindableMembers {
320+
template <typename M>
321+
constexpr auto operator()(M& member) const
322+
{
323+
if constexpr (is_partition<M> || is_combinations_generator<M>) {
324+
return std::tuple<M&>{member};
325+
} else {
326+
return std::tuple<>{};
327+
}
328+
}
329+
};
330+
331+
template <typename Members, typename L>
332+
static void forEachRef(Members& members, L&& l)
333+
{
334+
std::apply([&l](auto&... member) { (l(member), ...); }, members);
335+
}
336+
318337
template <typename Task, is_table_iterator_or_enumeration Grouping, std::ranges::input_range R, soa::is_table... Associated>
319338
static void invokeProcess(Task& task, InputRecord& inputs, R matchers, PointerReconstructor const& pointerReconstructor, void (Task::*processingFunction)(Grouping, Associated...), std::vector<ExpressionInfo>& infos, ArrowTableSlicingCache& slices, header::DataOrigin newOrigin = header::DataOrigin{"AOD"})
320339
{
@@ -326,23 +345,22 @@ struct AnalysisDataProcessorBuilder {
326345
}
327346

328347
constexpr const int numElements = homogeneous_apply_refs_size<false, std::decay_t<Task>>();
348+
auto bindable = apply_refs_tuple_cat_sized<numElements>(BindableMembers{}, task);
329349

330350
// set filtered tables for partitions with grouping
331-
homogeneous_apply_refs_sized<numElements>([&groupingTable](auto& element) {
351+
forEachRef(bindable, [&groupingTable](auto& element) {
332352
analysis_task_parsers::setPartition(element, groupingTable);
333353
analysis_task_parsers::bindInternalIndicesPartition(element, &groupingTable);
334354
return true;
335-
},
336-
task);
355+
});
337356

338357
if constexpr (sizeof...(Associated) == 0) {
339358
// single argument to process
340-
homogeneous_apply_refs_sized<numElements>([&groupingTable](auto& element) {
359+
forEachRef(bindable, [&groupingTable](auto& element) {
341360
analysis_task_parsers::bindExternalIndicesPartition(element, &groupingTable);
342361
analysis_task_parsers::setGroupedCombination(element, groupingTable);
343362
return true;
344-
},
345-
task);
363+
});
346364
if constexpr (soa::is_iterator<G>) {
347365
for (auto& element : groupingTable) {
348366
std::invoke(processingFunction, task, *element);
@@ -355,13 +373,12 @@ struct AnalysisDataProcessorBuilder {
355373
auto associatedTables = AnalysisDataProcessorBuilder::bindAssociatedTables(inputs, matchers, processingFunction, infos);
356374
// pre-bind self indices
357375
std::apply(
358-
[&task](auto&... t) mutable {
359-
(homogeneous_apply_refs_sized<numElements>(
360-
[&t](auto& p) {
361-
analysis_task_parsers::bindInternalIndicesPartition(p, &t);
362-
return true;
363-
},
364-
task),
376+
[&bindable](auto&... t) mutable {
377+
(forEachRef(bindable,
378+
[&t](auto& p) {
379+
analysis_task_parsers::bindInternalIndicesPartition(p, &t);
380+
return true;
381+
}),
365382
...);
366383
},
367384
associatedTables);
@@ -370,14 +387,13 @@ struct AnalysisDataProcessorBuilder {
370387
},
371388
associatedTables);
372389

373-
auto binder = [&task, &groupingTable, &associatedTables](auto& x) mutable {
390+
auto binder = [&bindable, &groupingTable, &associatedTables](auto& x) mutable {
374391
x.bindExternalIndices(&groupingTable, &std::get<std::decay_t<Associated>>(associatedTables)...);
375-
homogeneous_apply_refs_sized<numElements>([&x](auto& t) mutable {
392+
forEachRef(bindable, [&x](auto& t) mutable {
376393
analysis_task_parsers::setPartition(t, x);
377394
analysis_task_parsers::bindExternalIndicesPartition(t, &x);
378395
return true;
379-
},
380-
task);
396+
});
381397
};
382398
groupingTable.bindExternalIndices(&std::get<std::decay_t<Associated>>(associatedTables)...);
383399

@@ -389,11 +405,10 @@ struct AnalysisDataProcessorBuilder {
389405
associatedTables);
390406

391407
// GroupedCombinations bound separately, as they should be set once for all associated tables
392-
homogeneous_apply_refs_sized<numElements>([&groupingTable, &associatedTables](auto& t) {
408+
forEachRef(bindable, [&groupingTable, &associatedTables](auto& t) {
393409
analysis_task_parsers::setGroupedCombination(t, groupingTable, associatedTables);
394410
return true;
395-
},
396-
task);
411+
});
397412
overwriteInternalIndices(associatedTables, associatedTables);
398413
if constexpr (soa::is_iterator<G>) {
399414
auto slicer = GroupSlicer(groupingTable, associatedTables, slices, newOrigin);
@@ -412,23 +427,21 @@ struct AnalysisDataProcessorBuilder {
412427
associatedSlices);
413428

414429
// bind partitions and grouping table
415-
homogeneous_apply_refs_sized<numElements>([&groupingTable](auto& x) {
430+
forEachRef(bindable, [&groupingTable](auto& x) {
416431
analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable);
417432
return true;
418-
},
419-
task);
433+
});
420434

421435
[](Task& task, void (Task::*processingFunction)(Grouping, Associated...), Grouping g, std::tuple<std::decay_t<Associated>...>& at) {
422436
std::invoke(processingFunction, task, g, std::get<std::decay_t<Associated>>(at)...);
423437
}(task, processingFunction, slice.groupingElement(), associatedSlices);
424438
}
425439
} else {
426440
// bind partitions and grouping table
427-
homogeneous_apply_refs_sized<numElements>([&groupingTable](auto& x) {
441+
forEachRef(bindable, [&groupingTable](auto& x) {
428442
analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable);
429443
return true;
430-
},
431-
task);
444+
});
432445

433446
[](Task& task, void (Task::*processingFunction)(Grouping, Associated...), Grouping g, std::tuple<std::decay_t<Associated>...>& at) {
434447
std::invoke(processingFunction, task, g, std::get<std::decay_t<Associated>>(at)...);

‎Framework/Foundation/include/Framework/StructToTuple.h‎

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <Framework/Traits.h>
1515
#include <array>
16+
#include <tuple>
1617

1718
// Structured binding packs (P1061) are C++26, but clang implements them as an
1819
// extension in every language mode and advertises them via the feature test
@@ -91,11 +92,13 @@ consteval int homogeneous_apply_refs_size()
9192
#pragma clang diagnostic push
9293
#pragma clang diagnostic ignored "-Wc++26-extensions"
9394
#endif
94-
template <bool B = false, class T, int D = 0, typename L>
95+
template <bool B = false, class T, int D = 0, typename L, bool Cat = false>
9596
constexpr auto homogeneous_apply_refs(L l, T&& object)
9697
{
9798
auto&& [... members] = object;
98-
if constexpr (sizeof...(members) == 0) {
99+
if constexpr (Cat) {
100+
return std::tuple_cat(l(members)...);
101+
} else if constexpr (sizeof...(members) == 0) {
99102
return std::array<bool, 0>();
100103
} else {
101104
return std::array{l(members)...};
@@ -155,28 +158,42 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
155158

156159
#define DPL_FENUM(f, pre, post, d, u) DPL_FENUM_##d##0(f, pre, post), DPL_FENUM_##u(f, pre##d, post)
157160

158-
#define DPL_HOMOGENEOUS_APPLY_ENTRY_LOW(u) \
159-
constexpr(numElements == u) \
160-
{ \
161-
auto&& [DPL_ENUM_##u(p, )] = object; \
162-
return std::array<decltype(l(p0)), u>{DPL_FENUM_##u(l, p, )}; \
161+
#define DPL_HOMOGENEOUS_APPLY_RESULT(n, ...) \
162+
if constexpr (Cat) { \
163+
return std::tuple_cat(__VA_ARGS__); \
164+
} else { \
165+
return std::array<decltype(l(p0)), n>{__VA_ARGS__}; \
163166
}
164167

165-
#define DPL_HOMOGENEOUS_APPLY_ENTRY(d, u) \
166-
constexpr(numElements == d##u) \
167-
{ \
168-
auto&& [DPL_ENUM(p, , d, u)] = object; \
169-
return std::array<decltype(l(p0)), d##u>{DPL_FENUM(l, p, , d, u)}; \
168+
#define DPL_HOMOGENEOUS_APPLY_EMPTY \
169+
if constexpr (Cat) { \
170+
return std::tuple<>(); \
171+
} else { \
172+
return std::array<bool, 0>(); \
170173
}
171174

172-
#define DPL_HOMOGENEOUS_APPLY_ENTRY_TENS(d) \
173-
constexpr(numElements == d##0) \
174-
{ \
175-
auto&& [DPL_ENUM_##d##0(p, )] = object; \
176-
return std::array<decltype(l(p0)), d##0>{DPL_FENUM_##d##0(l, p, )}; \
175+
#define DPL_HOMOGENEOUS_APPLY_ENTRY_LOW(u) \
176+
constexpr(numElements == u) \
177+
{ \
178+
auto&& [DPL_ENUM_##u(p, )] = object; \
179+
DPL_HOMOGENEOUS_APPLY_RESULT(u, DPL_FENUM_##u(l, p, )) \
177180
}
178181

179-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
182+
#define DPL_HOMOGENEOUS_APPLY_ENTRY(d, u) \
183+
constexpr(numElements == d##u) \
184+
{ \
185+
auto&& [DPL_ENUM(p, , d, u)] = object; \
186+
DPL_HOMOGENEOUS_APPLY_RESULT(d##u, DPL_FENUM(l, p, , d, u)) \
187+
}
188+
189+
#define DPL_HOMOGENEOUS_APPLY_ENTRY_TENS(d) \
190+
constexpr(numElements == d##0) \
191+
{ \
192+
auto&& [DPL_ENUM_##d##0(p, )] = object; \
193+
DPL_HOMOGENEOUS_APPLY_RESULT(d##0, DPL_FENUM_##d##0(l, p, )) \
194+
}
195+
196+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
180197
requires(D == 9)
181198
constexpr auto homogeneous_apply_refs(L l, T&& object)
182199
{
@@ -192,11 +209,11 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
192209
else if DPL_HOMOGENEOUS_APPLY_ENTRY (9, 2)
193210
else if DPL_HOMOGENEOUS_APPLY_ENTRY (9, 1)
194211
else if DPL_HOMOGENEOUS_APPLY_ENTRY_TENS (9)
195-
else { return std::array<bool,0>(); }
212+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
196213
// clang-format on
197214
}
198215

199-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
216+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
200217
requires(D == 8)
201218
constexpr auto homogeneous_apply_refs(L l, T&& object)
202219
{
@@ -212,11 +229,11 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
212229
else if DPL_HOMOGENEOUS_APPLY_ENTRY (8, 2)
213230
else if DPL_HOMOGENEOUS_APPLY_ENTRY (8, 1)
214231
else if DPL_HOMOGENEOUS_APPLY_ENTRY_TENS (8)
215-
else { return std::array<bool,0>(); }
232+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
216233
// clang-format on
217234
}
218235

219-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
236+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
220237
requires(D == 7)
221238
constexpr auto homogeneous_apply_refs(L l, T&& object)
222239
{
@@ -232,11 +249,11 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
232249
else if DPL_HOMOGENEOUS_APPLY_ENTRY (7, 2)
233250
else if DPL_HOMOGENEOUS_APPLY_ENTRY (7, 1)
234251
else if DPL_HOMOGENEOUS_APPLY_ENTRY_TENS (7)
235-
else { return std::array<bool,0>(); }
252+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
236253
// clang-format on
237254
}
238255

239-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
256+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
240257
requires(D == 6)
241258
constexpr auto homogeneous_apply_refs(L l, T&& object)
242259
{
@@ -252,11 +269,11 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
252269
else if DPL_HOMOGENEOUS_APPLY_ENTRY (6, 2)
253270
else if DPL_HOMOGENEOUS_APPLY_ENTRY (6, 1)
254271
else if DPL_HOMOGENEOUS_APPLY_ENTRY_TENS (6)
255-
else { return std::array<bool,0>(); }
272+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
256273
// clang-format on
257274
}
258275

259-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
276+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
260277
requires(D == 5)
261278
constexpr auto homogeneous_apply_refs(L l, T&& object)
262279
{
@@ -272,11 +289,11 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
272289
else if DPL_HOMOGENEOUS_APPLY_ENTRY (5, 2)
273290
else if DPL_HOMOGENEOUS_APPLY_ENTRY (5, 1)
274291
else if DPL_HOMOGENEOUS_APPLY_ENTRY_TENS (5)
275-
else { return std::array<bool,0>(); }
292+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
276293
// clang-format on
277294
}
278295

279-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
296+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
280297
requires(D == 4)
281298
constexpr auto homogeneous_apply_refs(L l, T&& object)
282299
{
@@ -292,11 +309,11 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
292309
else if DPL_HOMOGENEOUS_APPLY_ENTRY (4, 2)
293310
else if DPL_HOMOGENEOUS_APPLY_ENTRY (4, 1)
294311
else if DPL_HOMOGENEOUS_APPLY_ENTRY_TENS (4)
295-
else { return std::array<bool,0>(); }
312+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
296313
// clang-format on
297314
}
298315

299-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
316+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
300317
requires(D == 3)
301318
constexpr auto homogeneous_apply_refs(L l, T&& object)
302319
{
@@ -312,11 +329,11 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
312329
else if DPL_HOMOGENEOUS_APPLY_ENTRY (3, 2)
313330
else if DPL_HOMOGENEOUS_APPLY_ENTRY (3, 1)
314331
else if DPL_HOMOGENEOUS_APPLY_ENTRY_TENS (3)
315-
else { return std::array<bool,0>(); }
332+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
316333
// clang-format on
317334
}
318335

319-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
336+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
320337
requires(D == 2)
321338
constexpr auto homogeneous_apply_refs(L l, T&& object)
322339
{
@@ -332,11 +349,11 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
332349
else if DPL_HOMOGENEOUS_APPLY_ENTRY (2, 2)
333350
else if DPL_HOMOGENEOUS_APPLY_ENTRY (2, 1)
334351
else if DPL_HOMOGENEOUS_APPLY_ENTRY_TENS (2)
335-
else { return std::array<bool,0>(); }
352+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
336353
// clang-format on
337354
}
338355

339-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
356+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
340357
requires(D == 1)
341358
constexpr auto homogeneous_apply_refs(L l, T&& object)
342359
{
@@ -352,11 +369,11 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
352369
else if DPL_HOMOGENEOUS_APPLY_ENTRY (1, 2)
353370
else if DPL_HOMOGENEOUS_APPLY_ENTRY (1, 1)
354371
else if DPL_HOMOGENEOUS_APPLY_ENTRY_TENS (1)
355-
else { return std::array<bool,0>(); }
372+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
356373
// clang-format on
357374
}
358375

359-
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L>
376+
template <bool B = false, class T, int D = nested_brace_constructible_size<B, T>() / 10, typename L, bool Cat = false>
360377
requires(D == 0)
361378
constexpr auto homogeneous_apply_refs(L l, T&& object)
362379
{
@@ -371,7 +388,7 @@ constexpr auto homogeneous_apply_refs(L l, T&& object)
371388
else if DPL_HOMOGENEOUS_APPLY_ENTRY_LOW (3)
372389
else if DPL_HOMOGENEOUS_APPLY_ENTRY_LOW (2)
373390
else if DPL_HOMOGENEOUS_APPLY_ENTRY_LOW (1)
374-
else { return std::array<bool,0>(); }
391+
else { DPL_HOMOGENEOUS_APPLY_EMPTY }
375392
// clang-format on
376393
}
377394

@@ -383,6 +400,15 @@ constexpr auto homogeneous_apply_refs_sized(L l, T&& object)
383400
return homogeneous_apply_refs<false, T, D, L>(l, object);
384401
}
385402

403+
/// Like homogeneous_apply_refs_sized, but l returns a std::tuple for each
404+
/// member and the results are concatenated: returning std::tuple<M&> or
405+
/// std::tuple<>() selects members. The calls to l are unsequenced.
406+
template <int D, typename T, typename L>
407+
constexpr auto apply_refs_tuple_cat_sized(L l, T&& object)
408+
{
409+
return homogeneous_apply_refs<false, T, D, L, true>(l, object);
410+
}
411+
386412
} // namespace o2::framework
387413

388414
#endif // O2_FRAMEWORK_STRUCTTOTUPLE_H_

0 commit comments

Comments
 (0)