1 /*
2 *
3 * Copyright 2015 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 /* Microbenchmarks around CHTTP2 HPACK operations */
20
21 #include <benchmark/benchmark.h>
22 #include <grpc/support/alloc.h>
23 #include <grpc/support/log.h>
24 #include <string.h>
25 #include <memory>
26 #include <sstream>
27
28 #include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
29 #include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
30 #include "src/core/ext/transport/chttp2/transport/incoming_metadata.h"
31 #include "src/core/lib/slice/slice_internal.h"
32 #include "src/core/lib/slice/slice_string_helpers.h"
33 #include "src/core/lib/transport/static_metadata.h"
34 #include "src/core/lib/transport/timeout_encoding.h"
35
36 #include "test/cpp/microbenchmarks/helpers.h"
37 #include "test/cpp/util/test_config.h"
38
39 auto& force_library_initialization = Library::get();
40
MakeSlice(std::vector<uint8_t> bytes)41 static grpc_slice MakeSlice(std::vector<uint8_t> bytes) {
42 grpc_slice s = grpc_slice_malloc(bytes.size());
43 uint8_t* p = GRPC_SLICE_START_PTR(s);
44 for (auto b : bytes) {
45 *p++ = b;
46 }
47 return s;
48 }
49
50 ////////////////////////////////////////////////////////////////////////////////
51 // HPACK encoder
52 //
53
BM_HpackEncoderInitDestroy(benchmark::State & state)54 static void BM_HpackEncoderInitDestroy(benchmark::State& state) {
55 TrackCounters track_counters;
56 grpc_core::ExecCtx exec_ctx;
57 std::unique_ptr<grpc_chttp2_hpack_compressor> c(
58 new grpc_chttp2_hpack_compressor);
59 while (state.KeepRunning()) {
60 grpc_chttp2_hpack_compressor_init(c.get());
61 grpc_chttp2_hpack_compressor_destroy(c.get());
62 grpc_core::ExecCtx::Get()->Flush();
63 }
64
65 track_counters.Finish(state);
66 }
67 BENCHMARK(BM_HpackEncoderInitDestroy);
68
BM_HpackEncoderEncodeDeadline(benchmark::State & state)69 static void BM_HpackEncoderEncodeDeadline(benchmark::State& state) {
70 TrackCounters track_counters;
71 grpc_core::ExecCtx exec_ctx;
72 grpc_millis saved_now = grpc_core::ExecCtx::Get()->Now();
73
74 grpc_metadata_batch b;
75 grpc_metadata_batch_init(&b);
76 b.deadline = saved_now + 30 * 1000;
77
78 std::unique_ptr<grpc_chttp2_hpack_compressor> c(
79 new grpc_chttp2_hpack_compressor);
80 grpc_chttp2_hpack_compressor_init(c.get());
81 grpc_transport_one_way_stats stats;
82 memset(&stats, 0, sizeof(stats));
83 grpc_slice_buffer outbuf;
84 grpc_slice_buffer_init(&outbuf);
85 while (state.KeepRunning()) {
86 grpc_encode_header_options hopt = {
87 static_cast<uint32_t>(state.iterations()),
88 true,
89 false,
90 static_cast<size_t>(1024),
91 &stats,
92 };
93 grpc_chttp2_encode_header(c.get(), nullptr, 0, &b, &hopt, &outbuf);
94 grpc_slice_buffer_reset_and_unref_internal(&outbuf);
95 grpc_core::ExecCtx::Get()->Flush();
96 }
97 grpc_metadata_batch_destroy(&b);
98 grpc_chttp2_hpack_compressor_destroy(c.get());
99 grpc_slice_buffer_destroy_internal(&outbuf);
100
101 std::ostringstream label;
102 label << "framing_bytes/iter:"
103 << (static_cast<double>(stats.framing_bytes) /
104 static_cast<double>(state.iterations()))
105 << " header_bytes/iter:"
106 << (static_cast<double>(stats.header_bytes) /
107 static_cast<double>(state.iterations()));
108 track_counters.AddLabel(label.str());
109 track_counters.Finish(state);
110 }
111 BENCHMARK(BM_HpackEncoderEncodeDeadline);
112
113 template <class Fixture>
BM_HpackEncoderEncodeHeader(benchmark::State & state)114 static void BM_HpackEncoderEncodeHeader(benchmark::State& state) {
115 TrackCounters track_counters;
116 grpc_core::ExecCtx exec_ctx;
117 static bool logged_representative_output = false;
118
119 grpc_metadata_batch b;
120 grpc_metadata_batch_init(&b);
121 std::vector<grpc_mdelem> elems = Fixture::GetElems();
122 std::vector<grpc_linked_mdelem> storage(elems.size());
123 for (size_t i = 0; i < elems.size(); i++) {
124 GPR_ASSERT(GRPC_LOG_IF_ERROR(
125 "addmd", grpc_metadata_batch_add_tail(&b, &storage[i], elems[i])));
126 }
127
128 std::unique_ptr<grpc_chttp2_hpack_compressor> c(
129 new grpc_chttp2_hpack_compressor);
130 grpc_chttp2_hpack_compressor_init(c.get());
131 grpc_transport_one_way_stats stats;
132 memset(&stats, 0, sizeof(stats));
133 grpc_slice_buffer outbuf;
134 grpc_slice_buffer_init(&outbuf);
135 while (state.KeepRunning()) {
136 grpc_encode_header_options hopt = {
137 static_cast<uint32_t>(state.iterations()),
138 state.range(0) != 0,
139 Fixture::kEnableTrueBinary,
140 static_cast<size_t>(state.range(1)),
141 &stats,
142 };
143 grpc_chttp2_encode_header(c.get(), nullptr, 0, &b, &hopt, &outbuf);
144 if (!logged_representative_output && state.iterations() > 3) {
145 logged_representative_output = true;
146 for (size_t i = 0; i < outbuf.count; i++) {
147 char* s = grpc_dump_slice(outbuf.slices[i], GPR_DUMP_HEX);
148 gpr_log(GPR_DEBUG, "%" PRIdPTR ": %s", i, s);
149 gpr_free(s);
150 }
151 }
152 grpc_slice_buffer_reset_and_unref_internal(&outbuf);
153 grpc_core::ExecCtx::Get()->Flush();
154 }
155 grpc_metadata_batch_destroy(&b);
156 grpc_chttp2_hpack_compressor_destroy(c.get());
157 grpc_slice_buffer_destroy_internal(&outbuf);
158
159 std::ostringstream label;
160 label << "framing_bytes/iter:"
161 << (static_cast<double>(stats.framing_bytes) /
162 static_cast<double>(state.iterations()))
163 << " header_bytes/iter:"
164 << (static_cast<double>(stats.header_bytes) /
165 static_cast<double>(state.iterations()));
166 track_counters.AddLabel(label.str());
167 track_counters.Finish(state);
168 }
169
170 namespace hpack_encoder_fixtures {
171
172 class EmptyBatch {
173 public:
174 static constexpr bool kEnableTrueBinary = false;
GetElems()175 static std::vector<grpc_mdelem> GetElems() { return {}; }
176 };
177
178 class SingleStaticElem {
179 public:
180 static constexpr bool kEnableTrueBinary = false;
GetElems()181 static std::vector<grpc_mdelem> GetElems() {
182 return {GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE};
183 }
184 };
185
186 class SingleInternedElem {
187 public:
188 static constexpr bool kEnableTrueBinary = false;
GetElems()189 static std::vector<grpc_mdelem> GetElems() {
190 return {grpc_mdelem_from_slices(
191 grpc_slice_intern(grpc_slice_from_static_string("abc")),
192 grpc_slice_intern(grpc_slice_from_static_string("def")))};
193 }
194 };
195
196 template <int kLength, bool kTrueBinary>
197 class SingleInternedBinaryElem {
198 public:
199 static constexpr bool kEnableTrueBinary = kTrueBinary;
GetElems()200 static std::vector<grpc_mdelem> GetElems() {
201 grpc_slice bytes = MakeBytes();
202 std::vector<grpc_mdelem> out = {grpc_mdelem_from_slices(
203 grpc_slice_intern(grpc_slice_from_static_string("abc-bin")),
204 grpc_slice_intern(bytes))};
205 grpc_slice_unref(bytes);
206 return out;
207 }
208
209 private:
MakeBytes()210 static grpc_slice MakeBytes() {
211 std::vector<char> v;
212 v.reserve(kLength);
213 for (int i = 0; i < kLength; i++) {
214 v.push_back(static_cast<char>(rand()));
215 }
216 return grpc_slice_from_copied_buffer(v.data(), v.size());
217 }
218 };
219
220 class SingleInternedKeyElem {
221 public:
222 static constexpr bool kEnableTrueBinary = false;
GetElems()223 static std::vector<grpc_mdelem> GetElems() {
224 return {grpc_mdelem_from_slices(
225 grpc_slice_intern(grpc_slice_from_static_string("abc")),
226 grpc_slice_from_static_string("def"))};
227 }
228 };
229
230 class SingleNonInternedElem {
231 public:
232 static constexpr bool kEnableTrueBinary = false;
GetElems()233 static std::vector<grpc_mdelem> GetElems() {
234 return {grpc_mdelem_from_slices(grpc_slice_from_static_string("abc"),
235 grpc_slice_from_static_string("def"))};
236 }
237 };
238
239 template <int kLength, bool kTrueBinary>
240 class SingleNonInternedBinaryElem {
241 public:
242 static constexpr bool kEnableTrueBinary = kTrueBinary;
GetElems()243 static std::vector<grpc_mdelem> GetElems() {
244 return {grpc_mdelem_from_slices(grpc_slice_from_static_string("abc-bin"),
245 MakeBytes())};
246 }
247
248 private:
MakeBytes()249 static grpc_slice MakeBytes() {
250 std::vector<char> v;
251 v.reserve(kLength);
252 for (int i = 0; i < kLength; i++) {
253 v.push_back(static_cast<char>(rand()));
254 }
255 return grpc_slice_from_copied_buffer(v.data(), v.size());
256 }
257 };
258
259 class RepresentativeClientInitialMetadata {
260 public:
261 static constexpr bool kEnableTrueBinary = true;
GetElems()262 static std::vector<grpc_mdelem> GetElems() {
263 return {
264 GRPC_MDELEM_SCHEME_HTTP,
265 GRPC_MDELEM_METHOD_POST,
266 grpc_mdelem_from_slices(
267 GRPC_MDSTR_PATH,
268 grpc_slice_intern(grpc_slice_from_static_string("/foo/bar"))),
269 grpc_mdelem_from_slices(GRPC_MDSTR_AUTHORITY,
270 grpc_slice_intern(grpc_slice_from_static_string(
271 "foo.test.google.fr:1234"))),
272 GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP,
273 GRPC_MDELEM_TE_TRAILERS,
274 GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
275 grpc_mdelem_from_slices(
276 GRPC_MDSTR_USER_AGENT,
277 grpc_slice_intern(grpc_slice_from_static_string(
278 "grpc-c/3.0.0-dev (linux; chttp2; green)")))};
279 }
280 };
281
282 // This fixture reflects how initial metadata are sent by a production client,
283 // with non-indexed :path and binary headers. The metadata here are the same as
284 // the corresponding parser benchmark below.
285 class MoreRepresentativeClientInitialMetadata {
286 public:
287 static constexpr bool kEnableTrueBinary = true;
GetElems()288 static std::vector<grpc_mdelem> GetElems() {
289 return {
290 GRPC_MDELEM_SCHEME_HTTP,
291 GRPC_MDELEM_METHOD_POST,
292 grpc_mdelem_from_slices(GRPC_MDSTR_PATH,
293 grpc_slice_intern(grpc_slice_from_static_string(
294 "/grpc.test.FooService/BarMethod"))),
295 grpc_mdelem_from_slices(GRPC_MDSTR_AUTHORITY,
296 grpc_slice_intern(grpc_slice_from_static_string(
297 "foo.test.google.fr:1234"))),
298 grpc_mdelem_from_slices(
299 GRPC_MDSTR_GRPC_TRACE_BIN,
300 grpc_slice_from_static_string("\x00\x01\x02\x03\x04\x05\x06\x07\x08"
301 "\x09\x0a\x0b\x0c\x0d\x0e\x0f"
302 "\x10\x11\x12\x13\x14\x15\x16\x17\x18"
303 "\x19\x1a\x1b\x1c\x1d\x1e\x1f"
304 "\x20\x21\x22\x23\x24\x25\x26\x27\x28"
305 "\x29\x2a\x2b\x2c\x2d\x2e\x2f"
306 "\x30")),
307 grpc_mdelem_from_slices(
308 GRPC_MDSTR_GRPC_TAGS_BIN,
309 grpc_slice_from_static_string("\x00\x01\x02\x03\x04\x05\x06\x07\x08"
310 "\x09\x0a\x0b\x0c\x0d\x0e\x0f"
311 "\x10\x11\x12\x13")),
312 GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP,
313 GRPC_MDELEM_TE_TRAILERS,
314 GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
315 grpc_mdelem_from_slices(
316 GRPC_MDSTR_USER_AGENT,
317 grpc_slice_intern(grpc_slice_from_static_string(
318 "grpc-c/3.0.0-dev (linux; chttp2; green)")))};
319 }
320 };
321
322 class RepresentativeServerInitialMetadata {
323 public:
324 static constexpr bool kEnableTrueBinary = true;
GetElems()325 static std::vector<grpc_mdelem> GetElems() {
326 return {GRPC_MDELEM_STATUS_200,
327 GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
328 GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP};
329 }
330 };
331
332 class RepresentativeServerTrailingMetadata {
333 public:
334 static constexpr bool kEnableTrueBinary = true;
GetElems()335 static std::vector<grpc_mdelem> GetElems() {
336 return {GRPC_MDELEM_GRPC_STATUS_0};
337 }
338 };
339
340 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({0, 16384});
341 // test with eof (shouldn't affect anything)
342 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({1, 16384});
343 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleStaticElem)
344 ->Args({0, 16384});
345 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedKeyElem)
346 ->Args({0, 16384});
347 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedElem)
348 ->Args({0, 16384});
349 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
350 SingleInternedBinaryElem<1, false>)
351 ->Args({0, 16384});
352 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
353 SingleInternedBinaryElem<3, false>)
354 ->Args({0, 16384});
355 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
356 SingleInternedBinaryElem<10, false>)
357 ->Args({0, 16384});
358 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
359 SingleInternedBinaryElem<31, false>)
360 ->Args({0, 16384});
361 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
362 SingleInternedBinaryElem<100, false>)
363 ->Args({0, 16384});
364 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
365 SingleInternedBinaryElem<1, true>)
366 ->Args({0, 16384});
367 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
368 SingleInternedBinaryElem<3, true>)
369 ->Args({0, 16384});
370 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
371 SingleInternedBinaryElem<10, true>)
372 ->Args({0, 16384});
373 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
374 SingleInternedBinaryElem<31, true>)
375 ->Args({0, 16384});
376 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
377 SingleInternedBinaryElem<100, true>)
378 ->Args({0, 16384});
379 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem)
380 ->Args({0, 16384});
381 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
382 SingleNonInternedBinaryElem<1, false>)
383 ->Args({0, 16384});
384 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
385 SingleNonInternedBinaryElem<3, false>)
386 ->Args({0, 16384});
387 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
388 SingleNonInternedBinaryElem<10, false>)
389 ->Args({0, 16384});
390 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
391 SingleNonInternedBinaryElem<31, false>)
392 ->Args({0, 16384});
393 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
394 SingleNonInternedBinaryElem<100, false>)
395 ->Args({0, 16384});
396 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
397 SingleNonInternedBinaryElem<1, true>)
398 ->Args({0, 16384});
399 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
400 SingleNonInternedBinaryElem<3, true>)
401 ->Args({0, 16384});
402 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
403 SingleNonInternedBinaryElem<10, true>)
404 ->Args({0, 16384});
405 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
406 SingleNonInternedBinaryElem<31, true>)
407 ->Args({0, 16384});
408 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
409 SingleNonInternedBinaryElem<100, true>)
410 ->Args({0, 16384});
411 // test with a tiny frame size, to highlight continuation costs
412 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem)
413 ->Args({0, 1});
414
415 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
416 RepresentativeClientInitialMetadata)
417 ->Args({0, 16384});
418 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
419 MoreRepresentativeClientInitialMetadata)
420 ->Args({0, 16384});
421 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
422 RepresentativeServerInitialMetadata)
423 ->Args({0, 16384});
424 BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
425 RepresentativeServerTrailingMetadata)
426 ->Args({1, 16384});
427
428 } // namespace hpack_encoder_fixtures
429
430 ////////////////////////////////////////////////////////////////////////////////
431 // HPACK parser
432 //
433
BM_HpackParserInitDestroy(benchmark::State & state)434 static void BM_HpackParserInitDestroy(benchmark::State& state) {
435 TrackCounters track_counters;
436 grpc_core::ExecCtx exec_ctx;
437 grpc_chttp2_hpack_parser p;
438 while (state.KeepRunning()) {
439 grpc_chttp2_hpack_parser_init(&p);
440 grpc_chttp2_hpack_parser_destroy(&p);
441 grpc_core::ExecCtx::Get()->Flush();
442 }
443
444 track_counters.Finish(state);
445 }
446 BENCHMARK(BM_HpackParserInitDestroy);
447
UnrefHeader(void * user_data,grpc_mdelem md)448 static void UnrefHeader(void* user_data, grpc_mdelem md) {
449 GRPC_MDELEM_UNREF(md);
450 }
451
452 template <class Fixture, void (*OnHeader)(void*, grpc_mdelem)>
BM_HpackParserParseHeader(benchmark::State & state)453 static void BM_HpackParserParseHeader(benchmark::State& state) {
454 TrackCounters track_counters;
455 grpc_core::ExecCtx exec_ctx;
456 std::vector<grpc_slice> init_slices = Fixture::GetInitSlices();
457 std::vector<grpc_slice> benchmark_slices = Fixture::GetBenchmarkSlices();
458 grpc_chttp2_hpack_parser p;
459 grpc_chttp2_hpack_parser_init(&p);
460 const int kArenaSize = 4096 * 4096;
461 p.on_header_user_data = gpr_arena_create(kArenaSize);
462 p.on_header = OnHeader;
463 for (auto slice : init_slices) {
464 GPR_ASSERT(GRPC_ERROR_NONE == grpc_chttp2_hpack_parser_parse(&p, slice));
465 }
466 while (state.KeepRunning()) {
467 for (auto slice : benchmark_slices) {
468 GPR_ASSERT(GRPC_ERROR_NONE == grpc_chttp2_hpack_parser_parse(&p, slice));
469 }
470 grpc_core::ExecCtx::Get()->Flush();
471 // Recreate arena every 4k iterations to avoid oom
472 if (0 == (state.iterations() & 0xfff)) {
473 gpr_arena_destroy((gpr_arena*)p.on_header_user_data);
474 p.on_header_user_data = gpr_arena_create(kArenaSize);
475 }
476 }
477 // Clean up
478 gpr_arena_destroy((gpr_arena*)p.on_header_user_data);
479 for (auto slice : init_slices) grpc_slice_unref(slice);
480 for (auto slice : benchmark_slices) grpc_slice_unref(slice);
481 grpc_chttp2_hpack_parser_destroy(&p);
482
483 track_counters.Finish(state);
484 }
485
486 namespace hpack_parser_fixtures {
487
488 class EmptyBatch {
489 public:
GetInitSlices()490 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()491 static std::vector<grpc_slice> GetBenchmarkSlices() {
492 return {MakeSlice({})};
493 }
494 };
495
496 class IndexedSingleStaticElem {
497 public:
GetInitSlices()498 static std::vector<grpc_slice> GetInitSlices() {
499 return {MakeSlice(
500 {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})};
501 }
GetBenchmarkSlices()502 static std::vector<grpc_slice> GetBenchmarkSlices() {
503 return {MakeSlice({0xbe})};
504 }
505 };
506
507 class AddIndexedSingleStaticElem {
508 public:
GetInitSlices()509 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()510 static std::vector<grpc_slice> GetBenchmarkSlices() {
511 return {MakeSlice(
512 {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})};
513 }
514 };
515
516 class KeyIndexedSingleStaticElem {
517 public:
GetInitSlices()518 static std::vector<grpc_slice> GetInitSlices() {
519 return {MakeSlice(
520 {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})};
521 }
GetBenchmarkSlices()522 static std::vector<grpc_slice> GetBenchmarkSlices() {
523 return {MakeSlice({0x7e, 0x03, 'd', 'e', 'f'})};
524 }
525 };
526
527 class IndexedSingleInternedElem {
528 public:
GetInitSlices()529 static std::vector<grpc_slice> GetInitSlices() {
530 return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
531 }
GetBenchmarkSlices()532 static std::vector<grpc_slice> GetBenchmarkSlices() {
533 return {MakeSlice({0xbe})};
534 }
535 };
536
537 class AddIndexedSingleInternedElem {
538 public:
GetInitSlices()539 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()540 static std::vector<grpc_slice> GetBenchmarkSlices() {
541 return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
542 }
543 };
544
545 class KeyIndexedSingleInternedElem {
546 public:
GetInitSlices()547 static std::vector<grpc_slice> GetInitSlices() {
548 return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
549 }
GetBenchmarkSlices()550 static std::vector<grpc_slice> GetBenchmarkSlices() {
551 return {MakeSlice({0x7e, 0x03, 'g', 'h', 'i'})};
552 }
553 };
554
555 class NonIndexedElem {
556 public:
GetInitSlices()557 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()558 static std::vector<grpc_slice> GetBenchmarkSlices() {
559 return {MakeSlice({0x00, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
560 }
561 };
562
563 template <int kLength, bool kTrueBinary>
564 class NonIndexedBinaryElem;
565
566 template <int kLength>
567 class NonIndexedBinaryElem<kLength, true> {
568 public:
GetInitSlices()569 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()570 static std::vector<grpc_slice> GetBenchmarkSlices() {
571 std::vector<uint8_t> v = {
572 0x00, 0x07, 'a', 'b', 'c',
573 '-', 'b', 'i', 'n', static_cast<uint8_t>(kLength + 1),
574 0};
575 for (int i = 0; i < kLength; i++) {
576 v.push_back(static_cast<uint8_t>(i));
577 }
578 return {MakeSlice(v)};
579 }
580 };
581
582 template <>
583 class NonIndexedBinaryElem<1, false> {
584 public:
GetInitSlices()585 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()586 static std::vector<grpc_slice> GetBenchmarkSlices() {
587 return {MakeSlice(
588 {0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0x82, 0xf7, 0xb3})};
589 }
590 };
591
592 template <>
593 class NonIndexedBinaryElem<3, false> {
594 public:
GetInitSlices()595 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()596 static std::vector<grpc_slice> GetBenchmarkSlices() {
597 return {MakeSlice({0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0x84,
598 0x7f, 0x4e, 0x29, 0x3f})};
599 }
600 };
601
602 template <>
603 class NonIndexedBinaryElem<10, false> {
604 public:
GetInitSlices()605 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()606 static std::vector<grpc_slice> GetBenchmarkSlices() {
607 return {MakeSlice({0x00, 0x07, 'a', 'b', 'c', '-', 'b',
608 'i', 'n', 0x8b, 0x71, 0x0c, 0xa5, 0x81,
609 0x73, 0x7b, 0x47, 0x13, 0xe9, 0xf7, 0xe3})};
610 }
611 };
612
613 template <>
614 class NonIndexedBinaryElem<31, false> {
615 public:
GetInitSlices()616 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()617 static std::vector<grpc_slice> GetBenchmarkSlices() {
618 return {MakeSlice({0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n',
619 0xa3, 0x92, 0x43, 0x7f, 0xbe, 0x7c, 0xea, 0x6f, 0xf3,
620 0x3d, 0xa7, 0xa7, 0x67, 0xfb, 0xe2, 0x82, 0xf7, 0xf2,
621 0x8f, 0x1f, 0x9d, 0xdf, 0xf1, 0x7e, 0xb3, 0xef, 0xb2,
622 0x8f, 0x53, 0x77, 0xce, 0x0c, 0x13, 0xe3, 0xfd, 0x87})};
623 }
624 };
625
626 template <>
627 class NonIndexedBinaryElem<100, false> {
628 public:
GetInitSlices()629 static std::vector<grpc_slice> GetInitSlices() { return {}; }
GetBenchmarkSlices()630 static std::vector<grpc_slice> GetBenchmarkSlices() {
631 return {MakeSlice(
632 {0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0xeb, 0x1d, 0x4d,
633 0xe8, 0x96, 0x8c, 0x14, 0x20, 0x06, 0xc1, 0xc3, 0xdf, 0x6e, 0x1f, 0xef,
634 0xde, 0x2f, 0xde, 0xb7, 0xf2, 0xfe, 0x6d, 0xd4, 0xe4, 0x7d, 0xf5, 0x55,
635 0x46, 0x52, 0x3d, 0x91, 0xf2, 0xd4, 0x6f, 0xca, 0x34, 0xcd, 0xd9, 0x39,
636 0xbd, 0x03, 0x27, 0xe3, 0x9c, 0x74, 0xcc, 0x17, 0x34, 0xed, 0xa6, 0x6a,
637 0x77, 0x73, 0x10, 0xcd, 0x8e, 0x4e, 0x5c, 0x7c, 0x72, 0x39, 0xd8, 0xe6,
638 0x78, 0x6b, 0xdb, 0xa5, 0xb7, 0xab, 0xe7, 0x46, 0xae, 0x21, 0xab, 0x7f,
639 0x01, 0x89, 0x13, 0xd7, 0xca, 0x17, 0x6e, 0xcb, 0xd6, 0x79, 0x71, 0x68,
640 0xbf, 0x8a, 0x3f, 0x32, 0xe8, 0xba, 0xf5, 0xbe, 0xb3, 0xbc, 0xde, 0x28,
641 0xc7, 0xcf, 0x62, 0x7a, 0x58, 0x2c, 0xcf, 0x4d, 0xe3})};
642 }
643 };
644
645 class RepresentativeClientInitialMetadata {
646 public:
GetInitSlices()647 static std::vector<grpc_slice> GetInitSlices() {
648 return {grpc_slice_from_static_string(
649 // generated with:
650 // ```
651 // tools/codegen/core/gen_header_frame.py --compression inc --no_framing
652 // < test/core/bad_client/tests/simple_request.headers
653 // ```
654 "@\x05:path\x08/foo/bar"
655 "@\x07:scheme\x04http"
656 "@\x07:method\x04POST"
657 "@\x0a:authority\x09localhost"
658 "@\x0c"
659 "content-type\x10"
660 "application/grpc"
661 "@\x14grpc-accept-encoding\x15identity,deflate,gzip"
662 "@\x02te\x08trailers"
663 "@\x0auser-agent\"bad-client grpc-c/0.12.0.0 (linux)")};
664 }
GetBenchmarkSlices()665 static std::vector<grpc_slice> GetBenchmarkSlices() {
666 // generated with:
667 // ```
668 // tools/codegen/core/gen_header_frame.py --compression pre --no_framing
669 // --hex < test/core/bad_client/tests/simple_request.headers
670 // ```
671 return {MakeSlice({0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe})};
672 }
673 };
674
675 // This fixture reflects how initial metadata are sent by a production client,
676 // with non-indexed :path and binary headers. The metadata here are the same as
677 // the corresponding encoder benchmark above.
678 class MoreRepresentativeClientInitialMetadata {
679 public:
GetInitSlices()680 static std::vector<grpc_slice> GetInitSlices() {
681 return {MakeSlice(
682 {0x40, 0x07, ':', 's', 'c', 'h', 'e', 'm', 'e', 0x04, 'h', 't',
683 't', 'p', 0x40, 0x07, ':', 'm', 'e', 't', 'h', 'o', 'd', 0x04,
684 'P', 'O', 'S', 'T', 0x40, 0x05, ':', 'p', 'a', 't', 'h', 0x1f,
685 '/', 'g', 'r', 'p', 'c', '.', 't', 'e', 's', 't', '.', 'F',
686 'o', 'o', 'S', 'e', 'r', 'v', 'i', 'c', 'e', '/', 'B', 'a',
687 'r', 'M', 'e', 't', 'h', 'o', 'd', 0x40, 0x0a, ':', 'a', 'u',
688 't', 'h', 'o', 'r', 'i', 't', 'y', 0x09, 'l', 'o', 'c', 'a',
689 'l', 'h', 'o', 's', 't', 0x40, 0x0e, 'g', 'r', 'p', 'c', '-',
690 't', 'r', 'a', 'c', 'e', '-', 'b', 'i', 'n', 0x31, 0x00, 0x01,
691 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
692 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
693 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
694 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x40,
695 0x0d, 'g', 'r', 'p', 'c', '-', 't', 'a', 'g', 's', '-', 'b',
696 'i', 'n', 0x14, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
697 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x40,
698 0x0c, 'c', 'o', 'n', 't', 'e', 'n', 't', '-', 't', 'y', 'p',
699 'e', 0x10, 'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o',
700 'n', '/', 'g', 'r', 'p', 'c', 0x40, 0x14, 'g', 'r', 'p', 'c',
701 '-', 'a', 'c', 'c', 'e', 'p', 't', '-', 'e', 'n', 'c', 'o',
702 'd', 'i', 'n', 'g', 0x15, 'i', 'd', 'e', 'n', 't', 'i', 't',
703 'y', ',', 'd', 'e', 'f', 'l', 'a', 't', 'e', ',', 'g', 'z',
704 'i', 'p', 0x40, 0x02, 't', 'e', 0x08, 't', 'r', 'a', 'i', 'l',
705 'e', 'r', 's', 0x40, 0x0a, 'u', 's', 'e', 'r', '-', 'a', 'g',
706 'e', 'n', 't', 0x22, 'b', 'a', 'd', '-', 'c', 'l', 'i', 'e',
707 'n', 't', ' ', 'g', 'r', 'p', 'c', '-', 'c', '/', '0', '.',
708 '1', '2', '.', '0', '.', '0', ' ', '(', 'l', 'i', 'n', 'u',
709 'x', ')'})};
710 }
GetBenchmarkSlices()711 static std::vector<grpc_slice> GetBenchmarkSlices() {
712 return {MakeSlice(
713 {0xc7, 0xc6, 0xc5, 0xc4, 0x7f, 0x04, 0x31, 0x00, 0x01, 0x02, 0x03, 0x04,
714 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
715 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
716 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
717 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x7f, 0x03, 0x14, 0x00,
718 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
719 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0xc1, 0xc0, 0xbf, 0xbe})};
720 }
721 };
722
723 class RepresentativeServerInitialMetadata {
724 public:
GetInitSlices()725 static std::vector<grpc_slice> GetInitSlices() {
726 return {grpc_slice_from_static_string(
727 // generated with:
728 // ```
729 // tools/codegen/core/gen_header_frame.py --compression inc --no_framing
730 // <
731 // test/cpp/microbenchmarks/representative_server_initial_metadata.headers
732 // ```
733 "@\x07:status\x03"
734 "200"
735 "@\x0c"
736 "content-type\x10"
737 "application/grpc"
738 "@\x14grpc-accept-encoding\x15identity,deflate,gzip")};
739 }
GetBenchmarkSlices()740 static std::vector<grpc_slice> GetBenchmarkSlices() {
741 // generated with:
742 // ```
743 // tools/codegen/core/gen_header_frame.py --compression pre --no_framing
744 // --hex <
745 // test/cpp/microbenchmarks/representative_server_initial_metadata.headers
746 // ```
747 return {MakeSlice({0xc0, 0xbf, 0xbe})};
748 }
749 };
750
751 class RepresentativeServerTrailingMetadata {
752 public:
GetInitSlices()753 static std::vector<grpc_slice> GetInitSlices() {
754 return {grpc_slice_from_static_string(
755 // generated with:
756 // ```
757 // tools/codegen/core/gen_header_frame.py --compression inc --no_framing
758 // <
759 // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
760 // ```
761 "@\x0bgrpc-status\x01"
762 "0"
763 "@\x0cgrpc-message\x00")};
764 }
GetBenchmarkSlices()765 static std::vector<grpc_slice> GetBenchmarkSlices() {
766 // generated with:
767 // ```
768 // tools/codegen/core/gen_header_frame.py --compression pre --no_framing
769 // --hex <
770 // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
771 // ```
772 return {MakeSlice({0xbf, 0xbe})};
773 }
774 };
775
free_timeout(void * p)776 static void free_timeout(void* p) { gpr_free(p); }
777
778 // Benchmark the current on_initial_header implementation
OnInitialHeader(void * user_data,grpc_mdelem md)779 static void OnInitialHeader(void* user_data, grpc_mdelem md) {
780 // Setup for benchmark. This will bloat the absolute values of this benchmark
781 grpc_chttp2_incoming_metadata_buffer buffer;
782 grpc_chttp2_incoming_metadata_buffer_init(&buffer, (gpr_arena*)user_data);
783 bool seen_error = false;
784
785 // Below here is the code we actually care about benchmarking
786 if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) &&
787 !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) {
788 seen_error = true;
789 }
790 if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) {
791 grpc_millis* cached_timeout =
792 static_cast<grpc_millis*>(grpc_mdelem_get_user_data(md, free_timeout));
793 grpc_millis timeout;
794 if (cached_timeout != nullptr) {
795 timeout = *cached_timeout;
796 } else {
797 if (GPR_UNLIKELY(
798 !grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout))) {
799 char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
800 gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val);
801 gpr_free(val);
802 timeout = GRPC_MILLIS_INF_FUTURE;
803 }
804 if (GRPC_MDELEM_IS_INTERNED(md)) {
805 /* not already parsed: parse it now, and store the
806 * result away */
807 cached_timeout =
808 static_cast<grpc_millis*>(gpr_malloc(sizeof(grpc_millis)));
809 *cached_timeout = timeout;
810 grpc_mdelem_set_user_data(md, free_timeout, cached_timeout);
811 }
812 }
813 benchmark::DoNotOptimize(timeout);
814 GRPC_MDELEM_UNREF(md);
815 } else {
816 const size_t new_size = buffer.size + GRPC_MDELEM_LENGTH(md);
817 if (!seen_error) {
818 buffer.size = new_size;
819 }
820 grpc_error* error = grpc_chttp2_incoming_metadata_buffer_add(&buffer, md);
821 if (error != GRPC_ERROR_NONE) {
822 GPR_ASSERT(0);
823 }
824 }
825 grpc_chttp2_incoming_metadata_buffer_destroy(&buffer);
826 }
827
828 // Benchmark timeout handling
OnHeaderTimeout(void * user_data,grpc_mdelem md)829 static void OnHeaderTimeout(void* user_data, grpc_mdelem md) {
830 if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) {
831 grpc_millis* cached_timeout =
832 static_cast<grpc_millis*>(grpc_mdelem_get_user_data(md, free_timeout));
833 grpc_millis timeout;
834 if (cached_timeout != nullptr) {
835 timeout = *cached_timeout;
836 } else {
837 if (!grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout)) {
838 char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md));
839 gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val);
840 gpr_free(val);
841 timeout = GRPC_MILLIS_INF_FUTURE;
842 }
843 if (GRPC_MDELEM_IS_INTERNED(md)) {
844 /* not already parsed: parse it now, and store the
845 * result away */
846 cached_timeout =
847 static_cast<grpc_millis*>(gpr_malloc(sizeof(grpc_millis)));
848 *cached_timeout = timeout;
849 grpc_mdelem_set_user_data(md, free_timeout, cached_timeout);
850 }
851 }
852 benchmark::DoNotOptimize(timeout);
853 GRPC_MDELEM_UNREF(md);
854 } else {
855 GPR_ASSERT(0);
856 }
857 }
858
859 // Send the same deadline repeatedly
860 class SameDeadline {
861 public:
GetInitSlices()862 static std::vector<grpc_slice> GetInitSlices() {
863 return {
864 grpc_slice_from_static_string("@\x0cgrpc-timeout\x03"
865 "30S")};
866 }
GetBenchmarkSlices()867 static std::vector<grpc_slice> GetBenchmarkSlices() {
868 // Use saved key and literal value.
869 return {MakeSlice({0x0f, 0x2f, 0x03, '3', '0', 'S'})};
870 }
871 };
872
873 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, EmptyBatch, UnrefHeader);
874 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleStaticElem,
875 UnrefHeader);
876 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleStaticElem,
877 UnrefHeader);
878 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleStaticElem,
879 UnrefHeader);
880 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleInternedElem,
881 UnrefHeader);
882 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleInternedElem,
883 UnrefHeader);
884 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleInternedElem,
885 UnrefHeader);
886 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedElem, UnrefHeader);
887 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<1, false>,
888 UnrefHeader);
889 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<3, false>,
890 UnrefHeader);
891 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<10, false>,
892 UnrefHeader);
893 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<31, false>,
894 UnrefHeader);
895 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<100, false>,
896 UnrefHeader);
897 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<1, true>,
898 UnrefHeader);
899 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<3, true>,
900 UnrefHeader);
901 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<10, true>,
902 UnrefHeader);
903 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<31, true>,
904 UnrefHeader);
905 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem<100, true>,
906 UnrefHeader);
907 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
908 RepresentativeClientInitialMetadata, UnrefHeader);
909 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
910 MoreRepresentativeClientInitialMetadata, UnrefHeader);
911 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
912 RepresentativeServerInitialMetadata, UnrefHeader);
913 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
914 RepresentativeServerTrailingMetadata, UnrefHeader);
915 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
916 RepresentativeClientInitialMetadata, OnInitialHeader);
917 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
918 MoreRepresentativeClientInitialMetadata, OnInitialHeader);
919 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
920 RepresentativeServerInitialMetadata, OnInitialHeader);
921 BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, SameDeadline, OnHeaderTimeout);
922
923 } // namespace hpack_parser_fixtures
924
925 // Some distros have RunSpecifiedBenchmarks under the benchmark namespace,
926 // and others do not. This allows us to support both modes.
927 namespace benchmark {
RunTheBenchmarksNamespaced()928 void RunTheBenchmarksNamespaced() { RunSpecifiedBenchmarks(); }
929 } // namespace benchmark
930
main(int argc,char ** argv)931 int main(int argc, char** argv) {
932 ::benchmark::Initialize(&argc, argv);
933 ::grpc::testing::InitTest(&argc, &argv, false);
934 benchmark::RunTheBenchmarksNamespaced();
935 return 0;
936 }
937