From f5caddde81ae72f8be7f26e296ee7105f09f1107 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 20 Sep 2022 16:22:11 +0200 Subject: [PATCH 1/9] DPL Analysis: rework index builder * Does not create soa::Table objects anymore, reducing overhead * Some code moved to IndexBuilderHelpers.h/cxx * Groundwork done to allow slice and array index columns for index tables (requires implementing search algos) --- Framework/Core/CMakeLists.txt | 1 + Framework/Core/include/Framework/ASoA.h | 16 ++ .../Core/include/Framework/AnalysisHelpers.h | 229 +++++++----------- .../Core/include/Framework/AnalysisManagers.h | 27 +-- Framework/Core/src/AODReaderHelpers.cxx | 30 ++- Framework/Core/test/test_IndexBuilder.cxx | 4 +- Framework/Foundation/include/Framework/Pack.h | 29 ++- .../test/test_FunctionalHelpers.cxx | 17 +- 8 files changed, 182 insertions(+), 171 deletions(-) diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 1a4902873a2ea..5040366776652 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -81,6 +81,7 @@ o2_add_library(Framework src/GraphvizHelpers.cxx src/MermaidHelpers.cxx src/HTTPParser.cxx + src/IndexBuilderHelpers.cxx src/InputRecord.cxx src/InputRouteHelpers.cxx src/InputSpan.cxx diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 3a86f1c8f1679..4e5963897ce5b 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -1017,6 +1017,10 @@ constexpr bool is_binding_compatible_v() return are_bindings_compatible_v(originals_pack_t{}); } +template +struct is_binding_compatible : std::conditional_t(), std::true_type, std::false_type> { +}; + //! Helper to check if a type T is an iterator template inline constexpr bool is_soa_iterator_v = framework::is_base_of_template_v || framework::is_specialization_v; @@ -1198,6 +1202,18 @@ class Table { } + template + inline arrow::ChunkedArray* getIndexToKey() { + if constexpr (framework::has_type_conditional_v) { + using IC = framework::pack_element_t(external_index_columns_t{}),external_index_columns_t>; + return mColumnChunks[framework::has_type_at(persistent_columns_t{})]; + } else if constexpr (std::is_same_v) { + return nullptr; + } else { + static_assert(framework::always_static_assert_v, "This table does not have an index to this type"); + } + } + unfiltered_iterator begin() { return unfiltered_iterator(mBegin); diff --git a/Framework/Core/include/Framework/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index 8f8d08e047987..e700932369a63 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -20,6 +20,9 @@ #include "Framework/OutputObjHeader.h" #include "Framework/StringHelpers.h" #include "Framework/Output.h" +#include "Framework/IndexBuilderHelpers.h" +#include +#include #include namespace o2::framework { @@ -219,154 +222,102 @@ struct Spawns : TableTransform - static auto indexBuilder(const char* label, framework::pack, Key const&, std::tuple&& tables) - { - static_assert(sizeof...(Cs) == sizeof...(T) + 1, "Number of columns does not coincide with number of supplied tables"); - using tables_t = framework::pack; - using first_t = T1; - auto tail = tuple_tail(tables); - TableBuilder builder; - auto cursor = framework::FFL(builder.cursor>()); - - std::array values; - iterator_tuple_t...> iterators = std::apply( - [](auto&&... x) { - return std::make_tuple(x.begin()...); - }, - tail); - - using rest_it_t = decltype(pack_from_tuple(iterators)); - - auto setValue = [&](auto& x, int idx) -> bool { - using type = std::decay_t; - constexpr auto position = framework::has_type_at_v(rest_it_t{}); - - if constexpr (std::is_same_v...>>, Key>) { - values[position] = idx; - return true; - } else { - lowerBound(idx, x); - if (x == soa::RowViewSentinel{static_cast(x.size())}) { - return false; - } else if (x.template getId() != idx) { - return false; - } else { - values[position] = x.globalIndex(); - ++x; - return true; - } - } - }; - - auto first = std::get(tables); - for (auto& row : first) { - auto idx = -1; - if constexpr (std::is_same_v) { - idx = row.globalIndex(); - } else { - idx = row.template getId(); - } - if (std::apply( - [](auto&... x) { - return ((x == soa::RowViewSentinel{static_cast(x.size())}) && ...); - }, - iterators)) { - break; - } +/// Sparse index: values in a row can be (-1), index table is isomorphic (joinable) +/// to T1 +struct Exclusive {}; +struct Sparse {}; + +namespace { +template +inline arrow::ChunkedArray* getIndexToKey(arrow::Table* table) { +// if constexpr (framework::has_type_conditional_v) { + using IC = framework::pack_element_t(typename T::external_index_columns_t{}),typename T::external_index_columns_t>; + return table->column(framework::has_type_at(typename T::persistent_columns_t{})).get(); +// } else if constexpr (std::is_same_v) { +// return nullptr; +// } else { +// static_assert(framework::always_static_assert_v, "This table does not have an index to this type"); +// } +} - auto result = std::apply( - [&](auto&... x) { - std::array results{setValue(x, idx)...}; - return (results[framework::has_type_at_v>(rest_it_t{})] && ...); - }, - iterators); +template +struct ColumnTrait { + static_assert(framework::is_base_of_template_v, "Not a column type!"); + using column_t = C; - if (result) { - cursor(0, row.globalIndex(), values[framework::has_type_at_v(tables_t{})]...); + static constexpr auto listSize(){ + if constexpr (std::is_same_v>) { + return -1; + } else if constexpr (std::is_same_v) { + return 2; + } else { + return 1; } - } - builder.setLabel(label); - return builder.finalize(); } - template - static auto makeIndex(Key const& key, std::tuple&& tables) + template + static std::shared_ptr makeColumnBuilder(arrow::Table* table, arrow::MemoryPool* pool) { - auto t = IDX{indexBuilder(o2::aod::MetadataTrait::metadata::tableLabel(), - typename o2::aod::MetadataTrait::metadata::index_pack_t{}, - key, - std::make_tuple(std::decay_t{{std::get(tables).asArrowTable()}}, std::decay_t{{std::get(tables).asArrowTable()}}...))}; - t.bindExternalIndices(&key, &std::get(tables), &std::get(tables)...); - return t; + if constexpr (!std::is_same_v) { + return std::make_shared(getIndexToKey(table), C::columnLabel(), listSize(), pool); + } else { + return std::make_shared(C::columnLabel(), pool); + } } }; -/// Sparse index: values in a row can be (-1), index table is isomorphic (joinable) -/// to T1 -struct IndexSparse { - template - static auto indexBuilder(const char* label, framework::pack, Key const&, std::tuple&& tables) - { - static_assert(sizeof...(Cs) == sizeof...(T) + 1, "Number of columns does not coincide with number of supplied tables"); - using tables_t = framework::pack; - using first_t = T1; - auto tail = tuple_tail(tables); - TableBuilder builder; - auto cursor = framework::FFL(builder.cursor>()); - - std::array values; - - iterator_tuple_t...> iterators = std::apply( - [](auto&&... x) { - return std::make_tuple(x.begin()...); - }, - tail); - - using rest_it_t = decltype(pack_from_tuple(iterators)); - - auto setValue = [&](auto& x, int idx) -> bool { - using type = std::decay_t; - constexpr auto position = framework::has_type_at_v(rest_it_t{}); - - if constexpr (std::is_same_v...>>, Key>) { - values[position] = idx; - return true; - } else { - lowerBound(idx, x); - if (x == soa::RowViewSentinel{static_cast(x.size())}) { - values[position] = -1; - return false; - } else if (x.template getId() != idx) { - values[position] = -1; - return false; - } else { - values[position] = x.globalIndex(); - ++x; - return true; - } - } - }; +} - auto first = std::get(tables); - for (auto& row : first) { +template +struct Reduction { + using type = typename std::conditional(),SelfIndexColumnBuilder,IndexColumnBuilder>::type; +}; + +template +struct IndexBuilder { + template + static auto indexBuilder(const char* label, std::vector>&& tables, framework::pack, framework::pack) + { + auto pool = arrow::default_memory_pool(); + SelfIndexColumnBuilder self{C1::columnLabel(),pool}; + std::unique_ptr keyIndex = nullptr; + int64_t counter = 0; + if constexpr (!std::is_same_v) { + keyIndex = std::make_unique(getIndexToKey(tables[0].get())); + } + + std::array, sizeof...(Cs)> columnBuilders{ColumnTrait::template makeColumnBuilder(framework::pack{}), framework::pack>, Key>( + tables[framework::has_type_at_v(framework::pack{}) + 1].get(), + pool)...}; + std::array finds; + + for (counter = 0; counter < tables[0]->num_rows(); ++counter) { auto idx = -1; - if constexpr (std::is_same_v) { - idx = row.globalIndex(); + if constexpr (std::is_same_v) { + idx = counter; } else { - idx = row.template getId(); + idx = keyIndex->valueAt(counter); + } + finds = {std::static_pointer_cast::type>(columnBuilders[framework::has_type_at_v(framework::pack{})])->template find(idx)...}; + if constexpr (std::is_same_v) { + (std::static_pointer_cast::type>(columnBuilders[framework::has_type_at_v(framework::pack{})])->template fill(idx),...); + self.fill(counter); + } else if constexpr (std::is_same_v) { + if (std::none_of(finds.begin(),finds.end(),[](bool const x){ return x == false; })) { + (std::static_pointer_cast::type>(columnBuilders[framework::has_type_at_v(framework::pack{})])->template fill(idx),...); + self.fill(counter); + } } - std::apply( - [&](auto&... x) { - (setValue(x, idx), ...); - }, - iterators); - - cursor(0, row.globalIndex(), values[framework::has_type_at_v(tables_t{})]...); } - builder.setLabel(label); - return builder.finalize(); + + std::vector> columns{self.template result(), std::static_pointer_cast::type>(columnBuilders[framework::has_type_at_v(framework::pack{})])->template result()...}; + std::vector> fields{self.field(), std::static_pointer_cast::type>(columnBuilders[framework::has_type_at_v(framework::pack{})])->field()...}; + auto schema = std::make_shared(fields); + schema->WithMetadata( + std::make_shared( + std::vector{std::string{"label"}}, + std::vector{std::string{label}})); + auto table = arrow::Table::Make(schema, columns); + return table; } template @@ -384,7 +335,7 @@ struct IndexSparse { /// This helper struct allows you to declare index tables to be created in a task template struct Builds : TableTransform::metadata> { - using IP = std::conditional_t::metadata::exclusive, IndexExclusive, IndexSparse>; + using IP = std::conditional_t::metadata::exclusive, IndexBuilder, IndexBuilder>; using Key = typename T::indexing_t; using H = typename T::first_t; using Ts = typename T::rest_t; @@ -410,10 +361,10 @@ struct Builds : TableTransform::metadata> { return index_pack_t{}; } - template - auto build(framework::pack, Key const& key, std::tuple&& tables) + template + auto build(framework::pack, framework::pack, std::vector>&& tables) { - this->table = std::make_shared(IP::indexBuilder(aod::MetadataTrait::metadata::tableLabel(), framework::pack{}, key, std::forward>(tables))); + this->table = std::make_shared(IP::template indexBuilder(aod::MetadataTrait::metadata::tableLabel(), std::forward>>(tables), framework::pack{}, framework::pack{})); return (this->table != nullptr); } }; diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index 4ea6d86ba8054..089c6a7d2bf28 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -346,26 +346,26 @@ struct OutputManager> { }; /// Builds specialization -template -static inline auto extractOriginalsVector(framework::pack, ProcessingContext& pc) +template +static inline auto doExtractOriginal(framework::pack, ProcessingContext& pc) { - return std::vector{extractOriginal(pc)...}; + if constexpr (sizeof...(Ts) == 1) { + return pc.inputs().get(aod::MetadataTrait>>::metadata::tableLabel())->asArrowTable(); + } else { + return std::vector{pc.inputs().get(aod::MetadataTrait::metadata::tableLabel())->asArrowTable()...}; + } } template -static inline auto extractTypedOriginal(ProcessingContext& pc) +static inline auto extractOriginalJoined(ProcessingContext& pc) { - if constexpr (soa::is_type_with_originals_v) { - return O{extractOriginalsVector(soa::originals_pack_t{}, pc)}; - } else { - return O{pc.inputs().get(aod::MetadataTrait::metadata::tableLabel())->asArrowTable()}; - } + return o2::soa::ArrowHelpers::joinTables({doExtractOriginal(soa::make_originals_from_type(), pc)}); } template -static inline auto extractOriginalsTuple(framework::pack, ProcessingContext& pc) +static inline auto extractOriginalsVector(framework::pack, ProcessingContext& pc) { - return std::make_tuple(extractTypedOriginal(pc)...); + return std::vector{extractOriginalJoined(pc)...}; } template @@ -378,9 +378,8 @@ struct OutputManager> { static bool prepare(ProcessingContext& pc, Builds& what) { - return what.build(what.pack(), - extractTypedOriginal::Key>(pc), - extractOriginalsTuple(what.originals_pack(), pc)); + return what.template build(what.pack(),what.originals_pack(), + extractOriginalsVector(what.originals_pack(), pc)); } static bool finalize(ProcessingContext& pc, Builds& what) diff --git a/Framework/Core/src/AODReaderHelpers.cxx b/Framework/Core/src/AODReaderHelpers.cxx index 8899c66009b53..4cccdf7849d15 100644 --- a/Framework/Core/src/AODReaderHelpers.cxx +++ b/Framework/Core/src/AODReaderHelpers.cxx @@ -56,7 +56,7 @@ auto setEOSCallback(InitContext& ic) } template -static inline auto doExtractTypedOriginal(framework::pack, ProcessingContext& pc) +static inline auto doExtractOriginal(framework::pack, ProcessingContext& pc) { if constexpr (sizeof...(Ts) == 1) { return pc.inputs().get(aod::MetadataTrait>>::metadata::tableLabel())->asArrowTable(); @@ -68,7 +68,13 @@ static inline auto doExtractTypedOriginal(framework::pack, ProcessingCont template static inline auto extractTypedOriginal(ProcessingContext& pc) { - return O{doExtractTypedOriginal(soa::make_originals_from_type(), pc)}; + return O{doExtractOriginal(soa::make_originals_from_type(), pc)}; +} + +template +static inline auto extractOriginal(ProcessingContext& pc) +{ + return o2::soa::ArrowHelpers::joinTables({doExtractOriginal(soa::make_originals_from_type(), pc)}); } template @@ -77,6 +83,12 @@ static inline auto extractOriginalsTuple(framework::pack, ProcessingConte return std::make_tuple(extractTypedOriginal(pc)...); } +template +static inline auto extractOriginalsVector(framework::pack, ProcessingContext& pc) +{ + return std::vector{extractOriginal(pc)...}; +} + AlgorithmSpec AODReaderHelpers::indexBuilderCallback(std::vector& requested) { return AlgorithmSpec::InitCallback{[requested](InitContext& ic) { @@ -91,13 +103,15 @@ AlgorithmSpec AODReaderHelpers::indexBuilderCallback(std::vector& req using index_pack_t = typename metadata_t::index_pack_t; using originals = typename metadata_t::originals; if constexpr (metadata_t::exclusive == true) { - return o2::framework::IndexExclusive::indexBuilder(input.binding.c_str(), index_pack_t{}, - extractTypedOriginal(pc), - extractOriginalsTuple(originals{}, pc)); + return o2::framework::IndexBuilder::indexBuilder(input.binding.c_str(), + extractOriginalsVector(originals{}, pc), + index_pack_t{}, + originals{}); } else { - return o2::framework::IndexSparse::indexBuilder(input.binding.c_str(), index_pack_t{}, - extractTypedOriginal(pc), - extractOriginalsTuple(originals{}, pc)); + return o2::framework::IndexBuilder::indexBuilder(input.binding.c_str(), + extractOriginalsVector(originals{}, pc), + index_pack_t{}, + originals{}); } }; diff --git a/Framework/Core/test/test_IndexBuilder.cxx b/Framework/Core/test/test_IndexBuilder.cxx index 388749b514d72..cdc8f91812d8c 100644 --- a/Framework/Core/test/test_IndexBuilder.cxx +++ b/Framework/Core/test/test_IndexBuilder.cxx @@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(TestIndexBuilder) auto t4 = b4.finalize(); Categorys st4{t4}; - auto t5 = IndexExclusive::indexBuilder("test1", typename IDXs::persistent_columns_t{}, st1, std::tie(st1, st2, st3, st4)); + auto t5 = IndexBuilder::indexBuilder("test1a", {t1, t2, t3, t4}, typename IDXs::persistent_columns_t{}, o2::framework::pack{}); BOOST_REQUIRE_EQUAL(t5->num_rows(), 4); IDXs idxt{t5}; idxt.bindExternalIndices(&st1, &st2, &st3, &st4); @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(TestIndexBuilder) BOOST_REQUIRE(row.category().pointId() == row.pointId()); } - auto t6 = IndexSparse::indexBuilder("test2", typename IDX2s::persistent_columns_t{}, st1, std::tie(st2, st1, st3, st4)); + auto t6 = IndexBuilder::indexBuilder("test3", {t2, t1, t3, t4}, typename IDX2s::persistent_columns_t{}, o2::framework::pack{}); BOOST_REQUIRE_EQUAL(t6->num_rows(), st2.size()); IDX2s idxs{t6}; std::array fs{0, 1, 2, -1, -1, 4, -1}; diff --git a/Framework/Foundation/include/Framework/Pack.h b/Framework/Foundation/include/Framework/Pack.h index b05c309a9eb81..fb4e822639daf 100644 --- a/Framework/Foundation/include/Framework/Pack.h +++ b/Framework/Foundation/include/Framework/Pack.h @@ -160,6 +160,16 @@ struct has_type> : std::disjunction...> { template inline constexpr bool has_type_v = has_type::value; +template