Collect more detailed metrics on hashtables. (#4046)

Previously we just looked at the raw count of probed keys. Now, we
compute the average and max of both the probe _distance_ measured in the
number of _groups_ probed, and the number of probe _compares_ measured
in the compares required _before_ finding the matching entry.

This lets us understand the relative impact of probe-distance vs. tag
collisions on a given set of benchmark keys. Some of this is motivated
by considering additional optimization techniques similar to those used
in Boost's table and the F14 table from Facebook/Meta.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Chandler Carruth
2024-06-10 21:43:13 +00:00
committed by GitHub
co-authored by Richard Smith
parent 21a81bc59e
commit 3be57b71e0
6 changed files with 176 additions and 56 deletions
+21 -9
View File
@@ -18,6 +18,7 @@ using RawHashtable::CarbonHashDI;
using RawHashtable::GetKeysAndHitKeys;
using RawHashtable::GetKeysAndMissKeys;
using RawHashtable::HitArgs;
using RawHashtable::ReportTableMetrics;
using RawHashtable::SizeArgs;
using RawHashtable::ValueToBool;
@@ -159,6 +160,15 @@ template <typename MapT>
using MapWrapper =
MapWrapperOverride<MapT, MapOverride::CARBON_MAP_BENCH_OVERRIDE>;
template <typename MapT>
auto ReportMetrics(const MapWrapper<MapT>& m_wrapper, benchmark::State& state)
-> void {
// Report some extra statistics about the Carbon type.
if constexpr (IsCarbonMap<MapT>) {
ReportTableMetrics(m_wrapper.m, state);
}
}
// NOLINTBEGIN(bugprone-macro-parentheses): Parentheses are incorrect here.
#define MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, KT, VT) \
BENCHMARK(NAME<Map<KT, VT>>)->Apply(APPLY); \
@@ -223,6 +233,8 @@ static void BM_MapContainsHit(benchmark::State& state) {
i += static_cast<ssize_t>(result);
}
}
ReportMetrics(m, state);
}
MAP_BENCHMARK_ONE_OP(BM_MapContainsHit, HitArgs);
@@ -250,6 +262,8 @@ static void BM_MapContainsMiss(benchmark::State& state) {
i += static_cast<ssize_t>(!result);
}
}
ReportMetrics(m, state);
}
MAP_BENCHMARK_ONE_OP(BM_MapContainsMiss, SizeArgs);
@@ -302,6 +316,8 @@ static void BM_MapLookupHit(benchmark::State& state) {
i += static_cast<ssize_t>(result);
}
}
ReportMetrics(m, state);
}
MAP_BENCHMARK_ONE_OP(BM_MapLookupHit, HitArgs);
@@ -339,6 +355,8 @@ static void BM_MapUpdateHit(benchmark::State& state) {
CARBON_DCHECK(!inserted);
}
}
ReportMetrics(m, state);
}
MAP_BENCHMARK_ONE_OP(BM_MapUpdateHit, HitArgs);
@@ -454,19 +472,13 @@ static void BM_MapInsertSeq(benchmark::State& state) {
if constexpr (IsCarbonMap<MapT>) {
// Re-build a map outside of the timing loop to look at the statistics
// rather than the timing.
MapT m;
MapWrapperT m;
for (auto k : keys) {
bool inserted = m.Insert(k, MakeValue<VT>()).is_inserted();
bool inserted = m.BenchInsert(k, MakeValue<VT>());
CARBON_DCHECK(inserted) << "Must be a successful insert!";
}
// While this count is "iteration invariant" (it should be exactly the same
// for every iteration as the set of keys is the same), we don't use that
// because it will scale this by the number of iterations. We want to
// display the probe count of this benchmark *parameter*, not the probe
// count that resulted from the number of iterations. That means we use the
// normal counter API without flags.
state.counters["Probed"] = m.CountProbedKeys();
ReportMetrics(m, state);
// Uncomment this call to print out statistics about the index-collisions
// among these keys for debugging: