diff options
author | Håvard Pettersen <havardpe@oath.com> | 2020-09-25 10:00:50 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@oath.com> | 2020-09-25 10:00:50 +0000 |
commit | 929128a2e44bc55d56202a252527107b786a13f3 (patch) | |
tree | f23a2ac4d6e8db39f30fd01029087c674a5743b9 /vespalib | |
parent | 9c90d3aaed422af9ecf41e2da34866ee7c26ee5c (diff) |
benchmark type detection
Diffstat (limited to 'vespalib')
-rw-r--r-- | vespalib/CMakeLists.txt | 3 | ||||
-rw-r--r-- | vespalib/src/tests/detect_type_benchmark/.gitignore | 1 | ||||
-rw-r--r-- | vespalib/src/tests/detect_type_benchmark/CMakeLists.txt | 8 | ||||
-rw-r--r-- | vespalib/src/tests/detect_type_benchmark/detect_type_benchmark.cpp | 150 |
4 files changed, 161 insertions, 1 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt index c894a798b91..ec003329999 100644 --- a/vespalib/CMakeLists.txt +++ b/vespalib/CMakeLists.txt @@ -26,6 +26,7 @@ vespa_define_module( src/tests/benchmark_timer src/tests/box src/tests/btree + src/tests/child_process src/tests/closure src/tests/component src/tests/compress @@ -46,6 +47,7 @@ vespa_define_module( src/tests/datastore/unique_store_dictionary src/tests/datastore/unique_store_string_allocator src/tests/delegatelist + src/tests/detect_type_benchmark src/tests/dotproduct src/tests/drop-file-from-cache src/tests/dual_merge_director @@ -97,7 +99,6 @@ vespa_define_module( src/tests/sharedptr src/tests/signalhandler src/tests/simple_thread_bundle - src/tests/child_process src/tests/slime src/tests/slime/external_data_value src/tests/slime/summary-feature-benchmark diff --git a/vespalib/src/tests/detect_type_benchmark/.gitignore b/vespalib/src/tests/detect_type_benchmark/.gitignore new file mode 100644 index 00000000000..3d2fbf713c6 --- /dev/null +++ b/vespalib/src/tests/detect_type_benchmark/.gitignore @@ -0,0 +1 @@ +/vespalib_detect_type_benchmark_app diff --git a/vespalib/src/tests/detect_type_benchmark/CMakeLists.txt b/vespalib/src/tests/detect_type_benchmark/CMakeLists.txt new file mode 100644 index 00000000000..279622dd452 --- /dev/null +++ b/vespalib/src/tests/detect_type_benchmark/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(vespalib_detect_type_benchmark_app TEST + SOURCES + detect_type_benchmark.cpp + DEPENDS + vespalib + GTest::GTest +) diff --git a/vespalib/src/tests/detect_type_benchmark/detect_type_benchmark.cpp b/vespalib/src/tests/detect_type_benchmark/detect_type_benchmark.cpp new file mode 100644 index 00000000000..44e15de2399 --- /dev/null +++ b/vespalib/src/tests/detect_type_benchmark/detect_type_benchmark.cpp @@ -0,0 +1,150 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/util/benchmark_timer.h> +#include <vespa/vespalib/gtest/gtest.h> +#include <typeindex> + +// Typically when you want a flexible way of identifying whether you +// are encountering a specific subclass, you try to dynamic_cast the +// pointer and check for a non-null return value. This is the most +// flexible way since it requires no extra code in the class itself +// and you will also detect any subclasses of the subclass you are +// testing for. Sometimes you only need to identify the exact class +// and speed in doing so is all that matters. This benchmark tries to +// isolate and measure the cost of different strategies. Note that +// dynamic_cast may be more expensive for more complex class +// hierarchies. + +using vespalib::BenchmarkTimer; + +constexpr int A_ID = 1; +constexpr int B_ID = 2; + +constexpr size_t LOOP_CNT = 1000000; + +class BaseClass { +private: + int _static_id; +public: + BaseClass(int id) : _static_id(id) {} + int static_id() const { return _static_id; } + virtual int dynamic_id() const = 0; + virtual ~BaseClass() {} +}; + +struct A : BaseClass { + A() : BaseClass(A_ID) {} + int dynamic_id() const override { return A_ID; } +}; + +struct B : BaseClass { + B() : BaseClass(B_ID) {} + int dynamic_id() const override { return B_ID; } +}; + +using is_A = bool (*)(const BaseClass *); + +//----------------------------------------------------------------------------- + +struct CheckType { + BaseClass *ptr; + is_A pred; + CheckType(BaseClass *ptr_in, is_A pred_in) : ptr(ptr_in), pred(pred_in) {} + void operator()() const { + bool result = pred(ptr); + (void) result; + } +}; + +struct Nop { + void operator()() const {} +}; + +//----------------------------------------------------------------------------- + +A a; +B b; +Nop nop; +double baseline = 0.0; +const auto &typeid_A = typeid(A); + +//----------------------------------------------------------------------------- + +bool always_true(const BaseClass *) __attribute__((noinline)); +bool always_true(const BaseClass *) { + return true; +} + +bool always_false(const BaseClass *) __attribute__((noinline)); +bool always_false(const BaseClass *) { + return false; +} + +//----------------------------------------------------------------------------- + +bool use_dynamic_cast(const BaseClass *) __attribute__((noinline)); +bool use_dynamic_cast(const BaseClass *ptr) { + return (dynamic_cast<const A*>(ptr)); +} + +bool use_type_index(const BaseClass *) __attribute__((noinline)); +bool use_type_index(const BaseClass *ptr) { + return (std::type_index(typeid(*ptr)) == std::type_index(typeid_A)); +} + +bool use_type_id(const BaseClass *) __attribute__((noinline)); +bool use_type_id(const BaseClass *ptr) { + return (typeid(*ptr) == typeid_A); +} + +bool use_dynamic_id(const BaseClass *) __attribute__((noinline)); +bool use_dynamic_id(const BaseClass *ptr) { + return (ptr->dynamic_id() == A_ID); +} + +bool use_static_id(const BaseClass *) __attribute__((noinline)); +bool use_static_id(const BaseClass *ptr) { + return (ptr->static_id() == A_ID); +} + +//----------------------------------------------------------------------------- + +double estimate_cost_ns(CheckType check) { + return BenchmarkTimer::benchmark(check, nop, LOOP_CNT, 5.0) * 1000.0 * 1000.0 * 1000.0; +} + +void benchmark(const char *desc, is_A pred) { + EXPECT_TRUE(pred(&a)) << desc; + EXPECT_FALSE(pred(&b)) << desc; + CheckType yes(&a, pred); + CheckType no(&b, pred); + double t1 = estimate_cost_ns(yes); + double t2 = estimate_cost_ns(no); + double my_cost = ((t1 + t2) / 2.0) - baseline; + fprintf(stderr, "%s cost is %5.2f ns (true %5.2f, false %5.2f, baseline %5.2f)\n", + desc, my_cost, t1, t2, baseline); +} + +//----------------------------------------------------------------------------- + +TEST(DetectTypeBenchmark, find_baseline) { + CheckType check_true(&a, always_true); + CheckType check_false(&b, always_false); + double t1 = estimate_cost_ns(check_true); + double t2 = estimate_cost_ns(check_false); + baseline = (t1 + t2) / 2.0; + fprintf(stderr, "baseline cost is %5.2f ns (true %5.2f, false %5.2f)\n", + baseline, t1, t2); +} + +TEST(DetectTypeBenchmark, measure_overhead) { + benchmark("[dynamic_cast]", use_dynamic_cast); + benchmark(" [type_index]", use_type_index); + benchmark(" [typeid]", use_type_id); + benchmark(" [dynamic id]", use_dynamic_id); + benchmark(" [static id]", use_static_id); +} + +//----------------------------------------------------------------------------- + +GTEST_MAIN_RUN_ALL_TESTS() |