|
| 1 | +#include <memory> |
| 2 | +#include <iostream> |
| 3 | +#include <chrono> |
| 4 | +#include <array> |
| 5 | +#include <string> |
| 6 | +#include <oup/observable_unique_ptr.hpp> |
| 7 | + |
| 8 | +// External functions, the compiler cannot see through. Prevents optimisations. |
| 9 | +template<typename T> |
| 10 | +void use_object(T&) noexcept; |
| 11 | + |
| 12 | +template<typename T> |
| 13 | +struct pointer_traits; |
| 14 | + |
| 15 | +template<typename T> |
| 16 | +struct pointer_traits<std::unique_ptr<T>> { |
| 17 | + using element_type = T; |
| 18 | + using ptr_type = std::unique_ptr<T>; |
| 19 | + using weak_type = T*; |
| 20 | + |
| 21 | + static ptr_type make_ptr() noexcept { return ptr_type(new element_type); } |
| 22 | + static ptr_type make_ptr_factory() noexcept { return std::make_unique<element_type>(); } |
| 23 | + static weak_type make_weak(ptr_type& p) noexcept { return p.get(); } |
| 24 | + template<typename F> |
| 25 | + static void deref_weak(weak_type& p, F&& func) noexcept { return func(*p); } |
| 26 | +}; |
| 27 | + |
| 28 | +template<typename T> |
| 29 | +struct pointer_traits<std::shared_ptr<T>> { |
| 30 | + using element_type = T; |
| 31 | + using ptr_type = std::shared_ptr<T>; |
| 32 | + using weak_type = std::weak_ptr<T>; |
| 33 | + |
| 34 | + static ptr_type make_ptr() noexcept { return ptr_type(new element_type); } |
| 35 | + static ptr_type make_ptr_factory() noexcept { return std::make_shared<element_type>(); } |
| 36 | + static weak_type make_weak(ptr_type& p) noexcept { return weak_type(p); } |
| 37 | + template<typename F> |
| 38 | + static void deref_weak(weak_type& p, F&& func) noexcept { if (auto s = p.lock()) func(*s); } |
| 39 | +}; |
| 40 | + |
| 41 | +template<typename T> |
| 42 | +struct pointer_traits<oup::observable_unique_ptr<T>> { |
| 43 | + using element_type = T; |
| 44 | + using ptr_type = oup::observable_unique_ptr<T>; |
| 45 | + using weak_type = oup::observer_ptr<T>; |
| 46 | + |
| 47 | + static ptr_type make_ptr() noexcept { return ptr_type(new element_type); } |
| 48 | + static ptr_type make_ptr_factory() noexcept { return oup::make_observable_unique<element_type>(); } |
| 49 | + static weak_type make_weak(ptr_type& p) noexcept { return weak_type(p); } |
| 50 | + template<typename F> |
| 51 | + static void deref_weak(weak_type& p, F&& func) noexcept { return func(*p); } |
| 52 | +}; |
| 53 | + |
| 54 | +template<typename T> |
| 55 | +struct pointer_traits<oup::observable_sealed_ptr<T>> { |
| 56 | + using element_type = T; |
| 57 | + using ptr_type = oup::observable_sealed_ptr<T>; |
| 58 | + using weak_type = oup::observer_ptr<T>; |
| 59 | + |
| 60 | + static ptr_type make_ptr() noexcept { return oup::make_observable_sealed<element_type>(); } |
| 61 | + static ptr_type make_ptr_factory() noexcept { return oup::make_observable_sealed<element_type>(); } |
| 62 | + static weak_type make_weak(ptr_type& p) noexcept { return weak_type(p); } |
| 63 | + template<typename F> |
| 64 | + static void deref_weak(weak_type& p, F&& func) noexcept { return func(*p); } |
| 65 | +}; |
| 66 | + |
| 67 | +template<typename T> |
| 68 | +struct benchmark { |
| 69 | + using traits = pointer_traits<T>; |
| 70 | + using element_type = typename traits::element_type; |
| 71 | + using owner_type = typename traits::ptr_type; |
| 72 | + using weak_type = typename traits::weak_type; |
| 73 | + |
| 74 | + owner_type owner; |
| 75 | + weak_type weak; |
| 76 | + |
| 77 | + benchmark() : owner(traits::make_ptr()), weak(traits::make_weak(owner)) {} |
| 78 | + |
| 79 | + void construct_destruct_owner_empty() { |
| 80 | + auto p = owner_type{}; |
| 81 | + use_object(p); |
| 82 | + } |
| 83 | + |
| 84 | + void construct_destruct_owner() { |
| 85 | + auto p = traits::make_ptr(); |
| 86 | + use_object(p); |
| 87 | + } |
| 88 | + |
| 89 | + void construct_destruct_owner_factory() { |
| 90 | + auto p = traits::make_ptr_factory(); |
| 91 | + use_object(p); |
| 92 | + } |
| 93 | + |
| 94 | + void construct_destruct_weak_empty() { |
| 95 | + auto p = weak_type{}; |
| 96 | + use_object(p); |
| 97 | + } |
| 98 | + |
| 99 | + void construct_destruct_weak() { |
| 100 | + auto wp = traits::make_weak(owner); |
| 101 | + use_object(wp); |
| 102 | + } |
| 103 | + |
| 104 | + void dereference_owner() { |
| 105 | + use_object(*owner); |
| 106 | + } |
| 107 | + |
| 108 | + void dereference_weak() { |
| 109 | + traits::deref_weak(weak, [](auto& o) { use_object(o); }); |
| 110 | + } |
| 111 | + void construct_destruct_weak_copy() { |
| 112 | + auto wp = weak; |
| 113 | + use_object(wp); |
| 114 | + } |
| 115 | +}; |
| 116 | + |
| 117 | +using timer = std::chrono::high_resolution_clock; |
| 118 | + |
| 119 | +template<typename B, typename F> |
| 120 | +double run_benchmark_for(F&& func) { |
| 121 | + B bench{}; |
| 122 | + |
| 123 | + auto prev = timer::now(); |
| 124 | + double elapsed = 0.0; |
| 125 | + double count = 0.0; |
| 126 | + constexpr std::size_t num_iter = 10'000; |
| 127 | + |
| 128 | + while (elapsed < 1.0) { |
| 129 | + for (std::size_t i = 0; i < num_iter; ++i) { |
| 130 | + func(bench); |
| 131 | + } |
| 132 | + |
| 133 | + auto now = timer::now(); |
| 134 | + elapsed += std::chrono::duration_cast<std::chrono::duration<double>>(now - prev).count(); |
| 135 | + count += static_cast<double>(num_iter); |
| 136 | + std::swap(now, prev); |
| 137 | + } |
| 138 | + |
| 139 | + return elapsed/count; |
| 140 | +} |
| 141 | + |
| 142 | +template<typename B, typename F> |
| 143 | +auto run_benchmark(F&& func) { |
| 144 | + using ref_type = benchmark<std::unique_ptr<typename B::element_type>>; |
| 145 | + double result = run_benchmark_for<B>(func); |
| 146 | + double result_ref = run_benchmark_for<ref_type>(func); |
| 147 | + return std::make_pair(result, result/result_ref); |
| 148 | +} |
| 149 | + |
| 150 | +template<typename T> |
| 151 | +void do_benchmarks_for_ptr(const char* type_name, const char* ptr_name) { |
| 152 | + using B = benchmark<T>; |
| 153 | + |
| 154 | + auto construct_destruct_owner_empty = run_benchmark<B>([](auto& b) { return b.construct_destruct_owner_empty(); }); |
| 155 | + auto construct_destruct_owner = run_benchmark<B>([](auto& b) { return b.construct_destruct_owner(); }); |
| 156 | + auto construct_destruct_owner_factory = run_benchmark<B>([](auto& b) { return b.construct_destruct_owner_factory(); }); |
| 157 | + auto dereference_owner = run_benchmark<B>([](auto& b) { return b.dereference_owner(); }); |
| 158 | + auto construct_destruct_weak_empty = run_benchmark<B>([](auto& b) { return b.construct_destruct_weak_empty(); }); |
| 159 | + auto construct_destruct_weak = run_benchmark<B>([](auto& b) { return b.construct_destruct_weak(); }); |
| 160 | + auto construct_destruct_weak_copy = run_benchmark<B>([](auto& b) { return b.construct_destruct_weak_copy(); }); |
| 161 | + auto dereference_weak = run_benchmark<B>([](auto& b) { return b.dereference_weak(); }); |
| 162 | + |
| 163 | + std::cout << ptr_name << "<" << type_name << ">:" << std::endl; |
| 164 | + #define report(which) std::cout << " - " << #which << ": " << which.first*1e6 << "us (x" << which.second << ")" << std::endl |
| 165 | + |
| 166 | + report(construct_destruct_owner_empty); |
| 167 | + report(construct_destruct_owner); |
| 168 | + report(construct_destruct_owner_factory); |
| 169 | + report(dereference_owner); |
| 170 | + report(construct_destruct_weak_empty); |
| 171 | + report(construct_destruct_weak); |
| 172 | + report(construct_destruct_weak_copy); |
| 173 | + report(dereference_weak); |
| 174 | + |
| 175 | + #undef report |
| 176 | + std::cout << std::endl; |
| 177 | +} |
| 178 | + |
| 179 | +template<typename T> |
| 180 | +void do_benchmarks(const char* type_name) { |
| 181 | + do_benchmarks_for_ptr<std::shared_ptr<T>>(type_name, "shared_ptr"); |
| 182 | + do_benchmarks_for_ptr<oup::observable_unique_ptr<T>>(type_name, "observable_unique_ptr"); |
| 183 | + do_benchmarks_for_ptr<oup::observable_sealed_ptr<T>>(type_name, "observable_sealed_ptr"); |
| 184 | +} |
| 185 | + |
| 186 | +int main() { |
| 187 | + do_benchmarks<int>("int"); |
| 188 | + do_benchmarks<std::string>("string"); |
| 189 | + do_benchmarks<std::array<int,65'536>>("big_array"); |
| 190 | + return 0; |
| 191 | +} |
0 commit comments