]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/test/bench/parser/bench_parser.cpp
3b4a83271f3b3e6968c493483adb8da0369606bf
[ceph.git] / ceph / src / boost / libs / beast / test / bench / parser / bench_parser.cpp
1 //
2 // Copyright (c) 2016-2017 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 "nodejs_parser.hpp"
11
12 #include "test/beast/http/message_fuzz.hpp"
13
14 #include <boost/beast/http.hpp>
15 #include <boost/beast/core/buffers_suffix.hpp>
16 #include <boost/beast/core/ostream.hpp>
17 #include <boost/beast/core/flat_buffer.hpp>
18 #include <boost/beast/core/multi_buffer.hpp>
19 #include <boost/beast/unit_test/suite.hpp>
20 #include <chrono>
21 #include <iostream>
22 #include <vector>
23
24 namespace boost {
25 namespace beast {
26 namespace http {
27
28 class parser_test : public beast::unit_test::suite
29 {
30 public:
31 static std::size_t constexpr N = 2000;
32
33 //using corpus = std::vector<multi_buffer>;
34 using corpus = std::vector<flat_buffer>;
35
36 corpus creq_;
37 corpus cres_;
38 std::size_t size_ = 0;
39
40 template<class ConstBufferSequence>
41 static
42 std::string
43 to_string(ConstBufferSequence const& bs)
44 {
45 std::string s;
46 s.reserve(buffer_size(bs));
47 for(auto b : beast::detail::buffers_range(bs))
48 s.append(reinterpret_cast<char const*>(b.data()),
49 b.size());
50 return s;
51 }
52
53 corpus
54 build_corpus(std::size_t n, std::true_type)
55 {
56 corpus v;
57 v.resize(n);
58 message_fuzz mg;
59 for(std::size_t i = 0; i < n; ++i)
60 {
61 mg.request(v[i]);
62 size_ += v[i].size();
63 BEAST_EXPECT(v[i].size() > 0);
64 }
65 return v;
66 }
67
68 corpus
69 build_corpus(std::size_t n, std::false_type)
70 {
71 corpus v;
72 v.resize(n);
73 message_fuzz mg;
74 for(std::size_t i = 0; i < n; ++i)
75 {
76 mg.response(v[i]);
77 size_ += v[i].size();
78 BEAST_EXPECT(v[i].size() > 0);
79 }
80 return v;
81 }
82
83 template<class ConstBufferSequence,
84 bool isRequest, class Derived>
85 static
86 std::size_t
87 feed(ConstBufferSequence const& buffers,
88 basic_parser<isRequest, Derived>& parser,
89 error_code& ec)
90 {
91 using boost::asio::buffer_size;
92 beast::buffers_suffix<
93 ConstBufferSequence> cb{buffers};
94 std::size_t used = 0;
95 for(;;)
96 {
97 auto const n =
98 parser.put(cb, ec);
99 if(ec)
100 return 0;
101 if(n == 0)
102 break;
103 cb.consume(n);
104 used += n;
105 if(parser.is_done())
106 break;
107 if(buffer_size(cb) == 0)
108 break;
109 }
110 return used;
111 }
112
113 template<class Parser>
114 void
115 testParser1(std::size_t repeat, corpus const& v)
116 {
117 while(repeat--)
118 for(auto const& b : v)
119 {
120 Parser p;
121 error_code ec;
122 p.write(b.data(), ec);
123 if(! BEAST_EXPECTS(! ec, ec.message()))
124 log << to_string(b.data()) << std::endl;
125 }
126 }
127
128 template<class Parser>
129 void
130 testParser2(std::size_t repeat, corpus const& v)
131 {
132 while(repeat--)
133 for(auto const& b : v)
134 {
135 Parser p;
136 p.header_limit((std::numeric_limits<std::uint32_t>::max)());
137 error_code ec;
138 feed(b.data(), p, ec);
139 if(! BEAST_EXPECTS(! ec, ec.message()))
140 log << to_string(b.data()) << std::endl;
141 }
142 }
143
144 template<class Function>
145 void
146 timedTest(std::size_t repeat, std::string const& name, Function&& f)
147 {
148 using namespace std::chrono;
149 using clock_type = std::chrono::high_resolution_clock;
150 log << name << std::endl;
151 for(std::size_t trial = 1; trial <= repeat; ++trial)
152 {
153 auto const t0 = clock_type::now();
154 f();
155 auto const elapsed = clock_type::now() - t0;
156 log <<
157 "Trial " << trial << ": " <<
158 duration_cast<milliseconds>(elapsed).count() << " ms" << std::endl;
159 }
160 }
161
162 template<bool isRequest>
163 struct null_parser :
164 basic_parser<isRequest, null_parser<isRequest>>
165 {
166 };
167
168 template<bool isRequest, class Body, class Fields>
169 struct bench_parser : basic_parser<
170 isRequest, bench_parser<isRequest, Body, Fields>>
171 {
172 using mutable_buffers_type =
173 boost::asio::mutable_buffer;
174
175 void
176 on_request_impl(verb, string_view,
177 string_view, int, error_code& ec)
178 {
179 ec.assign(0, ec.category());
180 }
181
182 void
183 on_response_impl(int,
184 string_view,
185 int, error_code& ec)
186 {
187 ec.assign(0, ec.category());
188 }
189
190 void
191 on_field_impl(field,
192 string_view, string_view, error_code& ec)
193 {
194 ec.assign(0, ec.category());
195 }
196
197 void
198 on_header_impl(error_code& ec)
199 {
200 ec.assign(0, ec.category());
201 }
202
203 void
204 on_body_init_impl(
205 boost::optional<std::uint64_t> const&,
206 error_code& ec)
207 {
208 ec.assign(0, ec.category());
209 }
210
211 std::size_t
212 on_body_impl(string_view s, error_code& ec)
213 {
214 ec.assign(0, ec.category());
215 return s.size();
216 }
217
218 void
219 on_chunk_header_impl(std::uint64_t,
220 string_view, error_code& ec)
221 {
222 ec.assign(0, ec.category());
223 }
224
225 std::size_t
226 on_chunk_body_impl(std::uint64_t,
227 string_view s, error_code& ec)
228 {
229 ec.assign(0, ec.category());
230 return s.size();
231 }
232
233 void
234 on_finish_impl(error_code& ec)
235 {
236 ec.assign(0, ec.category());
237 }
238 };
239
240 void
241 testSpeed()
242 {
243 static std::size_t constexpr Trials = 5;
244 static std::size_t constexpr Repeat = 500;
245
246 creq_ = build_corpus(N/2, std::true_type{});
247 cres_ = build_corpus(N/2, std::false_type{});
248
249 log << "sizeof(request parser) == " <<
250 sizeof(null_parser<true>) << '\n';
251
252 log << "sizeof(response parser) == " <<
253 sizeof(null_parser<false>)<< '\n';
254
255 testcase << "Parser speed test, " <<
256 ((Repeat * size_ + 512) / 1024) << "KB in " <<
257 (Repeat * (creq_.size() + cres_.size())) << " messages";
258
259 #if 0
260 timedTest(Trials, "http::parser",
261 [&]
262 {
263 testParser2<request_parser<dynamic_body>>(Repeat, creq_);
264 testParser2<response_parser<dynamic_body>>(Repeat, cres_);
265 });
266 #endif
267 #if 1
268 timedTest(Trials, "http::basic_parser",
269 [&]
270 {
271 testParser2<bench_parser<
272 true, dynamic_body, fields> >(
273 Repeat, creq_);
274 testParser2<bench_parser<
275 false, dynamic_body, fields>>(
276 Repeat, cres_);
277 });
278 #if 1
279 timedTest(Trials, "nodejs_parser",
280 [&]
281 {
282 testParser1<nodejs_parser<
283 true, dynamic_body, fields>>(
284 Repeat, creq_);
285 testParser1<nodejs_parser<
286 false, dynamic_body, fields>>(
287 Repeat, cres_);
288 });
289 #endif
290 #endif
291 pass();
292 }
293
294 void run() override
295 {
296 pass();
297 testSpeed();
298 }
299 };
300
301 BEAST_DEFINE_TESTSUITE(beast,benchmarks,parser);
302
303 } // http
304 } // beast
305 } // boost
306