Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# 0.13.2

- Added the `quality` command to `hash_benchmark_report.py`: it generates both the "Algorithm overview" and SMHasher3 "Results" tables in `mbo/hash/README.md` from `hash_algorithms.json` (manual/editorial columns) merged with the measured verdict/score/failures re-parsed from a data bundle (`--bundle`); `--check` gates them, and a `consistency` command verifies all bundles from the same source SHA agree (SMHasher3 verdicts are machine-independent).
- Fixed SMHasher3 result parsing so an invalid hash name or crash reads as `ERROR` instead of a false `PASS`, and the pass/fail score and failing families now parse from the run Summary; legacy/short SMHasher3 names are aliased to their registered form when a measured dataset is loaded.
- Integrated `mbo/hash/measurements` as a normal dev package (dropped its nested module and `.bazelignore` entry; still stripped from release archives) and added a `quality_sh_test` bazel test gating the generated tables; the test CI job now fetches the measurement bundles (Git LFS) so it reads real data.
- Added a `no-deps-on-measurements` pre-commit guard so nothing outside the release-stripped `mbo/hash/measurements` may depend on it (which would dangle in releases).
- Ported the default `mumbo` 64-bit hash to Starlark (`//mbo/hash:hash.bzl` `hash.mumbo`), byte-identical to C++ and verified against it; `hash.bzl` now offers `mumbo`, `dumbo`, and `fnv1a`.
- Added a `compare` command reporting per-case Δ% and a geomean between two datasets.
- Made `tables`/`plot`/`compare`/`quality` accept a bundle `.tgz` or a results JSON, positionally or via `--results`/`--bundle`.
- Added `plot --kind` (throughput/latency/all) and `--scale` (log-log/linear-log).
- Expanded the benchmark `kFullSizes` and swept latency over the full set (dense ns-vs-length latency curve).
- Added a `quality` command generating the overview + SMHasher3 Results tables from `hash_algorithms.json` and a measured bundle; `--check` gates them.
- Added a `consistency` command verifying same-source-SHA bundles agree on SMHasher3 verdicts.
- Fixed SMHasher3 parsing: a bad name or crash reads as `ERROR` not a false `PASS`; score and failing families parse from the Summary; legacy names aliased.
- Integrated `mbo/hash/measurements` as a normal dev package (still release-stripped) with a `quality_sh_test` gating the tables; test CI fetches the LFS bundles.
- Added a `no-deps-on-measurements` pre-commit guard.
- Ported `mumbo` (64-bit) to Starlark (`hash.mumbo`), byte-identical to C++; `hash.bzl` now offers `mumbo`, `dumbo`, and `fnv1a`.

# 0.13.1

Expand Down
73 changes: 68 additions & 5 deletions mbo/hash/hash_benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,70 @@ constexpr std::array<int, 22> kReadmeSizes = {
// ~1.2) from 1..4096 unioned with the boundary set above, so the ns-vs-length
// curve is smooth and the tier edges stay sampled. For the complete dataset /
// graph, not for the README tables.
constexpr std::array<int, 53> kFullSizes = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 18, 19, 22, 27, 32,
38, 46, 47, 48, 55, 63, 64, 66, 79, 95, 114, 127, 128, 137, 165, 198, 237, 256,
285, 342, 410, 492, 591, 709, 851, 1'021, 1'024, 1'225, 1'470, 1'764, 2'116, 2'540, 3'048, 3'657, 4'096,
// Tiny 9
// GCC/MSVC SSO 7
// Clang SSO 6
// Extended/AVX2 9
// Cache 4
// Bins 5
// Medium 14
// Large 10
// TOTAL 64
constexpr std::array<int, 64> kFullSizes = {
// --- Tiny Keys & Word Alignments ---
1, 2, 3,
4, // 32-bit register (optimized fast-paths for integers)
5, 6, 7,
8, // 64-bit word (standard 64-bit register boundary)
9,

// --- Register Alignments & GCC/MSVC SSO limits ---
11,
12, // 3x 4-byte words / 3D float vectors
13,
15, // GCC (libstdc++) & MSVC SSO capacity limit (15 chars + null)
16, // 128-bit vector register boundary (SSE/AVX)
18, 19,

// --- Clang SSO limit & Object sizes ---
22, // Clang (libc++) SSO capacity limit (22 chars + null)
23, // Clang heap spill boundary (first byte to trigger dynamic allocation); Folly fbstring SSO capacity
24, // LLVM's libc++: sizeof(std::string) 3x 8-byte; Folly fbstring SSO capacity
25, 27, 29,

// --- Extended SSO & 256-bit Vector Limits ---
31, // jemalloc 32-byte class boundary
32, // AVX2: 256-bit vector register boundary; MSVC: sizeof(std::string) 4x 8-byte
38,
40, // Common loop unrolling (5x 8-byte words)
46, 47, 48, // Alignments around 48 bytes (tcmalloc size class / MSVC heap spill)
49, // Just over 48 bytes (forces allocator to bump to 64-byte class)
55,

// --- CPU Cache Line Boundaries (64 Bytes) ---
63, // Just under cache line (fits entirely within one line)
64, // Exactly one L1 cache line (64 bytes)
66, // Just over cache line (spills into second cache line)
72,

// --- Allocator Bin / Bucket Transitions (jemalloc & tcmalloc) ---
79, // Just under 80-byte allocator bucket
80, // 80-byte allocation class boundary
95, // Just under 96-byte allocator bucket
107, 114,

// --- Dual Cache Line & Medium Keys (Scaling exponentially at ~1.2x) ---
127, // Just under 2 cache lines
128, // Exactly 2 cache lines (AVX-512 register size)
137, 165, 198, 237, // Geometric steps mapping allocator classes (144, 176, 208)
256, // 4 cache lines / 256-byte allocator boundary
285, 342, 410, 492, 591, 709, 851,

// --- Large Keys & Page Boundaries ---
1'021, // Max payload size fitting inside a 1KB allocator block with a null-terminator
1'024, // Exactly 1KB (half a page block step for typical modern slab allocators)
1'225, 1'470, 1'764, 2'116, 2'540, 3'048, 3'657,
4'096 // Exactly one x86/ARM64 virtual page (often triggering direct mmap)
};

// Table/chart consistency rule: kReadmeSizes (README tables) must be a subset of
Expand Down Expand Up @@ -159,7 +219,10 @@ void RegisterAlgo() {
hash128->Arg(size);
}
}
benchmark::RegisterBenchmark("BmHash64Latency<" + name + ">", BmHash64Latency<Algo>)->Arg(16)->Arg(64)->Arg(1'024);
auto* const latency = benchmark::RegisterBenchmark("BmHash64Latency<" + name + ">", BmHash64Latency<Algo>);
for (const int size : sizes) {
latency->Arg(size);
}
}

// Registers benchmarks for every descriptor in the central algo::AllAlgorithms
Expand Down
10 changes: 8 additions & 2 deletions mbo/hash/measurements/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,14 @@ bundle against the committed charts, reading the machine list back from the
README's manifest.

The pipeline steps are also usable individually: `run` (perf -> canonical JSON),
`bundle` (pack a run), `tables` / `plot` (render from any canonical JSON -
`tar xzOf <bundle>.tgz results.json` pulls one out), `publish`, `verify`.
`bundle` (pack a run), `tables` / `plot` (render from any canonical JSON _or_ a
bundle `.tgz` directly - given positionally or via `--results`/`--bundle`),
`publish`, `verify`. `plot --kind` selects the curves: `throughput` (the 64- and
128-bit one-shot charts, default), `latency` (the mixed-length latency curve, now
that it sweeps the full size set), or `all`. `plot --scale` selects the axes:
`log-log` (default - ns spans ~4 decades, so a linear y crushes the fast
algorithms) or `linear-log` (linear y, log x, to read absolute ns gaps in a
narrow range); x is always log since lengths are sampled geometrically.

## SMHasher3 quality (`smhasher`)

Expand Down
Loading
Loading