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