diff options
| author | Fabrice <fabrice@schaub-dev.xyz> | 2026-04-13 12:13:24 +0200 |
|---|---|---|
| committer | Fabrice <fabrice@schaub-dev.xyz> | 2026-04-13 12:13:24 +0200 |
| commit | 1439330daed4331eb1ff34e1429ba1ae2fafe937 (patch) | |
| tree | 5a03b45048855c206f10737b5b9268dadee0c19b /benches | |
| parent | 57da910f0f257f47b7d07739b231da1d502a4096 (diff) | |
adding klib bench
Diffstat (limited to 'benches')
| -rw-r--r-- | benches/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | benches/bench_common.hpp | 1 | ||||
| m--------- | benches/klib | 0 | ||||
| -rw-r--r-- | benches/klib_bench.cpp | 238 | ||||
| -rw-r--r-- | benches/results.py | 1 |
5 files changed, 244 insertions, 0 deletions
diff --git a/benches/CMakeLists.txt b/benches/CMakeLists.txt index d096f4b..7e44a56 100644 --- a/benches/CMakeLists.txt +++ b/benches/CMakeLists.txt @@ -13,6 +13,7 @@ set(cm_bench_targets tidwall_bench unordered_map_bench abseil_bench + klib_bench ) foreach(target_name IN LISTS cm_bench_targets) @@ -53,3 +54,6 @@ target_include_directories(tidwall_bench PRIVATE "${cm_tidwalldir}") target_sources(tidwall_bench PRIVATE "${cm_tidwalldir}/hashmap.c") target_link_libraries(abseil_bench PRIVATE absl::flat_hash_map) + +set(cm_klibdir "${cm_benchesdir}/klib") +target_include_directories(klib_bench PRIVATE "${cm_klibdir}") diff --git a/benches/bench_common.hpp b/benches/bench_common.hpp index 732ffb4..1f3b4d3 100644 --- a/benches/bench_common.hpp +++ b/benches/bench_common.hpp @@ -20,6 +20,7 @@ #include "tidwall/hashmap.h" #include "absl/container/flat_hash_map.h" + namespace cmbench { inline constexpr std::int64_t kEntryCount = 1'000'000; diff --git a/benches/klib b/benches/klib new file mode 160000 +Subproject 97a0fcb790b43b9e5da8994f4671021fec036f1 diff --git a/benches/klib_bench.cpp b/benches/klib_bench.cpp new file mode 100644 index 0000000..97db497 --- /dev/null +++ b/benches/klib_bench.cpp @@ -0,0 +1,238 @@ +#include <benchmark/benchmark.h> + +#include "bench_common.hpp" + +extern "C" { +#include "klib/khash.h" +} + +namespace { + +using namespace cmbench; + +// Define khash instances for each key-value type +static inline std::uint64_t kh_hash_u64(std::uint64_t key) { + return hash_xxh_avalanche(key); +} + +static inline int kh_equal_u64(std::uint64_t a, std::uint64_t b) { + return a == b; +} + +KHASH_INIT(u64_scalar, std::uint64_t, ScalarValue, 1, kh_hash_u64, kh_equal_u64) + +static inline std::uint64_t kh_hash_entity(EntityId key) { + return hash_xxh_avalanche(key); +} + +static inline int kh_equal_entity(EntityId a, EntityId b) { + return byte_equal(a, b); +} + +KHASH_INIT(entity_payload, EntityId, ComponentPayload, 1, kh_hash_entity, + kh_equal_entity) + +static inline std::uint64_t kh_hash_component(ComponentKey key) { + return hash_xxh_avalanche(key); +} + +static inline int kh_equal_component(ComponentKey a, ComponentKey b) { + return byte_equal(a, b); +} + +KHASH_INIT(component_meta, ComponentKey, ComponentMeta, 1, kh_hash_component, + kh_equal_component) + +// Adapter for u64 -> ScalarValue +struct KlibScalarAdapter { + khash_t(u64_scalar) * map = nullptr; + + KlibScalarAdapter() { map = kh_init(u64_scalar); } + + ~KlibScalarAdapter() { + if (map != nullptr) kh_destroy(u64_scalar, map); + } + + KlibScalarAdapter(const KlibScalarAdapter&) = delete; + KlibScalarAdapter& operator=(const KlibScalarAdapter&) = delete; + + void reserve(std::size_t count) { benchmark::DoNotOptimize(count); } + + void insert(const std::uint64_t& key, const ScalarValue& value) { + int ret; + khiter_t k = kh_put(u64_scalar, map, key, &ret); + if (ret < 0) panic_impl(__FILE__, __LINE__, "insert failed"); + kh_value(map, k) = value; + } + + ScalarValue* lookup_hit(const std::uint64_t& key) { + khiter_t k = kh_get(u64_scalar, map, key); + if (k == kh_end(map)) panic_impl(__FILE__, __LINE__, "lookup hit failed"); + return &kh_value(map, k); + } + + void lookup_miss(const std::uint64_t& key) { + khiter_t k = kh_get(u64_scalar, map, key); + if (k != kh_end(map)) panic_impl(__FILE__, __LINE__, "lookup miss failed"); + benchmark::DoNotOptimize(k); + } + + void erase(const std::uint64_t& key) { + khiter_t k = kh_get(u64_scalar, map, key); + if (k == kh_end(map)) panic_impl(__FILE__, __LINE__, "erase failed"); + kh_del(u64_scalar, map, k); + } +}; + +// Adapter for EntityId -> ComponentPayload +struct KlibEntityAdapter { + khash_t(entity_payload) * map = nullptr; + + KlibEntityAdapter() { map = kh_init(entity_payload); } + + ~KlibEntityAdapter() { + if (map != nullptr) kh_destroy(entity_payload, map); + } + + KlibEntityAdapter(const KlibEntityAdapter&) = delete; + KlibEntityAdapter& operator=(const KlibEntityAdapter&) = delete; + + void reserve(std::size_t count) { benchmark::DoNotOptimize(count); } + + void insert(const EntityId& key, const ComponentPayload& value) { + int ret; + khiter_t k = kh_put(entity_payload, map, key, &ret); + if (ret < 0) panic_impl(__FILE__, __LINE__, "insert failed"); + kh_value(map, k) = value; + } + + ComponentPayload* lookup_hit(const EntityId& key) { + khiter_t k = kh_get(entity_payload, map, key); + if (k == kh_end(map)) panic_impl(__FILE__, __LINE__, "lookup hit failed"); + return &kh_value(map, k); + } + + void lookup_miss(const EntityId& key) { + khiter_t k = kh_get(entity_payload, map, key); + if (k != kh_end(map)) panic_impl(__FILE__, __LINE__, "lookup miss failed"); + benchmark::DoNotOptimize(k); + } + + void erase(const EntityId& key) { + khiter_t k = kh_get(entity_payload, map, key); + if (k == kh_end(map)) panic_impl(__FILE__, __LINE__, "erase failed"); + kh_del(entity_payload, map, k); + } +}; + +// Adapter for ComponentKey -> ComponentMeta +struct KlibComponentAdapter { + khash_t(component_meta) * map = nullptr; + + KlibComponentAdapter() { map = kh_init(component_meta); } + + ~KlibComponentAdapter() { + if (map != nullptr) kh_destroy(component_meta, map); + } + + KlibComponentAdapter(const KlibComponentAdapter&) = delete; + KlibComponentAdapter& operator=(const KlibComponentAdapter&) = delete; + + void reserve(std::size_t count) { benchmark::DoNotOptimize(count); } + + void insert(const ComponentKey& key, const ComponentMeta& value) { + int ret; + khiter_t k = kh_put(component_meta, map, key, &ret); + if (ret < 0) panic_impl(__FILE__, __LINE__, "insert failed"); + kh_value(map, k) = value; + } + + ComponentMeta* lookup_hit(const ComponentKey& key) { + khiter_t k = kh_get(component_meta, map, key); + if (k == kh_end(map)) panic_impl(__FILE__, __LINE__, "lookup hit failed"); + return &kh_value(map, k); + } + + void lookup_miss(const ComponentKey& key) { + khiter_t k = kh_get(component_meta, map, key); + if (k != kh_end(map)) panic_impl(__FILE__, __LINE__, "lookup miss failed"); + benchmark::DoNotOptimize(k); + } + + void erase(const ComponentKey& key) { + khiter_t k = kh_get(component_meta, map, key); + if (k == kh_end(map)) panic_impl(__FILE__, __LINE__, "erase failed"); + kh_del(component_meta, map, k); + } +}; + +static void BM_Insert_Scalar(benchmark::State& state) { + bench_insert<KlibScalarAdapter, std::uint64_t, ScalarValue>( + state, scalar_workload(), {"khash", "c", "Scalar", "Insert"}); +} +static void BM_LookupHit_Scalar(benchmark::State& state) { + bench_lookup_hit<KlibScalarAdapter, std::uint64_t, ScalarValue>( + state, scalar_workload(), {"khash", "c", "Scalar", "LookupHit"}); +} +static void BM_LookupMiss_Scalar(benchmark::State& state) { + bench_lookup_miss<KlibScalarAdapter, std::uint64_t, ScalarValue>( + state, scalar_workload(), {"khash", "c", "Scalar", "LookupMiss"}); +} +static void BM_Erase_Scalar(benchmark::State& state) { + bench_erase<KlibScalarAdapter, std::uint64_t, ScalarValue>( + state, scalar_workload(), {"khash", "c", "Scalar", "Erase"}); +} + +static void BM_Insert_HandlePayload(benchmark::State& state) { + bench_insert<KlibEntityAdapter, EntityId, ComponentPayload>( + state, entity_workload(), {"khash", "c", "HandlePayload", "Insert"}); +} +static void BM_LookupHit_HandlePayload(benchmark::State& state) { + bench_lookup_hit<KlibEntityAdapter, EntityId, ComponentPayload>( + state, entity_workload(), {"khash", "c", "HandlePayload", "LookupHit"}); +} +static void BM_LookupMiss_HandlePayload(benchmark::State& state) { + bench_lookup_miss<KlibEntityAdapter, EntityId, ComponentPayload>( + state, entity_workload(), + {"khash", "c", "HandlePayload", "LookupMiss"}); +} +static void BM_Erase_HandlePayload(benchmark::State& state) { + bench_erase<KlibEntityAdapter, EntityId, ComponentPayload>( + state, entity_workload(), {"khash", "c", "HandlePayload", "Erase"}); +} + +static void BM_Insert_CompositeKey(benchmark::State& state) { + bench_insert<KlibComponentAdapter, ComponentKey, ComponentMeta>( + state, component_workload(), {"khash", "c", "CompositeKey", "Insert"}); +} +static void BM_LookupHit_CompositeKey(benchmark::State& state) { + bench_lookup_hit<KlibComponentAdapter, ComponentKey, ComponentMeta>( + state, component_workload(), + {"khash", "c", "CompositeKey", "LookupHit"}); +} +static void BM_LookupMiss_CompositeKey(benchmark::State& state) { + bench_lookup_miss<KlibComponentAdapter, ComponentKey, ComponentMeta>( + state, component_workload(), + {"khash", "c", "CompositeKey", "LookupMiss"}); +} +static void BM_Erase_CompositeKey(benchmark::State& state) { + bench_erase<KlibComponentAdapter, ComponentKey, ComponentMeta>( + state, component_workload(), {"khash", "c", "CompositeKey", "Erase"}); +} + +} // namespace + +BENCHMARK(BM_Insert_Scalar); +BENCHMARK(BM_LookupHit_Scalar); +BENCHMARK(BM_LookupMiss_Scalar); +BENCHMARK(BM_Erase_Scalar); + +BENCHMARK(BM_Insert_HandlePayload); +BENCHMARK(BM_LookupHit_HandlePayload); +BENCHMARK(BM_LookupMiss_HandlePayload); +BENCHMARK(BM_Erase_HandlePayload); + +BENCHMARK(BM_Insert_CompositeKey); +BENCHMARK(BM_LookupHit_CompositeKey); +BENCHMARK(BM_LookupMiss_CompositeKey); +BENCHMARK(BM_Erase_CompositeKey); diff --git a/benches/results.py b/benches/results.py index e6c6f4c..047d9c0 100644 --- a/benches/results.py +++ b/benches/results.py @@ -104,6 +104,7 @@ def main(): "std::unordered_map": root_dir / "unordered.json", "tidwall": root_dir / "tidwall.json", "absl::flat_hash_map": root_dir / "abseil.json", + "khash": root_dir / "klib.json", } all_results = {} |
