Skip to content

Commit 47d098d

Browse files
authored
Get column value by column label in Framework Core ASoA (#13498)
1 parent c5cbdc4 commit 47d098d

File tree

3 files changed

+138
-1
lines changed

3 files changed

+138
-1
lines changed

Framework/Core/include/Framework/ASoA.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <array>
3131
#include <cassert>
3232
#include <fmt/format.h>
33+
#include <concepts>
34+
#include <cstring>
3335
#include <gsl/span>
3436
#include <limits>
3537

@@ -2172,6 +2174,82 @@ std::tuple<typename Cs::type...> getRowData(arrow::Table* table, T rowIterator,
21722174
{
21732175
return std::make_tuple(getSingleRowData<T, Cs>(table, rowIterator, ci, ai, globalIndex)...);
21742176
}
2177+
2178+
template <typename R, typename T, typename C>
2179+
R getColumnValue(const T& rowIterator)
2180+
{
2181+
return static_cast<R>(static_cast<C>(rowIterator).get());
2182+
}
2183+
2184+
template <typename R, typename T>
2185+
using ColumnGetterFunction = R (*)(const T&);
2186+
2187+
template <typename T, typename R>
2188+
concept dynamic_with_common_getter = is_dynamic_column<T> &&
2189+
// lambda is callable without additional free args
2190+
framework::pack_size(typename T::bindings_t{}) == framework::pack_size(typename T::callable_t::args{}) &&
2191+
requires(T t) {
2192+
{ t.get() } -> std::convertible_to<R>;
2193+
};
2194+
2195+
template <typename T, typename R>
2196+
concept persistent_with_common_getter = is_persistent_v<T> && requires(T t) {
2197+
{ t.get() } -> std::convertible_to<R>;
2198+
};
2199+
2200+
template <typename R, typename T, persistent_with_common_getter<R> C>
2201+
ColumnGetterFunction<R, T> createGetterPtr(const std::string_view& targetColumnLabel)
2202+
{
2203+
return targetColumnLabel == C::columnLabel() ? &getColumnValue<R, T, C> : nullptr;
2204+
}
2205+
2206+
template <typename R, typename T, dynamic_with_common_getter<R> C>
2207+
ColumnGetterFunction<R, T> createGetterPtr(const std::string_view& targetColumnLabel)
2208+
{
2209+
std::string_view columnLabel(C::columnLabel());
2210+
2211+
// allows user to use consistent formatting (with prefix) of all column labels
2212+
// by default there isn't 'f' prefix for dynamic column labels
2213+
if (targetColumnLabel.starts_with("f") && targetColumnLabel.substr(1) == columnLabel) {
2214+
return &getColumnValue<R, T, C>;
2215+
}
2216+
2217+
// check also exact match if user is aware of prefix missing
2218+
if (targetColumnLabel == columnLabel) {
2219+
return &getColumnValue<R, T, C>;
2220+
}
2221+
2222+
return nullptr;
2223+
}
2224+
2225+
template <typename R, typename T, typename... Cs>
2226+
ColumnGetterFunction<R, T> getColumnGetterByLabel(o2::framework::pack<Cs...>, const std::string_view& targetColumnLabel)
2227+
{
2228+
ColumnGetterFunction<R, T> func;
2229+
2230+
(void)((func = createGetterPtr<R, T, Cs>(targetColumnLabel), func) || ...);
2231+
2232+
if (!func) {
2233+
throw framework::runtime_error_f("Getter for \"%s\" not found", targetColumnLabel);
2234+
}
2235+
2236+
return func;
2237+
}
2238+
2239+
template <typename T, typename R>
2240+
using with_common_getter_t = typename std::conditional<persistent_with_common_getter<T, R> || dynamic_with_common_getter<T, R>, std::true_type, std::false_type>::type;
2241+
2242+
template <typename R, typename T>
2243+
ColumnGetterFunction<R, typename T::iterator> getColumnGetterByLabel(const std::string_view& targetColumnLabel)
2244+
{
2245+
using TypesWithCommonGetter = o2::framework::selected_pack_multicondition<with_common_getter_t, framework::pack<R>, typename T::columns_t>;
2246+
2247+
if (targetColumnLabel.size() == 0) {
2248+
throw framework::runtime_error("columnLabel: must not be empty");
2249+
}
2250+
2251+
return getColumnGetterByLabel<R, typename T::iterator>(TypesWithCommonGetter{}, targetColumnLabel);
2252+
}
21752253
} // namespace row_helpers
21762254
} // namespace o2::soa
21772255

Framework/Core/include/Framework/BinningPolicy.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#ifndef FRAMEWORK_BINNINGPOLICY_H
1313
#define FRAMEWORK_BINNINGPOLICY_H
1414

15-
#include "Framework/ASoA.h"
1615
#include "Framework/HistogramSpec.h" // only for VARIABLE_WIDTH
1716
#include "Framework/Pack.h"
1817

Framework/Core/test/benchmark_ASoA.cxx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ DECLARE_SOA_COLUMN_FULL(X, x, float, "x");
2929
DECLARE_SOA_COLUMN_FULL(Y, y, float, "y");
3030
DECLARE_SOA_COLUMN_FULL(Z, z, float, "z");
3131
DECLARE_SOA_DYNAMIC_COLUMN(Sum, sum, [](float x, float y) { return x + y; });
32+
DECLARE_SOA_DYNAMIC_COLUMN(SumFreeArgs, sumFreeArgs, [](float x, float y, float freeArg) { return x + y + freeArg; });
3233
} // namespace test
3334

3435
DECLARE_SOA_TABLE(TestTable, "AOD", "TESTTBL", test::X, test::Y, test::Z, test::Sum<test::X, test::Y>);
@@ -290,6 +291,36 @@ static void BM_ASoADynamicColumnPresent(benchmark::State& state)
290291

291292
BENCHMARK(BM_ASoADynamicColumnPresent)->Range(8, 8 << maxrange);
292293

294+
static void BM_ASoADynamicColumnPresentGetGetterByLabel(benchmark::State& state)
295+
{
296+
// Seed with a real random value, if available
297+
std::default_random_engine e1(1234567891);
298+
std::uniform_real_distribution<float> uniform_dist(0, 1);
299+
300+
TableBuilder builder;
301+
auto rowWriter = builder.persist<float, float, float>({"x", "y", "z"});
302+
for (auto i = 0; i < state.range(0); ++i) {
303+
rowWriter(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1));
304+
}
305+
auto table = builder.finalize();
306+
307+
using Test = o2::soa::InPlaceTable<"A/0"_h, test::X, test::Y, test::Z, test::Sum<test::X, test::Y>>;
308+
309+
for (auto _ : state) {
310+
Test tests{table};
311+
float sum = 0;
312+
auto xGetter = o2::soa::row_helpers::getColumnGetterByLabel<float, Test>("x");
313+
auto yGetter = o2::soa::row_helpers::getColumnGetterByLabel<float, Test>("y");
314+
for (auto& test : tests) {
315+
sum += xGetter(test) + yGetter(test);
316+
}
317+
benchmark::DoNotOptimize(sum);
318+
}
319+
state.SetBytesProcessed(state.iterations() * state.range(0) * sizeof(float) * 2);
320+
}
321+
322+
BENCHMARK(BM_ASoADynamicColumnPresentGetGetterByLabel)->Range(8, 8 << maxrange);
323+
293324
static void BM_ASoADynamicColumnCall(benchmark::State& state)
294325
{
295326
// Seed with a real random value, if available
@@ -317,4 +348,33 @@ static void BM_ASoADynamicColumnCall(benchmark::State& state)
317348
}
318349
BENCHMARK(BM_ASoADynamicColumnCall)->Range(8, 8 << maxrange);
319350

351+
static void BM_ASoADynamicColumnCallGetGetterByLabel(benchmark::State& state)
352+
{
353+
// Seed with a real random value, if available
354+
std::default_random_engine e1(1234567891);
355+
std::uniform_real_distribution<float> uniform_dist(0, 1);
356+
357+
TableBuilder builder;
358+
auto rowWriter = builder.persist<float, float, float>({"x", "y", "z"});
359+
for (auto i = 0; i < state.range(0); ++i) {
360+
rowWriter(0, uniform_dist(e1), uniform_dist(e1), uniform_dist(e1));
361+
}
362+
auto table = builder.finalize();
363+
364+
// SumFreeArgs presence checks if dynamic columns get() is handled correctly during compilation
365+
using Test = o2::soa::InPlaceTable<"A/0"_h, test::X, test::Y, test::Sum<test::X, test::Y>, test::SumFreeArgs<test::X, test::Y>>;
366+
367+
Test tests{table};
368+
for (auto _ : state) {
369+
float sum = 0;
370+
auto sumGetter = o2::soa::row_helpers::getColumnGetterByLabel<float, Test>("Sum");
371+
for (auto& test : tests) {
372+
sum += sumGetter(test);
373+
}
374+
benchmark::DoNotOptimize(sum);
375+
}
376+
state.SetBytesProcessed(state.iterations() * state.range(0) * sizeof(float) * 2);
377+
}
378+
BENCHMARK(BM_ASoADynamicColumnCallGetGetterByLabel)->Range(8, 8 << maxrange);
379+
320380
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)