Skip to content

Commit fe2e1f7

Browse files
committed
Show biased and unbiased benchmarks.
1 parent e7de78b commit fe2e1f7

30 files changed

+452
-40
lines changed

code/CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,10 @@ add_test(lambda_tuple lambda_tuple)
3636
add_executable(concepts concepts.cpp)
3737
add_test(concepts concepts)
3838

39+
add_executable(expression_templates expression_templates.cpp)
40+
add_test(expression_templates expression_templates)
41+
42+
add_executable(loop_unrolling loop_unrolling.cpp)
43+
add_test(loop_unrolling loop_unrolling)
44+
3945
add_subdirectory(benchmark)

code/benchmark/CMakeLists.txt

+39-2
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ endfunction()
147147
##############################################################################
148148

149149
foreach(operation IN ITEMS apply get make_tuple tuple_cat tuple_transform)
150+
# Biased benchmarks
150151
boost_hana_add_curve_from_source(benchmark.${operation} lambda_tuple ${operation}.cpp
151152
"
152153
((0..50).to_a + (51..500).step(25).to_a).map { |n|
@@ -156,7 +157,8 @@ foreach(operation IN ITEMS apply get make_tuple tuple_cat tuple_transform)
156157
namespace other = standard;
157158
\",
158159
n_elements: n,
159-
x: n
160+
x: n,
161+
no_bias: false
160162
}
161163
}
162164
"
@@ -171,7 +173,42 @@ foreach(operation IN ITEMS apply get make_tuple tuple_cat tuple_transform)
171173
namespace other = lambda;
172174
\",
173175
n_elements: n,
174-
x: n
176+
x: n,
177+
no_bias: false
178+
}
179+
}
180+
"
181+
)
182+
183+
184+
# Unbiased benchmarks
185+
boost_hana_add_curve_from_source(benchmark.${operation}.unbiased lambda_tuple_unbiased ${operation}.cpp
186+
"
187+
((0..50).to_a + (51..500).step(25).to_a).map { |n|
188+
{
189+
setup: \"
190+
namespace benchmark = lambda;
191+
namespace other = standard;
192+
\",
193+
n_elements: n,
194+
x: n,
195+
no_bias: true
196+
}
197+
}
198+
"
199+
)
200+
201+
boost_hana_add_curve_from_source(benchmark.${operation}.unbiased std_tuple_unbiased ${operation}.cpp
202+
"
203+
((0..50).to_a + (51..500).step(25).to_a).map { |n|
204+
{
205+
setup: \"
206+
namespace benchmark = standard;
207+
namespace other = lambda;
208+
\",
209+
n_elements: n,
210+
x: n,
211+
no_bias: true
175212
}
176213
}
177214
"

code/benchmark/apply.cpp

+9-4
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,20 @@ template <int i>
99
struct x { };
1010

1111
int main() {
12-
other::make_tuple(
13-
<%= (1..n_elements).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
14-
);
12+
<% if no_bias %>
13+
other::make_tuple(
14+
<%= (1..n_elements).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
15+
);
16+
<% end %>
1517

1618
auto xs = benchmark::make_tuple(
1719
<%= (1..n_elements).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
1820
);
1921

2022
<% 10.times do %>
21-
benchmark::apply([](auto ...x) { }, xs);
23+
{
24+
auto f = [](auto ...x) { };
25+
benchmark::apply(f, xs);
26+
}
2227
<% end %>
2328
}

code/benchmark/get.cpp

+6-6
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ template <int i>
99
struct x { };
1010

1111
int main() {
12-
other::make_tuple(
13-
<%= (1..n_elements+1).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
14-
);
12+
<% if no_bias %>
13+
other::make_tuple(
14+
<%= (1..n_elements+1).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
15+
);
16+
<% end %>
1517

1618
auto xs = benchmark::make_tuple(
1719
// Make sure we use a non-empty tuple.
1820
<%= (1..n_elements+1).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
1921
);
2022

21-
<% (0..n_elements).each do |i| %>
22-
benchmark::get< <%=i%> >(xs);
23-
<% end %>
23+
benchmark::get< <%=n_elements/2%> >(xs);
2424
}
-880 Bytes
Loading
-704 Bytes
Loading
Loading
Loading
-1.29 KB
Loading
-299 Bytes
Loading
Loading
Loading
Loading
112 Bytes
Loading
Loading
Loading
Loading
-292 Bytes
Loading
Loading
Loading
Loading
Loading
Loading
Loading

code/benchmark/tuple_cat.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ template <int i>
1212
struct y { };
1313

1414
int main() {
15-
other::make_tuple(
16-
<%= (1..n_elements).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
17-
);
18-
19-
other::make_tuple(
20-
<%= (1..n_elements).to_a.map{ |i| "y<#{i}>{}" }.join(',') %>
21-
);
15+
<% if no_bias %>
16+
other::make_tuple(
17+
<%= (1..n_elements).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
18+
);
19+
20+
other::make_tuple(
21+
<%= (1..n_elements).to_a.map{ |i| "y<#{i}>{}" }.join(',') %>
22+
);
23+
<% end %>
2224

2325

2426
auto xs = benchmark::make_tuple(

code/benchmark/tuple_transform.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ template <int i>
99
struct x { };
1010

1111
int main() {
12-
other::make_tuple(
13-
<%= (1..n_elements).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
14-
);
12+
<% if no_bias %>
13+
other::make_tuple(
14+
<%= (1..n_elements).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>
15+
);
16+
<% end %>
1517

1618
auto xs = benchmark::make_tuple(
1719
<%= (1..n_elements).to_a.map{ |i| "x<#{i}>{}" }.join(',') %>

code/expression_templates.cpp

+222
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// Copyright Louis Dionne 2014
2+
// Distributed under the Boost Software License, Version 1.0.
3+
4+
#include <boost/hana/bool.hpp>
5+
#include <boost/hana/core/datatype.hpp>
6+
#include <boost/hana/core/is_a.hpp>
7+
#include <boost/hana/foldable/folds_mcd.hpp>
8+
#include <boost/hana/functional/fix.hpp>
9+
#include <boost/hana/functional/id.hpp>
10+
#include <boost/hana/functional/placeholder.hpp>
11+
#include <boost/hana/functor/fmap_mcd.hpp>
12+
#include <boost/hana/tuple.hpp>
13+
14+
#include <type_traits>
15+
#include <utility>
16+
17+
18+
//////////////////////////////////////////////////////////////////////////////
19+
// Expression
20+
//////////////////////////////////////////////////////////////////////////////
21+
struct Expression { };
22+
23+
template <typename Op, typename Args>
24+
struct function_node {
25+
Op op;
26+
Args args;
27+
using hana_datatype = Expression;
28+
static constexpr bool is_function = true;
29+
};
30+
31+
template <typename T>
32+
struct terminal_node {
33+
T value;
34+
using hana_datatype = Expression;
35+
static constexpr bool is_terminal = true;
36+
};
37+
38+
template <typename T>
39+
using strip_t = std::remove_cv_t<std::remove_reference_t<T>>;
40+
41+
template <typename T, typename = void>
42+
constexpr auto is_terminal = boost::hana::false_;
43+
44+
template <typename T>
45+
constexpr auto is_terminal<T, decltype((void)strip_t<T>::is_terminal)> = boost::hana::true_;
46+
47+
template <typename F, typename = void>
48+
constexpr auto is_function = boost::hana::false_;
49+
50+
template <typename F>
51+
constexpr auto is_function<F, decltype((void)strip_t<F>::is_function)> = boost::hana::true_;
52+
53+
template <typename T>
54+
constexpr auto is_Expression = boost::hana::bool_<
55+
std::is_same<Expression, boost::hana::datatype_t<T>>::value
56+
>;
57+
58+
59+
auto terminal = [](auto&& x) -> decltype(auto) {
60+
auto make = [](auto&& y) -> decltype(auto) {
61+
return terminal_node<std::decay_t<decltype(y)>>{
62+
std::forward<decltype(y)>(y)
63+
};
64+
};
65+
return boost::hana::if_(is_Expression<decltype(x)>,
66+
boost::hana::id,
67+
make
68+
)(std::forward<decltype(x)>(x));
69+
};
70+
71+
// `f` is a function object called on the evaluated `args...` when the
72+
// expression itself is evaluated.
73+
//
74+
// `args...` are 0 or more objects representing the arguments to `f`. `args...`
75+
// are passed through `terminal`; so `Expression`s can also be used, in which
76+
// case they are left as-is.
77+
//
78+
// note:
79+
// `function(f)` may only be called a single time, because we're
80+
// moving `f` out.
81+
auto function = [](auto&& f) -> decltype(auto) {
82+
return [f(std::forward<decltype(f)>(f))](auto&& ...args) -> decltype(auto) {
83+
auto a = boost::hana::tuple(terminal(std::forward<decltype(args)>(args))...);
84+
return function_node<std::decay_t<decltype(f)>, decltype(a)>{
85+
std::move(f), std::move(a)
86+
};
87+
};
88+
};
89+
90+
template <typename T, typename = std::enable_if_t<is_terminal<T>()>>
91+
constexpr decltype(auto) eval_impl(T&& t)
92+
{ return std::forward<T>(t).value; }
93+
94+
auto eval = [](auto&& expr) -> decltype(auto) {
95+
return eval_impl(std::forward<decltype(expr)>(expr));
96+
};
97+
98+
template <typename F, typename = void, typename = std::enable_if_t<is_function<F>()>>
99+
constexpr decltype(auto) eval_impl(F&& f) {
100+
return boost::hana::unpack(
101+
boost::hana::fmap(std::forward<F>(f).args, eval),
102+
std::forward<F>(f).op
103+
);
104+
}
105+
106+
107+
namespace boost { namespace hana {
108+
template <>
109+
struct Functor::instance<Expression> : Functor::fmap_mcd {
110+
template <typename Op, typename Args, typename F>
111+
static constexpr decltype(auto) fmap_impl(function_node<Op, Args> e, F f) {
112+
auto g = [=](auto&& arg) -> decltype(auto) {
113+
return fmap(std::forward<decltype(arg)>(arg), f);
114+
};
115+
return unpack(fmap(e.args, g), function(e.op));
116+
}
117+
118+
template <typename T, typename F>
119+
static constexpr decltype(auto) fmap_impl(terminal_node<T> e, F&& f) {
120+
return terminal(std::forward<F>(f)(e.value));
121+
}
122+
};
123+
124+
template <>
125+
struct Foldable::instance<Expression> : Foldable::folds_mcd {
126+
template <typename Op, typename Args, typename S, typename F>
127+
static constexpr decltype(auto) foldl_impl(function_node<Op, Args> e, S&& s, F f) {
128+
return foldl(e.args, std::forward<S>(s),
129+
[=](auto&& state, auto&& arg) -> decltype(auto) {
130+
return foldl(
131+
std::forward<decltype(arg)>(arg),
132+
std::forward<decltype(state)>(state),
133+
f
134+
);
135+
});
136+
}
137+
138+
template <typename T, typename S, typename F>
139+
static constexpr decltype(auto) foldl_impl(terminal_node<T> e, S&& s, F&& f) {
140+
return std::forward<F>(f)(std::forward<S>(s), e.value);
141+
}
142+
143+
144+
template <typename Op, typename Args, typename S, typename F>
145+
static constexpr decltype(auto) foldr_impl(function_node<Op, Args> e, S&& s, F f) {
146+
return foldr(e.args, std::forward<S>(s),
147+
[=](auto&& arg, auto&& state) -> decltype(auto) {
148+
return foldr(
149+
std::forward<decltype(arg)>(arg),
150+
std::forward<decltype(state)>(state),
151+
f
152+
);
153+
});
154+
}
155+
156+
template <typename T, typename S, typename F>
157+
static constexpr decltype(auto) foldr_impl(terminal_node<T> e, S&& s, F&& f) {
158+
return std::forward<F>(f)(e.value, std::forward<S>(s));
159+
}
160+
};
161+
}}
162+
163+
#define BINARY_NODE(OP) \
164+
template <typename E1, typename E2, typename = std::enable_if_t< \
165+
is_Expression<E1>() || is_Expression<E2>() \
166+
>> \
167+
decltype(auto) operator OP(E1&& e1, E2&& e2) { \
168+
auto f = boost::hana::_ OP boost::hana::_; \
169+
return function(f)(std::forward<E1>(e1), std::forward<E2>(e2)); \
170+
} \
171+
/**/
172+
173+
BINARY_NODE(+)
174+
BINARY_NODE(-)
175+
BINARY_NODE(*)
176+
BINARY_NODE(/)
177+
178+
179+
//////////////////////////////////////////////////////////////////////////////
180+
// Tests
181+
//////////////////////////////////////////////////////////////////////////////
182+
183+
#include <boost/hana/detail/assert.hpp>
184+
#include <boost/hana/integral.hpp>
185+
186+
#include <sstream>
187+
#include <string>
188+
using namespace boost::hana;
189+
190+
191+
int main() {
192+
// eval
193+
BOOST_HANA_RUNTIME_ASSERT(eval(function(_ + _)(1, 2)) == 1 + 2);
194+
BOOST_HANA_RUNTIME_ASSERT(eval(terminal(1) + 2) == 1 + 2);
195+
BOOST_HANA_RUNTIME_ASSERT(eval(terminal(1) + 2 + 3) == 1 + 2 + 3);
196+
BOOST_HANA_RUNTIME_ASSERT(eval(terminal(1) + 2 + 3 - 4) == 1 + 2 + 3 - 4);
197+
BOOST_HANA_CONSTANT_ASSERT(
198+
eval(terminal(int_<1>) + terminal(int_<2>) + terminal(int_<3>))
199+
==
200+
int_<1 + 2 + 3>
201+
);
202+
203+
// Functor
204+
auto to_s = [](auto x) { return (std::ostringstream{} << x).str(); };
205+
auto inc = [](auto x) { return x + 1; };
206+
BOOST_HANA_RUNTIME_ASSERT(eval(fmap(terminal(1) + 2 + 3, inc)) == 2 + 3 + 4);
207+
BOOST_HANA_RUNTIME_ASSERT(eval(fmap(terminal(1) + 2 + 3, to_s)) == "123");
208+
209+
210+
// Foldable
211+
BOOST_HANA_RUNTIME_ASSERT(
212+
foldl(terminal("1") + "2" + "3", std::string{}, _+_)
213+
==
214+
"123"
215+
);
216+
217+
BOOST_HANA_RUNTIME_ASSERT(
218+
foldr(terminal("1") + "2" + "3", std::string{}, _+_)
219+
==
220+
"123"
221+
);
222+
}

0 commit comments

Comments
 (0)