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