Skip to content

Commit 10aac8e

Browse files
committed
Use accumulator_node for fold accumulators
Also remove unnecessary infrastructure
1 parent 5450d61 commit 10aac8e

17 files changed

Lines changed: 703 additions & 395 deletions

phlex/core/CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ cet_make_library(
2828
registrar.cpp
2929
registration_api.cpp
3030
product_query.cpp
31-
store_counters.cpp
3231
LIBRARIES
3332
PUBLIC
3433
TBB::tbb
@@ -66,7 +65,6 @@ install(
6665
provider_node.hpp
6766
registrar.hpp
6867
registration_api.hpp
69-
store_counters.hpp
7068
upstream_predicates.hpp
7169
DESTINATION include/phlex/core
7270
)

phlex/core/declared_fold.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
namespace phlex::experimental {
44
declared_fold::declared_fold(algorithm_name name,
55
std::vector<std::string> predicates,
6-
product_queries input_products) :
7-
products_consumer{std::move(name), std::move(predicates), std::move(input_products)}
6+
product_queries input_products,
7+
std::string partition_layer) :
8+
products_consumer{std::move(name), std::move(predicates), std::move(input_products)},
9+
partition_layer_{std::move(partition_layer)}
810
{
911
}
1012

phlex/core/declared_fold.hpp

Lines changed: 58 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
#include "phlex/concurrency.hpp"
77
#include "phlex/core/concepts.hpp"
88
#include "phlex/core/fold/send.hpp"
9+
#include "phlex/core/fold_join_node.hpp"
910
#include "phlex/core/fwd.hpp"
1011
#include "phlex/core/input_arguments.hpp"
1112
#include "phlex/core/message.hpp"
12-
#include "phlex/core/multilayer_join_node.hpp"
1313
#include "phlex/core/product_query.hpp"
1414
#include "phlex/core/products_consumer.hpp"
15-
#include "phlex/core/store_counters.hpp"
1615
#include "phlex/model/algorithm_name.hpp"
1716
#include "phlex/model/data_cell_index.hpp"
1817
#include "phlex/model/handle.hpp"
@@ -40,26 +39,44 @@ namespace phlex::experimental {
4039
public:
4140
declared_fold(algorithm_name name,
4241
std::vector<std::string> predicates,
43-
product_queries input_products);
42+
product_queries input_products,
43+
std::string partition_layer);
4444
~declared_fold() override;
4545

4646
virtual tbb::flow::sender<message>& output_port() = 0;
47-
virtual tbb::flow::receiver<flush_message>& flush_port() = 0;
4847
virtual product_specifications const& output() const = 0;
48+
virtual tbb::flow::receiver<index_message>& partition_port() = 0;
4949
virtual std::size_t product_count() const = 0;
50+
identifier const& partition_layer() const { return partition_layer_; }
51+
52+
private:
53+
identifier partition_layer_;
5054
};
5155

5256
using declared_fold_ptr = std::unique_ptr<declared_fold>;
5357
using declared_folds = simple_ptr_map<declared_fold_ptr>;
5458

59+
template <typename FoldResult, typename InitTuple, std::size_t... Is>
60+
auto make_initializer(InitTuple args, std::index_sequence<Is...>)
61+
{
62+
// The compiler emits a warning if an empty args tuple is captured by reference and then not expanded inside the lambda expression. We therefore only capture args in the lambda expression if sizeof...(Is) > 0.
63+
if constexpr (sizeof...(Is) == 0) {
64+
return [](data_cell_index const&) { return std::make_unique<FoldResult>(); };
65+
} else {
66+
return [args](data_cell_index const&) {
67+
return std::make_unique<FoldResult>(std::get<Is>(args)...);
68+
};
69+
}
70+
}
71+
5572
// =====================================================================================
5673

5774
template <typename AlgorithmBits, typename InitTuple>
58-
class fold_node : public declared_fold, private count_stores {
75+
class fold_node : public declared_fold {
5976
using all_parameter_types = typename AlgorithmBits::input_parameter_types;
77+
using result_type = std::decay_t<std::tuple_element_t<0, all_parameter_types>>;
6078
using input_parameter_types = skip_first_type<all_parameter_types>; // Skip fold object
6179
static constexpr auto num_inputs = std::tuple_size_v<input_parameter_types>;
62-
using result_type = std::decay_t<std::tuple_element_t<0, all_parameter_types>>;
6380

6481
static constexpr std::size_t num_outputs = 1; // hard-coded for now
6582
using function_t = typename AlgorithmBits::bound_type;
@@ -73,136 +90,69 @@ namespace phlex::experimental {
7390
InitTuple initializer,
7491
product_queries input_products,
7592
std::vector<std::string> output,
76-
std::string partition) :
77-
declared_fold{std::move(algo_name), std::move(predicates), std::move(input_products)},
78-
initializer_{std::move(initializer)},
93+
std::string partition_layer) :
94+
declared_fold{std::move(algo_name),
95+
std::move(predicates),
96+
std::move(input_products),
97+
std::move(partition_layer)},
7998
output_{to_product_specifications(name(), std::move(output), make_type_ids<result_type>())},
80-
partition_{std::move(partition)},
81-
flush_receiver_{g,
82-
tbb::flow::unlimited,
83-
[this](flush_message const& msg) -> tbb::flow::continue_msg {
84-
auto const& [index, counts, original_message_id] = msg;
85-
if (index->layer_name() != partition_) {
86-
return {};
87-
}
88-
89-
counter_for(index->hash()).set_flush_value(counts, original_message_id);
90-
emit_and_evict_if_done(index);
91-
return {};
92-
}},
93-
join_{make_join_or_none<num_inputs>(
94-
g, name().full(), layers())}, // FIXME: This should change to include result product!
99+
join_{g,
100+
name().full(),
101+
this->partition_layer(),
102+
layers(),
103+
this->output(),
104+
make_initializer<result_type>(
105+
std::move(initializer), std::make_index_sequence<std::tuple_size_v<InitTuple>>{})},
95106
fold_{g,
96107
concurrency,
97-
[this, ft = alg.release_algorithm()](messages_t<num_inputs> const& messages, auto&) {
98-
// N.B. The assumption is that a fold will *never* need to cache
99-
// the product store it creates. Any flush messages *do not* need
100-
// to be propagated to downstream nodes.
101-
auto const& msg = most_derived(messages);
102-
auto const& index = msg.store->index();
103-
104-
auto fold_index = index->parent(partition_);
105-
if (not fold_index) {
106-
return;
107-
}
108-
109-
auto index_hash_for_counter = fold_index->hash();
108+
[this, ft = alg.release_algorithm()](
109+
accumulator_with_messages<result_type, num_inputs> const& accum_with_msgs, auto&) {
110+
std::size_t const partition_hash = apply_fold(ft, accum_with_msgs);
110111

111-
call(ft, messages, std::make_index_sequence<num_inputs>{});
112112
++calls_;
113113

114-
counter_for(index_hash_for_counter).increment(index->layer_hash());
115-
116-
emit_and_evict_if_done(fold_index);
114+
join_.notify_result_repeater_port().try_put(partition_hash);
117115
}}
118116
{
119-
if constexpr (num_inputs > 1ull) {
120-
make_edge(join_, fold_);
121-
}
117+
make_edge(join_, fold_);
122118
}
123119

124120
private:
125-
void emit_and_evict_if_done(data_cell_index_ptr const& fold_index)
126-
{
127-
if (auto counter = done_with(fold_index->hash())) {
128-
auto parent = std::make_shared<product_store>(fold_index, name());
129-
commit(parent);
130-
++product_count_;
131-
tbb::flow::output_port<0>(fold_).try_put(
132-
{.store = parent, .id = counter->original_message_id()});
133-
}
134-
}
135-
136121
tbb::flow::receiver<message>& port_for(product_query const& input_product) override
137122
{
138-
return receiver_for<num_inputs>(join_, input(), input_product, fold_);
123+
return receiver_for(join_, input(), input_product);
139124
}
140125

141-
std::vector<tbb::flow::receiver<message>*> ports() override
142-
{
143-
return input_ports<num_inputs>(join_, fold_);
144-
}
126+
std::vector<tbb::flow::receiver<message>*> ports() override { return input_ports(join_); }
145127

146-
tbb::flow::receiver<flush_message>& flush_port() override { return flush_receiver_; }
147-
tbb::flow::sender<message>& output_port() override { return tbb::flow::output_port<0>(fold_); }
128+
tbb::flow::receiver<index_message>& partition_port() override { return join_.partition_port(); }
129+
tbb::flow::sender<message>& output_port() override { return join_.output_port(); }
148130
product_specifications const& output() const override { return output_; }
149131

150-
template <std::size_t... Is>
151-
void call(function_t const& ft,
152-
messages_t<num_inputs> const& messages,
153-
std::index_sequence<Is...>)
154-
{
155-
auto const parent_index = most_derived(messages).store->index()->parent(partition_);
156-
157-
// FIXME: Not the safest approach!
158-
auto it = results_.find(parent_index->hash());
159-
if (it == results_.end()) {
160-
it = results_
161-
.insert({parent_index->hash(),
162-
initialized_object(
163-
initializer_, std::make_index_sequence<std::tuple_size_v<InitTuple>>{})})
164-
.first;
165-
}
166-
167-
if constexpr (num_inputs == 1ull) {
168-
std::invoke(ft, *it->second, std::get<Is>(input_).retrieve(messages)...);
169-
} else {
170-
std::invoke(ft, *it->second, std::get<Is>(input_).retrieve(std::get<Is>(messages))...);
171-
}
172-
}
173-
174132
named_index_ports index_ports() final { return join_.index_ports(); }
175133
std::size_t num_calls() const final { return calls_.load(); }
176134
std::size_t product_count() const final { return product_count_.load(); }
177135

178-
template <size_t... Is>
179-
auto initialized_object(InitTuple const& init, std::index_sequence<Is...>) const
180-
{
181-
return std::unique_ptr<result_type>(new result_type{std::get<Is>(init)...});
182-
}
183-
184-
auto commit(product_store_ptr& store)
136+
std::size_t apply_fold(
137+
function_t const& ft,
138+
accumulator_with_messages<result_type, num_inputs> const& accum_with_msgs)
185139
{
186-
auto& result = results_.at(store->index()->hash());
187-
if constexpr (requires { send(*result); }) {
188-
store->add_product(output()[0], send(*result));
189-
} else {
190-
store->add_product(output()[0], std::move(result));
191-
}
192-
// Reclaim some memory; it would be better to erase the entire entry from the map,
193-
// but that is not thread-safe.
194-
result.reset();
140+
// We have to do awkward index management until we can use structured bindings with packs.
141+
auto& accumulator = std::get<0>(accum_with_msgs);
142+
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
143+
accumulator.partial_result->call(
144+
ft, std::get<Is>(input_).retrieve(std::get<Is + 1>(accum_with_msgs))...);
145+
}(std::make_index_sequence<num_inputs>{});
146+
return accumulator.index->hash();
195147
}
196148

197149
InitTuple initializer_;
198150
input_retriever_types<input_parameter_types> input_{input_arguments<input_parameter_types>()};
199151
product_specifications output_;
200-
identifier partition_;
201-
tbb::flow::function_node<flush_message> flush_receiver_;
202-
join_or_none_t<num_inputs> join_;
203-
tbb::flow::multifunction_node<messages_t<num_inputs>, message_tuple<1>> fold_;
204-
tbb::concurrent_unordered_map<data_cell_index::hash_type, std::unique_ptr<result_type>>
205-
results_;
152+
fold_join_node<result_type, num_inputs> join_;
153+
tbb::flow::multifunction_node<accumulator_with_messages<result_type, num_inputs>,
154+
message_tuple<1>>
155+
fold_;
206156
std::atomic<std::size_t> calls_;
207157
std::atomic<std::size_t> product_count_;
208158
};

phlex/core/declared_observer.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include "phlex/core/multilayer_join_node.hpp"
1111
#include "phlex/core/product_query.hpp"
1212
#include "phlex/core/products_consumer.hpp"
13-
#include "phlex/core/store_counters.hpp"
1413
#include "phlex/metaprogramming/type_deduction.hpp"
1514
#include "phlex/model/algorithm_name.hpp"
1615
#include "phlex/model/data_cell_index.hpp"

phlex/core/declared_unfold.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace phlex::experimental {
2020
product_store_const_ptr generator::make_child(std::size_t const i, products new_products)
2121
{
2222
auto child_index = parent_->index()->make_child(child_layer_name_, i);
23-
++child_counts_;
23+
++child_count_;
2424
return std::make_shared<product_store>(child_index, node_name_, std::move(new_products));
2525
}
2626

phlex/core/declared_unfold.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ namespace phlex::experimental {
4242
std::string const& child_layer_name);
4343

4444
std::size_t child_layer_hash() const { return child_layer_hash_; }
45-
std::size_t child_count() const { return child_counts_; }
45+
std::size_t child_count() const { return child_count_; }
4646
product_store_const_ptr make_child(std::size_t i, products new_products);
4747

4848
private:
@@ -52,7 +52,7 @@ namespace phlex::experimental {
5252
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
5353
std::string const& child_layer_name_;
5454
std::size_t child_layer_hash_;
55-
std::size_t child_counts_ = 0;
55+
std::size_t child_count_ = 0;
5656
};
5757

5858
class PHLEX_CORE_EXPORT declared_unfold : public products_consumer {

phlex/core/detail/accumulator_node.hpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace phlex::experimental::detail {
2828
}
2929

3030
template <typename FT, typename... Args>
31-
void call(FT const& f, Args&&... args)
31+
void call(FT const& f, Args const&... args)
3232
{
3333
std::invoke(f, *accumulator_, args...);
3434
}
@@ -51,7 +51,7 @@ namespace phlex::experimental::detail {
5151
struct accumulator_message {
5252
phlex::data_cell_index_ptr index;
5353
std::shared_ptr<accumulator<T>> partial_result;
54-
std::size_t id;
54+
std::size_t id{};
5555

5656
accumulator_message propagate_with_id(std::size_t new_id) const
5757
{
@@ -93,14 +93,19 @@ namespace phlex::experimental::detail {
9393
product_specifications output,
9494
result_initializer_t initializer);
9595

96+
accumulator_node(accumulator_node const&) = delete;
97+
accumulator_node(accumulator_node&&) = delete;
98+
accumulator_node& operator=(accumulator_node const&) = delete;
99+
accumulator_node& operator=(accumulator_node&&) = delete;
100+
96101
tbb::flow::receiver<index_message>& partition_port();
97102
tbb::flow::receiver<indexed_end_token>& flush_port();
98103
tbb::flow::receiver<index_message>& index_port();
99104

100105
bool cache_is_empty() const;
101106
std::size_t cache_size() const;
102107

103-
~accumulator_node();
108+
~accumulator_node() override;
104109

105110
private:
106111
using accumulator_msg_t = accumulator_message<Result>;

0 commit comments

Comments
 (0)