/* * * Copyright 2015, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* Microbenchmarks around CHTTP2 HPACK operations */ #include #include #include #include extern "C" { #include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" #include "src/core/ext/transport/chttp2/transport/hpack_parser.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" #include "src/core/lib/transport/static_metadata.h" } #include "test/cpp/microbenchmarks/helpers.h" #include "third_party/benchmark/include/benchmark/benchmark.h" auto &force_library_initialization = Library::get(); //////////////////////////////////////////////////////////////////////////////// // HPACK encoder // static void BM_HpackEncoderInitDestroy(benchmark::State &state) { TrackCounters track_counters; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_chttp2_hpack_compressor c; while (state.KeepRunning()) { grpc_chttp2_hpack_compressor_init(&c); grpc_chttp2_hpack_compressor_destroy(&exec_ctx, &c); grpc_exec_ctx_flush(&exec_ctx); } grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); } BENCHMARK(BM_HpackEncoderInitDestroy); template static void BM_HpackEncoderEncodeHeader(benchmark::State &state) { TrackCounters track_counters; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; static bool logged_representative_output = false; grpc_metadata_batch b; grpc_metadata_batch_init(&b); std::vector elems = Fixture::GetElems(&exec_ctx); std::vector storage(elems.size()); for (size_t i = 0; i < elems.size(); i++) { GPR_ASSERT(GRPC_LOG_IF_ERROR( "addmd", grpc_metadata_batch_add_tail(&exec_ctx, &b, &storage[i], elems[i]))); } grpc_chttp2_hpack_compressor c; grpc_chttp2_hpack_compressor_init(&c); grpc_transport_one_way_stats stats; memset(&stats, 0, sizeof(stats)); grpc_slice_buffer outbuf; grpc_slice_buffer_init(&outbuf); while (state.KeepRunning()) { uint32_t stream_id = static_cast(state.iterations()); grpc_chttp2_encode_header(&exec_ctx, &c, stream_id, &b, state.range(0), state.range(1), &stats, &outbuf); if (!logged_representative_output) { logged_representative_output = true; for (size_t i = 0; i < outbuf.count; i++) { char *s = grpc_dump_slice(outbuf.slices[i], GPR_DUMP_HEX); gpr_log(GPR_DEBUG, "%" PRIdPTR ": %s", i, s); gpr_free(s); } } grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &outbuf); grpc_exec_ctx_flush(&exec_ctx); } grpc_metadata_batch_destroy(&exec_ctx, &b); grpc_chttp2_hpack_compressor_destroy(&exec_ctx, &c); grpc_slice_buffer_destroy_internal(&exec_ctx, &outbuf); grpc_exec_ctx_finish(&exec_ctx); std::ostringstream label; label << "framing_bytes/iter:" << (static_cast(stats.framing_bytes) / static_cast(state.iterations())) << " header_bytes/iter:" << (static_cast(stats.header_bytes) / static_cast(state.iterations())); state.SetLabel(label.str()); track_counters.Finish(state); } namespace hpack_encoder_fixtures { class EmptyBatch { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { return {}; } }; class SingleStaticElem { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { return {GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE}; } }; class SingleInternedElem { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { return {grpc_mdelem_from_slices( exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc")), grpc_slice_intern(grpc_slice_from_static_string("def")))}; } }; template class SingleInternedBinaryElem { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { grpc_slice bytes = MakeBytes(); std::vector out = {grpc_mdelem_from_slices( exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc-bin")), grpc_slice_intern(bytes))}; grpc_slice_unref(bytes); return out; } private: static grpc_slice MakeBytes() { std::vector v; for (int i = 0; i < kLength; i++) { v.push_back(static_cast(rand())); } return grpc_slice_from_copied_buffer(v.data(), v.size()); } }; class SingleInternedKeyElem { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { return {grpc_mdelem_from_slices( exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc")), grpc_slice_from_static_string("def"))}; } }; class SingleNonInternedElem { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { return {grpc_mdelem_from_slices(exec_ctx, grpc_slice_from_static_string("abc"), grpc_slice_from_static_string("def"))}; } }; template class SingleNonInternedBinaryElem { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { return {grpc_mdelem_from_slices( exec_ctx, grpc_slice_from_static_string("abc-bin"), MakeBytes())}; } private: static grpc_slice MakeBytes() { std::vector v; for (int i = 0; i < kLength; i++) { v.push_back(static_cast(rand())); } return grpc_slice_from_copied_buffer(v.data(), v.size()); } }; class RepresentativeClientInitialMetadata { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { return { GRPC_MDELEM_SCHEME_HTTP, GRPC_MDELEM_METHOD_POST, grpc_mdelem_from_slices( exec_ctx, GRPC_MDSTR_PATH, grpc_slice_intern(grpc_slice_from_static_string("/foo/bar"))), grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_AUTHORITY, grpc_slice_intern(grpc_slice_from_static_string( "foo.test.google.fr:1234"))), GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP, GRPC_MDELEM_TE_TRAILERS, GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC, grpc_mdelem_from_slices( exec_ctx, GRPC_MDSTR_USER_AGENT, grpc_slice_intern(grpc_slice_from_static_string( "grpc-c/3.0.0-dev (linux; chttp2; green)")))}; } }; class RepresentativeServerInitialMetadata { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { return {GRPC_MDELEM_STATUS_200, GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC, GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP}; } }; class RepresentativeServerTrailingMetadata { public: static std::vector GetElems(grpc_exec_ctx *exec_ctx) { return {GRPC_MDELEM_GRPC_STATUS_0}; } }; BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({0, 16384}); // test with eof (shouldn't affect anything) BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({1, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleStaticElem) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedKeyElem) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedElem) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedBinaryElem<1>) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedBinaryElem<3>) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedBinaryElem<10>) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedBinaryElem<31>) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedBinaryElem<100>) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedBinaryElem<1>) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedBinaryElem<3>) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedBinaryElem<10>) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedBinaryElem<31>) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedBinaryElem<100>) ->Args({0, 16384}); // test with a tiny frame size, to highlight continuation costs BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem) ->Args({0, 1}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, RepresentativeClientInitialMetadata) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, RepresentativeServerInitialMetadata) ->Args({0, 16384}); BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, RepresentativeServerTrailingMetadata) ->Args({1, 16384}); } // namespace hpack_encoder_fixtures //////////////////////////////////////////////////////////////////////////////// // HPACK parser // static void BM_HpackParserInitDestroy(benchmark::State &state) { TrackCounters track_counters; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_chttp2_hpack_parser p; while (state.KeepRunning()) { grpc_chttp2_hpack_parser_init(&exec_ctx, &p); grpc_chttp2_hpack_parser_destroy(&exec_ctx, &p); grpc_exec_ctx_flush(&exec_ctx); } grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); } BENCHMARK(BM_HpackParserInitDestroy); static void UnrefHeader(grpc_exec_ctx *exec_ctx, void *user_data, grpc_mdelem md) { GRPC_MDELEM_UNREF(exec_ctx, md); } template static void BM_HpackParserParseHeader(benchmark::State &state) { TrackCounters track_counters; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; std::vector init_slices = Fixture::GetInitSlices(); std::vector benchmark_slices = Fixture::GetBenchmarkSlices(); grpc_chttp2_hpack_parser p; grpc_chttp2_hpack_parser_init(&exec_ctx, &p); p.on_header = UnrefHeader; p.on_header_user_data = nullptr; for (auto slice : init_slices) { grpc_chttp2_hpack_parser_parse(&exec_ctx, &p, slice); } while (state.KeepRunning()) { for (auto slice : benchmark_slices) { grpc_chttp2_hpack_parser_parse(&exec_ctx, &p, slice); } grpc_exec_ctx_flush(&exec_ctx); } for (auto slice : init_slices) grpc_slice_unref(slice); for (auto slice : benchmark_slices) grpc_slice_unref(slice); grpc_chttp2_hpack_parser_destroy(&exec_ctx, &p); grpc_exec_ctx_finish(&exec_ctx); track_counters.Finish(state); } namespace hpack_parser_fixtures { static grpc_slice MakeSlice(std::vector bytes) { grpc_slice s = grpc_slice_malloc(bytes.size()); uint8_t *p = GRPC_SLICE_START_PTR(s); for (auto b : bytes) { *p++ = b; } return s; } class EmptyBatch { public: static std::vector GetInitSlices() { return {}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({})}; } }; class IndexedSingleStaticElem { public: static std::vector GetInitSlices() { return {MakeSlice( {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({0xbe})}; } }; class AddIndexedSingleStaticElem { public: static std::vector GetInitSlices() { return {}; } static std::vector GetBenchmarkSlices() { return {MakeSlice( {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})}; } }; class KeyIndexedSingleStaticElem { public: static std::vector GetInitSlices() { return {MakeSlice( {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({0x7e, 0x03, 'd', 'e', 'f'})}; } }; class IndexedSingleInternedElem { public: static std::vector GetInitSlices() { return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({0xbe})}; } }; class AddIndexedSingleInternedElem { public: static std::vector GetInitSlices() { return {}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})}; } }; class KeyIndexedSingleInternedElem { public: static std::vector GetInitSlices() { return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({0x7e, 0x03, 'g', 'h', 'i'})}; } }; class NonIndexedElem { public: static std::vector GetInitSlices() { return {}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({0x00, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})}; } }; class NonIndexedBinaryElem1 { public: static std::vector GetInitSlices() { return {}; } static std::vector GetBenchmarkSlices() { return {MakeSlice( {0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0x82, 0xf7, 0xb3})}; } }; class NonIndexedBinaryElem3 { public: static std::vector GetInitSlices() { return {}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0x84, 0x7f, 0x4e, 0x29, 0x3f})}; } }; class NonIndexedBinaryElem10 { public: static std::vector GetInitSlices() { return {}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0x8b, 0x71, 0x0c, 0xa5, 0x81, 0x73, 0x7b, 0x47, 0x13, 0xe9, 0xf7, 0xe3})}; } }; class NonIndexedBinaryElem31 { public: static std::vector GetInitSlices() { return {}; } static std::vector GetBenchmarkSlices() { return {MakeSlice({0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0xa3, 0x92, 0x43, 0x7f, 0xbe, 0x7c, 0xea, 0x6f, 0xf3, 0x3d, 0xa7, 0xa7, 0x67, 0xfb, 0xe2, 0x82, 0xf7, 0xf2, 0x8f, 0x1f, 0x9d, 0xdf, 0xf1, 0x7e, 0xb3, 0xef, 0xb2, 0x8f, 0x53, 0x77, 0xce, 0x0c, 0x13, 0xe3, 0xfd, 0x87})}; } }; class NonIndexedBinaryElem100 { public: static std::vector GetInitSlices() { return {}; } static std::vector GetBenchmarkSlices() { return {MakeSlice( {0x00, 0x07, 'a', 'b', 'c', '-', 'b', 'i', 'n', 0xeb, 0x1d, 0x4d, 0xe8, 0x96, 0x8c, 0x14, 0x20, 0x06, 0xc1, 0xc3, 0xdf, 0x6e, 0x1f, 0xef, 0xde, 0x2f, 0xde, 0xb7, 0xf2, 0xfe, 0x6d, 0xd4, 0xe4, 0x7d, 0xf5, 0x55, 0x46, 0x52, 0x3d, 0x91, 0xf2, 0xd4, 0x6f, 0xca, 0x34, 0xcd, 0xd9, 0x39, 0xbd, 0x03, 0x27, 0xe3, 0x9c, 0x74, 0xcc, 0x17, 0x34, 0xed, 0xa6, 0x6a, 0x77, 0x73, 0x10, 0xcd, 0x8e, 0x4e, 0x5c, 0x7c, 0x72, 0x39, 0xd8, 0xe6, 0x78, 0x6b, 0xdb, 0xa5, 0xb7, 0xab, 0xe7, 0x46, 0xae, 0x21, 0xab, 0x7f, 0x01, 0x89, 0x13, 0xd7, 0xca, 0x17, 0x6e, 0xcb, 0xd6, 0x79, 0x71, 0x68, 0xbf, 0x8a, 0x3f, 0x32, 0xe8, 0xba, 0xf5, 0xbe, 0xb3, 0xbc, 0xde, 0x28, 0xc7, 0xcf, 0x62, 0x7a, 0x58, 0x2c, 0xcf, 0x4d, 0xe3})}; } }; class RepresentativeClientInitialMetadata { public: static std::vector GetInitSlices() { return {grpc_slice_from_static_string( // generated with: // ``` // tools/codegen/core/gen_header_frame.py --compression inc --no_framing // < test/core/bad_client/tests/simple_request.headers // ``` "@\x05:path\x08/foo/bar" "@\x07:scheme\x04http" "@\x07:method\x04POST" "@\x0a:authority\x09localhost" "@\x0c" "content-type\x10" "application/grpc" "@\x14grpc-accept-encoding\x15identity,deflate,gzip" "@\x02te\x08trailers" "@\x0auser-agent\"bad-client grpc-c/0.12.0.0 (linux)")}; } static std::vector GetBenchmarkSlices() { // generated with: // ``` // tools/codegen/core/gen_header_frame.py --compression pre --no_framing // --hex < test/core/bad_client/tests/simple_request.headers // ``` return {MakeSlice({0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe})}; } }; class RepresentativeServerInitialMetadata { public: static std::vector GetInitSlices() { return {grpc_slice_from_static_string( // generated with: // ``` // tools/codegen/core/gen_header_frame.py --compression inc --no_framing // < // test/cpp/microbenchmarks/representative_server_initial_metadata.headers // ``` "@\x07:status\x03" "200" "@\x0c" "content-type\x10" "application/grpc" "@\x14grpc-accept-encoding\x15identity,deflate,gzip")}; } static std::vector GetBenchmarkSlices() { // generated with: // ``` // tools/codegen/core/gen_header_frame.py --compression pre --no_framing // --hex < // test/cpp/microbenchmarks/representative_server_initial_metadata.headers // ``` return {MakeSlice({0xc0, 0xbf, 0xbe})}; } }; class RepresentativeServerTrailingMetadata { public: static std::vector GetInitSlices() { return {grpc_slice_from_static_string( // generated with: // ``` // tools/codegen/core/gen_header_frame.py --compression inc --no_framing // < // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers // ``` "@\x0bgrpc-status\x01" "0" "@\x0cgrpc-message\x00")}; } static std::vector GetBenchmarkSlices() { // generated with: // ``` // tools/codegen/core/gen_header_frame.py --compression pre --no_framing // --hex < // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers // ``` return {MakeSlice({0xbf, 0xbe})}; } }; BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, EmptyBatch); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleStaticElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleStaticElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleStaticElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleInternedElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleInternedElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleInternedElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedElem); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem1); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem3); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem10); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem31); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedBinaryElem100); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, RepresentativeClientInitialMetadata); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, RepresentativeServerInitialMetadata); BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, RepresentativeServerTrailingMetadata); } // namespace hpack_parser_fixtures static void BM_Base16SomeStuff(benchmark::State &state) { uint8_t *bytes = new uint8_t[state.range(0)]; for (int i = 0; i < state.range(0); i++) { bytes[i] = static_cast(rand()); } uint8_t *encoded = new uint8_t[state.range(0) * 2]; static const uint8_t hex[] = "0123456789abcdef"; while (state.KeepRunning()) { for (int i = 0; i < state.range(0); i++) { encoded[2 * i + 0] = hex[encoded[i] >> 8]; encoded[2 * i + 1] = hex[encoded[i] & 0xf]; } } delete[] encoded; delete[] bytes; state.SetBytesProcessed(state.iterations() * state.range(0)); } BENCHMARK(BM_Base16SomeStuff)->Range(1, 4096); BENCHMARK_MAIN();