• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9 
10 #include <boost/beast/core/buffers_range.hpp>
11 #include <boost/beast/core/flat_buffer.hpp>
12 #include <boost/beast/core/multi_buffer.hpp>
13 #include <boost/beast/core/read_size.hpp>
14 #include <boost/beast/core/string.hpp>
15 #include <boost/beast/_experimental/unit_test/suite.hpp>
16 #include <boost/asio/streambuf.hpp>
17 #include <algorithm>
18 #include <chrono>
19 #include <cstdlib>
20 #include <iomanip>
21 #include <utility>
22 #include <vector>
23 
24 namespace boost {
25 namespace beast {
26 
27 class buffers_test : public beast::unit_test::suite
28 {
29 public:
30     using size_type = std::uint64_t;
31 
32     class timer
33     {
34         using clock_type =
35             std::chrono::system_clock;
36 
37         clock_type::time_point when_;
38 
39     public:
40         using duration =
41             clock_type::duration;
42 
timer()43         timer()
44             : when_(clock_type::now())
45         {
46         }
47 
48         duration
elapsed() const49         elapsed() const
50         {
51             return clock_type::now() - when_;
52         }
53     };
54 
55     inline
56     size_type
throughput(std::chrono::duration<double> const & elapsed,size_type items)57     throughput(std::chrono::duration<
58         double> const& elapsed, size_type items)
59     {
60         using namespace std::chrono;
61         return static_cast<size_type>(
62             1 / (elapsed/items).count());
63     }
64 
65     template<class MutableBufferSequence>
66     static
67     std::size_t
fill(MutableBufferSequence const & buffers)68     fill(MutableBufferSequence const& buffers)
69     {
70         std::size_t n = 0;
71         for(auto b : beast::buffers_range_ref(buffers))
72         {
73             std::fill(
74                 static_cast<char*>(b.data()),
75                 static_cast<char*>(b.data()) +
76                     b.size(), '\0');
77             n += b.size();
78         }
79         return n;
80     }
81 
82     template<class DynamicBuffer>
83     size_type
do_prepares(std::size_t repeat,std::size_t count,std::size_t size)84     do_prepares(std::size_t repeat,
85         std::size_t count, std::size_t size)
86     {
87         timer t;
88         size_type total = 0;
89         for(auto i = repeat; i--;)
90         {
91             DynamicBuffer b;
92             for(auto j = count; j--;)
93             {
94                 auto const n = fill(b.prepare(size));
95                 b.commit(n);
96                 total += n;
97             }
98         }
99         return throughput(t.elapsed(), total);
100     }
101 
102     template<class DynamicBuffer>
103     size_type
do_hints(std::size_t repeat,std::size_t count,std::size_t size)104     do_hints(std::size_t repeat,
105         std::size_t count, std::size_t size)
106     {
107         timer t;
108         size_type total = 0;
109         for(auto i = repeat; i--;)
110         {
111             DynamicBuffer b;
112             for(auto j = count; j--;)
113             {
114                 for(auto remain = size; remain;)
115                 {
116                     auto const n = fill(b.prepare(
117                         read_size(b, remain)));
118                     b.commit(n);
119                     remain -= n;
120                     total += n;
121                 }
122             }
123         }
124         return throughput(t.elapsed(), total);
125     }
126 
127     template<class DynamicBuffer>
128     size_type
do_random(std::size_t repeat,std::size_t count,std::size_t size)129     do_random(std::size_t repeat,
130         std::size_t count, std::size_t size)
131     {
132         timer t;
133         size_type total = 0;
134         for(auto i = repeat; i--;)
135         {
136             DynamicBuffer b;
137             for(auto j = count; j--;)
138             {
139                 auto const n = fill(b.prepare(
140                     1 + (rand()%(2*size))));
141                 b.commit(n);
142                 total += n;
143             }
144         }
145         return throughput(t.elapsed(), total);
146     }
147 
148     static
149     inline
150     void
do_trials_1(bool)151     do_trials_1(bool)
152     {
153     }
154 
155     template<class F0, class... FN>
156     void
do_trials_1(bool print,F0 && f,FN...fn)157     do_trials_1(bool print, F0&& f, FN... fn)
158     {
159         timer t;
160         using namespace std::chrono;
161         static size_type constexpr den = 1024 * 1024;
162         if(print)
163         {
164             log << std::right << std::setw(10) <<
165                 ((f() + (den / 2)) / den) << " MB/s";
166             log.flush();
167         }
168         else
169         {
170             f();
171         }
172         do_trials_1(print, fn...);
173     }
174 
175     template<class F0, class... FN>
176     void
do_trials(string_view name,std::size_t trials,F0 && f0,FN...fn)177     do_trials(string_view name,
178         std::size_t trials, F0&& f0, FN... fn)
179     {
180         using namespace std::chrono;
181         // warm-up
182         do_trials_1(false, f0, fn...);
183         do_trials_1(false, f0, fn...);
184         while(trials--)
185         {
186             timer t;
187             log << std::left << std::setw(24) << name << ":";
188             log.flush();
189             do_trials_1(true, f0, fn...);
190             log << "   " <<
191                 duration_cast<milliseconds>(t.elapsed()).count() << "ms";
192             log << std::endl;
193         }
194     }
195 
196     void
run()197     run() override
198     {
199         static std::size_t constexpr trials = 1;
200         static std::size_t constexpr repeat = 250;
201         std::vector<std::pair<std::size_t, std::size_t>> params;
202         params.emplace_back(1024, 1024);
203         params.emplace_back(512, 4096);
204         params.emplace_back(256, 32768);
205         log << std::endl;
206         for(auto const& param : params)
207         {
208             auto const count = param.first;
209             auto const size = param.second;
210             auto const s = std::string("count=") + std::to_string(count) +
211                 ", size=" + std::to_string(size);
212             log << std::left << std::setw(24) << s << " " <<
213                 std::right << std::setw(15) << "prepare" <<
214                 std::right << std::setw(15) << "with hint" <<
215                 std::right << std::setw(15) << "random" <<
216                 std::endl;
217             do_trials("multi_buffer", trials,
218                  [&](){ return do_prepares<multi_buffer>(repeat, count, size); }
219                 ,[&](){ return do_hints   <multi_buffer>(repeat, count, size); }
220                 ,[&](){ return do_random  <multi_buffer>(repeat, count, size); }
221             );
222             do_trials("flat_buffer", trials,
223                  [&](){ return do_prepares<flat_buffer>(repeat, count, size); }
224                 ,[&](){ return do_hints   <flat_buffer>(repeat, count, size); }
225                 ,[&](){ return do_random  <flat_buffer>(repeat, count, size); }
226             );
227             do_trials("net::streambuf", trials,
228                  [&](){ return do_prepares<net::streambuf>(repeat, count, size); }
229                 ,[&](){ return do_hints   <net::streambuf>(repeat, count, size); }
230                 ,[&](){ return do_random  <net::streambuf>(repeat, count, size); }
231             );
232             log << std::endl;
233         }
234         pass();
235     }
236 };
237 
238 BEAST_DEFINE_TESTSUITE(beast,benchmarks,buffers);
239 
240 } // beast
241 } // boost
242