Skip to content

Commit f5aa4e5

Browse files
committed
enable more aggressive inlining of flows
1 parent 0c464e5 commit f5aa4e5

File tree

4 files changed

+129
-83
lines changed

4 files changed

+129
-83
lines changed

include/flow/graph_builder.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,14 @@ struct graph_builder {
149149
constexpr auto built = build(v);
150150
static_assert(built.has_value(),
151151
"Topological sort failed: cycle in flow");
152-
return *built;
152+
153+
constexpr auto functionPtrs = built->functionPtrs;
154+
constexpr auto size = functionPtrs.size();
155+
constexpr auto name = built->name;
156+
157+
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
158+
return detail::inlined_func_list<name, functionPtrs[Is]...>{};
159+
}(std::make_index_sequence<size>{});
153160
}
154161

155162
constexpr static auto run() { built()(); }

include/flow/impl.hpp

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313
#include <type_traits>
1414

1515
namespace flow {
16-
// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor)
17-
struct interface {
18-
virtual auto operator()() const -> void {}
19-
};
20-
2116
/**
2217
* flow::impl is a constant representation of a series of Milestones and actions
2318
* to be executed in a specific order.
@@ -35,8 +30,7 @@ struct interface {
3530
*
3631
* @see flow::builder
3732
*/
38-
template <stdx::ct_string Name, std::size_t NumSteps>
39-
class impl : public interface {
33+
template <stdx::ct_string Name, std::size_t NumSteps> class impl {
4034
private:
4135
constexpr static bool loggingEnabled = not Name.empty();
4236

@@ -48,12 +42,14 @@ class impl : public interface {
4842
}
4943
}();
5044

45+
public:
5146
stdx::cx_vector<FunctionPtr, capacity> functionPtrs{};
5247

53-
public:
5448
using node_t = rt_node;
5549
constexpr static bool active = capacity > 0;
5650

51+
constexpr static auto name = Name;
52+
5753
/**
5854
* Create a new flow::impl of Milestones.
5955
*
@@ -77,24 +73,29 @@ class impl : public interface {
7773
[](auto const &milestone) { return milestone.run; });
7874
}
7975
}
76+
};
77+
78+
namespace detail {
79+
template <stdx::ct_string Name, auto... FuncPtrs> struct inlined_func_list {
80+
constexpr static auto active = sizeof...(FuncPtrs) > 0;
81+
82+
__attribute__((flatten, always_inline)) auto operator()() const -> void {
83+
constexpr static bool loggingEnabled = not Name.empty();
8084

81-
/**
82-
* Execute the entire flow in order.
83-
*/
84-
__attribute__((flatten)) auto operator()() const -> void final {
8585
constexpr auto name =
8686
stdx::ct_string_to_type<Name, sc::string_constant>();
87+
8788
if constexpr (loggingEnabled) {
8889
CIB_TRACE("flow.start({})", name);
8990
}
9091

91-
[this]<std::size_t... Is>(std::index_sequence<Is...>) {
92-
(functionPtrs[Is](), ...);
93-
}(std::make_index_sequence<capacity>{});
92+
(FuncPtrs(), ...);
9493

9594
if constexpr (loggingEnabled) {
9695
CIB_TRACE("flow.end({})", name);
9796
}
9897
}
9998
};
99+
} // namespace detail
100+
100101
} // namespace flow

include/flow/step.hpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,20 @@ template <stdx::ct_string Name> struct ct_node : rt_node {
2727
decltype(stdx::ct_string_to_type<Name, sc::string_constant>());
2828
};
2929

30+
template <stdx::ct_string Name, stdx::ct_string Type>
31+
static void log_name_func() {
32+
CIB_TRACE("flow.{}({})",
33+
stdx::ct_string_to_type<Type, sc::string_constant>(),
34+
stdx::ct_string_to_type<Name, sc::string_constant>());
35+
}
36+
3037
namespace detail {
3138
template <stdx::ct_string Name, stdx::ct_string Type, typename F>
3239
[[nodiscard]] constexpr auto make_node() {
33-
return ct_node<Name>{
34-
{.run = F{}, .log_name = [] {
35-
CIB_TRACE("flow.{}({})",
36-
stdx::ct_string_to_type<Type, sc::string_constant>(),
37-
stdx::ct_string_to_type<Name, sc::string_constant>());
38-
}}};
40+
return ct_node<Name>{{.run = F{}, .log_name = log_name_func<Name, Type>}};
3941
}
42+
43+
constexpr auto empty_func = []() {};
4044
} // namespace detail
4145

4246
template <stdx::ct_string Name, typename F>
@@ -54,7 +58,7 @@ template <stdx::ct_string Name> [[nodiscard]] constexpr auto step() {
5458
}
5559

5660
template <stdx::ct_string Name> [[nodiscard]] constexpr auto milestone() {
57-
return detail::make_node<Name, "milestone", decltype([] {})>();
61+
return detail::make_node<Name, "milestone", decltype(detail::empty_func)>();
5862
}
5963

6064
inline namespace literals {

test/flow/graph_builder.cpp

Lines changed: 94 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -19,106 +19,127 @@ constexpr auto d = flow::action<"d">([] { actual += "d"; });
1919
using builder = flow::graph_builder<flow::impl>;
2020
} // namespace
2121

22+
struct empty_flow {
23+
constexpr static auto value = flow::graph<>{};
24+
};
2225
TEST_CASE("build and run empty flow", "[graph_builder]") {
23-
auto g = flow::graph<>{};
24-
auto const flow = builder::build(g);
25-
REQUIRE(flow.has_value());
26-
flow.value()();
26+
constexpr auto f = builder::render<empty_flow>();
27+
f();
2728
}
2829

30+
struct single_action {
31+
constexpr static auto value = flow::graph<>{}.add(a);
32+
};
2933
TEST_CASE("add single action", "[graph_builder]") {
3034
actual.clear();
31-
auto g = flow::graph<>{}.add(a);
32-
auto const flow = builder::build(g);
33-
REQUIRE(flow.has_value());
34-
flow.value()();
35+
constexpr auto f = builder::render<single_action>();
36+
f();
3537
CHECK(actual == "a");
3638
}
3739

40+
struct two_milestone_linear_before {
41+
constexpr static auto value = flow::graph<>{}.add(a >> milestone0);
42+
};
3843
TEST_CASE("two milestone linear before dependency", "[graph_builder]") {
3944
actual.clear();
40-
auto g = flow::graph<>{}.add(a >> milestone0);
41-
auto const flow = builder::build(g);
42-
REQUIRE(flow.has_value());
43-
flow.value()();
45+
constexpr auto f = builder::render<two_milestone_linear_before>();
46+
f();
4447
CHECK(actual == "a");
4548
}
4649

50+
struct actions_get_executed_once {
51+
constexpr static auto value = flow::graph<>{}
52+
.add(a >> milestone0)
53+
.add(a >> milestone1)
54+
.add(milestone0 >> milestone1);
55+
};
4756
TEST_CASE("actions get executed once", "[graph_builder]") {
4857
actual.clear();
49-
auto g = flow::graph<>{}
50-
.add(a >> milestone0)
51-
.add(a >> milestone1)
52-
.add(milestone0 >> milestone1);
53-
auto const flow = builder::build(g);
54-
flow.value()();
58+
constexpr auto f = builder::render<actions_get_executed_once>();
59+
f();
5560
CHECK(actual == "a");
5661
}
5762

63+
struct two_milestone_linear_after_dependency {
64+
constexpr static auto value = flow::graph<>{}
65+
.add(a >> milestone0)
66+
.add(milestone0 >> milestone1)
67+
.add(milestone0 >> b >> milestone1);
68+
};
5869
TEST_CASE("two milestone linear after dependency", "[graph_builder]") {
5970
actual.clear();
60-
auto g = flow::graph<>{}
61-
.add(a >> milestone0)
62-
.add(milestone0 >> milestone1)
63-
.add(milestone0 >> b >> milestone1);
64-
auto const flow = builder::build(g);
65-
flow.value()();
71+
constexpr auto f = builder::render<two_milestone_linear_after_dependency>();
72+
f();
6673
CHECK(actual == "ab");
6774
}
6875

76+
struct three_milestone_linear_before_and_after_dependency {
77+
constexpr static auto value = flow::graph<>{}.add(a >> b >> c);
78+
};
6979
TEST_CASE("three milestone linear before and after dependency",
7080
"[graph_builder]") {
7181
actual.clear();
72-
auto g = flow::graph<>{}.add(a >> b >> c);
73-
auto const flow = builder::build(g);
74-
flow.value()();
82+
constexpr auto f =
83+
builder::render<three_milestone_linear_before_and_after_dependency>();
84+
f();
7585
CHECK(actual == "abc");
7686
}
7787

88+
struct just_two_actions_in_order {
89+
constexpr static auto value = flow::graph<>{}.add(a >> b);
90+
};
7891
TEST_CASE("just two actions in order", "[graph_builder]") {
7992
actual.clear();
80-
auto g = flow::graph<>{}.add(a >> b);
81-
auto const flow = builder::build(g);
82-
flow.value()();
93+
constexpr auto f = builder::render<just_two_actions_in_order>();
94+
f();
8395
CHECK(actual == "ab");
8496
}
8597

98+
struct insert_action_between_two_actions {
99+
constexpr static auto value = flow::graph<>{}.add(a >> c).add(a >> b >> c);
100+
};
86101
TEST_CASE("insert action between two actions", "[graph_builder]") {
87102
actual.clear();
88-
auto g = flow::graph<>{}.add(a >> c).add(a >> b >> c);
89-
auto const flow = builder::build(g);
90-
flow.value()();
103+
constexpr auto f = builder::render<insert_action_between_two_actions>();
104+
f();
91105
CHECK(actual == "abc");
92106
}
93107

108+
struct add_single_parallel_2 {
109+
constexpr static auto value = flow::graph<>{}.add(a && b);
110+
};
94111
TEST_CASE("add single parallel 2", "[graph_builder]") {
95112
actual.clear();
96-
auto g = flow::graph<>{}.add(a && b);
97-
auto const flow = builder::build(g);
98-
flow.value()();
113+
constexpr auto f = builder::render<add_single_parallel_2>();
114+
f();
99115

100116
CHECK(actual.find('a') != std::string::npos);
101117
CHECK(actual.find('b') != std::string::npos);
102118
CHECK(actual.size() == 2);
103119
}
104120

121+
struct add_single_parallel_3 {
122+
constexpr static auto value = flow::graph<>{}.add(a && b && c);
123+
};
105124
TEST_CASE("add single parallel 3", "[graph_builder]") {
106125
actual.clear();
107-
auto g = flow::graph<>{}.add(a && b && c);
108-
auto const flow = builder::build(g);
109-
flow.value()();
126+
constexpr auto f = builder::render<add_single_parallel_3>();
127+
f();
110128

111129
CHECK(actual.find('a') != std::string::npos);
112130
CHECK(actual.find('b') != std::string::npos);
113131
CHECK(actual.find('c') != std::string::npos);
114132
CHECK(actual.size() == 3);
115133
}
116134

135+
struct add_single_parallel_3_with_later_dependency_1 {
136+
constexpr static auto value = flow::graph<>{}.add(a && b && c).add(c >> a);
137+
};
117138
TEST_CASE("add single parallel 3 with later dependency 1", "[graph_builder]") {
118139
actual.clear();
119-
auto g = flow::graph<>{}.add(a && b && c).add(c >> a);
120-
auto const flow = builder::build(g);
121-
flow.value()();
140+
constexpr auto f =
141+
builder::render<add_single_parallel_3_with_later_dependency_1>();
142+
f();
122143

123144
CHECK(actual.find('a') != std::string::npos);
124145
CHECK(actual.find('b') != std::string::npos);
@@ -127,11 +148,14 @@ TEST_CASE("add single parallel 3 with later dependency 1", "[graph_builder]") {
127148
CHECK(actual.size() == 3);
128149
}
129150

151+
struct add_single_parallel_3_with_later_dependency_2 {
152+
constexpr static auto value = flow::graph<>{}.add(a && b && c).add(a >> c);
153+
};
130154
TEST_CASE("add single parallel 3 with later dependency 2", "[graph_builder]") {
131155
actual.clear();
132-
auto g = flow::graph<>{}.add(a && b && c).add(a >> c);
133-
auto const flow = builder::build(g);
134-
flow.value()();
156+
constexpr auto f =
157+
builder::render<add_single_parallel_3_with_later_dependency_2>();
158+
f();
135159

136160
CHECK(actual.find('a') != std::string::npos);
137161
CHECK(actual.find('b') != std::string::npos);
@@ -140,11 +164,13 @@ TEST_CASE("add single parallel 3 with later dependency 2", "[graph_builder]") {
140164
CHECK(actual.size() == 3);
141165
}
142166

167+
struct add_parallel_rhs {
168+
constexpr static auto value = flow::graph<>{}.add(a >> (b && c));
169+
};
143170
TEST_CASE("add parallel rhs", "[graph_builder]") {
144171
actual.clear();
145-
auto g = flow::graph<>{}.add(a >> (b && c));
146-
auto const flow = builder::build(g);
147-
flow.value()();
172+
constexpr auto f = builder::render<add_parallel_rhs>();
173+
f();
148174

149175
CHECK(actual.find('a') != std::string::npos);
150176
CHECK(actual.find('b') != std::string::npos);
@@ -154,11 +180,13 @@ TEST_CASE("add parallel rhs", "[graph_builder]") {
154180
CHECK(actual.size() == 3);
155181
}
156182

183+
struct add_parallel_lhs {
184+
constexpr static auto value = flow::graph<>{}.add((a && b) >> c);
185+
};
157186
TEST_CASE("add parallel lhs", "[graph_builder]") {
158187
actual.clear();
159-
auto g = flow::graph<>{}.add((a && b) >> c);
160-
auto const flow = builder::build(g);
161-
flow.value()();
188+
constexpr auto f = builder::render<add_parallel_lhs>();
189+
f();
162190

163191
CHECK(actual.find('a') != std::string::npos);
164192
CHECK(actual.find('b') != std::string::npos);
@@ -168,11 +196,13 @@ TEST_CASE("add parallel lhs", "[graph_builder]") {
168196
CHECK(actual.size() == 3);
169197
}
170198

199+
struct add_parallel_in_the_middle {
200+
constexpr static auto value = flow::graph<>{}.add(a >> (b && c) >> d);
201+
};
171202
TEST_CASE("add parallel in the middle", "[graph_builder]") {
172203
actual.clear();
173-
auto g = flow::graph<>{}.add(a >> (b && c) >> d);
174-
auto const flow = builder::build(g);
175-
flow.value()();
204+
constexpr auto f = builder::render<add_parallel_in_the_middle>();
205+
f();
176206

177207
CHECK(actual.find('a') != std::string::npos);
178208
CHECK(actual.find('b') != std::string::npos);
@@ -188,11 +218,13 @@ TEST_CASE("add parallel in the middle", "[graph_builder]") {
188218
CHECK(actual.size() == 4);
189219
}
190220

221+
struct add_dependency_lhs {
222+
constexpr static auto value = flow::graph<>{}.add((a >> b) && c);
223+
};
191224
TEST_CASE("add dependency lhs", "[graph_builder]") {
192225
actual.clear();
193-
auto g = flow::graph<>{}.add((a >> b) && c);
194-
auto const flow = builder::build(g);
195-
flow.value()();
226+
constexpr auto f = builder::render<add_dependency_lhs>();
227+
f();
196228

197229
CHECK(actual.find('a') != std::string::npos);
198230
CHECK(actual.find('b') != std::string::npos);
@@ -203,11 +235,13 @@ TEST_CASE("add dependency lhs", "[graph_builder]") {
203235
CHECK(actual.size() == 3);
204236
}
205237

238+
struct add_dependency_rhs {
239+
constexpr static auto value = flow::graph<>{}.add(a && (b >> c));
240+
};
206241
TEST_CASE("add dependency rhs", "[graph_builder]") {
207242
actual.clear();
208-
auto g = flow::graph<>{}.add(a && (b >> c));
209-
auto const flow = builder::build(g);
210-
flow.value()();
243+
constexpr auto f = builder::render<add_dependency_rhs>();
244+
f();
211245

212246
CHECK(actual.find('a') != std::string::npos);
213247
CHECK(actual.find('b') != std::string::npos);

0 commit comments

Comments
 (0)