summaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorHåvard Pettersen <havardpe@oath.com>2020-09-25 10:00:50 +0000
committerHåvard Pettersen <havardpe@oath.com>2020-09-25 10:00:50 +0000
commit929128a2e44bc55d56202a252527107b786a13f3 (patch)
treef23a2ac4d6e8db39f30fd01029087c674a5743b9 /vespalib
parent9c90d3aaed422af9ecf41e2da34866ee7c26ee5c (diff)
benchmark type detection
Diffstat (limited to 'vespalib')
-rw-r--r--vespalib/CMakeLists.txt3
-rw-r--r--vespalib/src/tests/detect_type_benchmark/.gitignore1
-rw-r--r--vespalib/src/tests/detect_type_benchmark/CMakeLists.txt8
-rw-r--r--vespalib/src/tests/detect_type_benchmark/detect_type_benchmark.cpp150
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()