Skip to content

Replace CHOOSE_IMPLEMENTATION/CHOOSE_SVE_IMPLEMENTATION macros with constexpr dispatch tables - #1004

Draft
dor-forer wants to merge 2 commits into
mainfrom
spaces-refactor-r1-residual-dispatch
Draft

Replace CHOOSE_IMPLEMENTATION/CHOOSE_SVE_IMPLEMENTATION macros with constexpr dispatch tables#1004
dor-forer wants to merge 2 commits into
mainfrom
spaces-refactor-r1-residual-dispatch

Conversation

@dor-forer

Copy link
Copy Markdown
Collaborator

Summary

  • Replaces the C1..C64/CASES4..64/CHOOSE_IMPLEMENTATION/SVE_CASE/CHOOSE_SVE_IMPLEMENTATION macro pyramid in implementation_chooser.h with two small C++20 helpers in the new spaces/functions/residual_dispatch.h: dispatch_by_residual<Dist, Chunk>(dim, selector) and dispatch_by_sve_residual<Dist>(dim, chunk, selector). Both build a static constexpr lookup table once, at compile time, and index it with dim % Chunk — same O(1) shape as the switch it replaces, but the chunk size is stated exactly once (the helper's own template argument), so it can't drift from a separately-written modulus at the call site the way two independent literals could.
  • All ~130 Choose_* function bodies across all 21 spaces/functions/*.cpp files (x86: SSE/SSE3/SSE4/AVX/AVX2 family/F16C/AVX512 family; ARM: NEON family, SVE/SVE2/SVE_BF16) converted. implementation_chooser.h/implementation_chooser_cleanup.h deleted — zero remaining references anywhere.
  • Choose_*/GetDistFunc* names and signatures are unchanged, so tests/unit/test_spaces.cpp and tests/benchmark/spaces_benchmarks/bm_spaces.h needed zero changes. Every leaf SIMD kernel is untouched.
  • This is R1 of a two-part refactor (board-reviewed plan in SPACES-REFACTOR-PLAN.md/SPACES-REFACTOR-LOG.md in this repo). R2 (replacing the #ifdef-nested runtime feature-check cascades in IP_space.cpp/L2_space.cpp with a declarative dispatch table) is stacked on top of this in a separate PR.

Verification

  • Compile: all 15 x86 files independently recompiled standalone with their exact per-tier release flags (-Werror -Wall -std=gnu++20 -O2, e.g. -mavx512f -mavx512bw -mavx512vl -mavx512vnni for AVX512F_BW_VL_VNNI.cpp) — clean, no warnings. The 7 ARM files (NEON/SVE families) converted with the same pattern but could not be compiled on this dev box (no aarch64/SVE cross-toolchain available) — needs a real ARM build + test pass before merge.
  • Unit tests: make unit_test CTEST_ARGS="-R Spaces"1442/1442 passed, 0 failed, on this machine's real detected x86 tiers.
  • Binary size (measured on the AVX2 BF16_IP/BF16_L2 conversion): per-wrapper code drops from 528 to 19 bytes — the 32-way jump-table landing blocks disappear in favor of one indexed load into a 256-byte static constexpr table. Net code+data across a converted pair: ~48% smaller. Section-by-section breakdown confirms the whole-.o size increase some builds show is 100% DWARF debug info for the longer template/lambda names (stripped in production); real shipped .text+.rodata+.data.rel.ro shrank.
  • Dispatch speed: a synthetic microbenchmark (same switch-shape vs same array-shape, random dim per call to defeat branch-predictor warmup) shows ~5.8x faster dispatch selection (15.6ns → 2.7ns/call). Real and reproducible, but this call happens exactly once per index build (components_factory.h:23), never per query — confirmed via bm_spaces.h's own structure, which calls Choose_* once in benchmark setup, outside the timed loop. So: no effect on distance-kernel query throughput (expected and confirmed — kernels are byte-identical), real effect on the one-time dispatch cost.

Test plan

  • make unit_test CTEST_ARGS="-R Spaces" — 1442/1442 passed
  • Standalone compile of all 15 x86 functions/*.cpp with exact release flags
  • Real ARM/NEON/SVE compile + test pass (no aarch64 toolchain on this dev box)
  • make check-format / CI format check

…onstexpr dispatch tables

The switch-generating macro pyramid in implementation_chooser.h is replaced by
dispatch_by_residual/dispatch_by_sve_residual (residual_dispatch.h): a small
C++20 helper that builds a static constexpr array of the same per-residual
kernel specializations via a templated-lambda selector, indexed once by
dim % chunk instead of switched on. The chunk size is stated exactly once (as
the helper's own template argument) so it can't drift from a separately
written modulus the way two independent literals could.

Choose_*/GetDistFunc names and signatures are unchanged, so no other file
needed to change - test_spaces.cpp and bm_spaces.h were verified to compile
against this unchanged. Every leaf SIMD kernel is untouched.

Measured on this AVX2 build: per-wrapper machine code drops from 528 to 19
bytes (the 32-way jump-table landing blocks disappear in favor of one indexed
load); net code+data across a converted pair is ~48% smaller. A synthetic
dispatch microbenchmark (random dim per call, to avoid branch-predictor
warmup) shows ~5.8x faster selection - real, but this call happens once per
index build, never per query, so it has no effect on distance-kernel
throughput.
Explain what each template argument at a dispatch_by_residual/
dispatch_by_sve_residual call site actually represents (Dist, Chunk,
Selector), with a worked example, instead of describing the helper only
in terms of the now-deleted CHOOSE_IMPLEMENTATION macro it replaced.
Also fixes a pre-existing clang-format drift in the lambda bodies.
@dor-forer

Copy link
Copy Markdown
Collaborator Author

✅ `make check-format` passes clean on this branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant