KNN benchmarks
Compare prediction latency, explicit local worker pools and admitted workspace for the same exhaustive nearest-neighbor search.
For the Batch workload (N=2,048, Q=256, D=16, k=5), uniform prediction falls from 55.967 ms in the original serial implementation to 14.359 ms with four reused workers: 3.90× overall. This includes both implementation improvements and parallel execution.
For distance weighting, the updated serial call takes 55.702 ms and four reused workers take 14.336 ms: 3.89× relative to the same weighted serial policy. These are local measured medians, with spread and complete worker comparisons below.
Recorded 2026-10-30. The updated measurements require the matching development runtime. The original serial implementation is retained as a separate comparison. Source publication does not publish a new runtime or editor package.
Each cell below reports median ± median absolute deviation (MAD), in milliseconds. Repetitions are retained prediction-call observations after excluded warmups. MAD describes the observed spread; it is not a confidence interval. All parallel columns use reused local contexts.
Read the three speedup denominators separately. “Vs new serial” divides the updated serial time by the shown time under the same weighting policy. “Vs 1 worker” divides the reused one-worker time by the shown time, isolating pool scaling. “Vs original serial” compares uniform voting against the preserved original implementation and includes algorithm changes. Values below 1× indicate slower execution.
Uniform and distance weighting are different estimators. Their timing tables do not establish an accuracy improvement, and the original implementation has no distance-weighted or parallel baseline.
Uniform voting
Existing calls retain uniform votes and serial CPU execution. The updated implementation avoids ordering selected neighbors when a uniform prediction needs only their votes.
Batch
N=2,048, Q=256, D=16, k=5; 5 fitted classes.
| Metric | Original serial | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|---|
| Median ± MAD (ms) | 55.967 ± 0.33250 | 55.376 ± 0.47808 | 54.809 ± 0.48250 | 27.863 ± 0.40400 | 14.359 ± 0.048916 |
| Repetitions | 9 | 9 | 9 | 9 | 9 |
| Speedup vs new serial | 0.99× | 1.00× | 1.01× | 1.99× | 3.86× |
| Speedup vs 1 worker | 0.98× | 0.99× | 1.00× | 1.97× | 3.82× |
| Speedup vs original serial | 1.00× | 1.01× | 1.02× | 2.01× | 3.90× |
Wide features
N=4,096, Q=64, D=64, k=5; 5 fitted classes.
| Metric | Original serial | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|---|
| Median ± MAD (ms) | 169.10 ± 2.1057 | 170.15 ± 3.2807 | 164.50 ± 0.42412 | 87.142 ± 2.8195 | 43.961 ± 0.27067 |
| Repetitions | 7 | 7 | 7 | 7 | 7 |
| Speedup vs new serial | 1.01× | 1.00× | 1.03× | 1.95× | 3.87× |
| Speedup vs 1 worker | 0.97× | 0.97× | 1.00× | 1.89× | 3.74× |
| Speedup vs original serial | 1.00× | 0.99× | 1.03× | 1.94× | 3.85× |
Distance weighting
The fitted inverse-distance policy uses the same Euclidean neighbors and explicit query workers. Ordered weighted accumulation preserves the same arithmetic order across worker counts.
Batch
N=2,048, Q=256, D=16, k=5; 5 fitted classes.
| Metric | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|
| Median ± MAD (ms) | 55.702 ± 0.77979 | 54.478 ± 0.35775 | 28.045 ± 0.15767 | 14.336 ± 0.10200 |
| Repetitions | 9 | 9 | 9 | 9 |
| Speedup vs new serial | 1.00× | 1.02× | 1.99× | 3.89× |
| Speedup vs 1 worker | 0.98× | 1.00× | 1.94× | 3.80× |
Wide features
N=4,096, Q=64, D=64, k=5; 5 fitted classes.
| Metric | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|
| Median ± MAD (ms) | 169.73 ± 3.2090 | 164.08 ± 0.59933 | 85.464 ± 0.61808 | 44.049 ± 0.14679 |
| Repetitions | 7 | 7 | 7 | 7 |
| Speedup vs new serial | 1.00× | 1.03× | 1.99× | 3.85× |
| Speedup vs 1 worker | 0.97× | 1.00× | 1.92× | 3.72× |
Additional workloads
Small queries, larger neighbor sets and selecting every fitted row can behave differently. These complete observations remain part of the comparison.
With k=32, distance-weighted four-worker prediction is 2.98× faster than its updated serial policy. When every fitted row is selected, uniform four-worker prediction is 21.32× faster than the original serial implementation. That includes a 10.14× improvement in the updated serial algorithm; four workers contribute 2.10× relative to that updated serial time. The denominators separate implementation changes from pool scaling.
Tiny query: N=32, Q=1, D=4, k=5
| Metric | Original serial | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|---|
| Median ± MAD (ms) | 0.00070800 ± 0.000041000 | 0.00079200 ± 0.000041000 | 0.0071670 ± 0.0017500 | 0.0073330 ± 0.0012500 | 0.021000 ± 0.0047910 |
| Repetitions | 31 | 31 | 31 | 31 | 31 |
| Speedup vs new serial | 1.12× | 1.00× | 0.11× | 0.11× | 0.04× |
| Speedup vs 1 worker | 10.12× | 9.05× | 1.00× | 0.98× | 0.34× |
| Speedup vs original serial | 1.00× | 0.89× | 0.10× | 0.10× | 0.03× |
| Metric | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|
| Median ± MAD (ms) | 0.00095900 ± 0.000041000 | 0.0053750 ± 0.00091600 | 0.0082910 ± 0.0021240 | 0.017125 ± 0.0068750 |
| Repetitions | 31 | 31 | 31 | 31 |
| Speedup vs new serial | 1.00× | 0.18× | 0.12× | 0.06× |
| Speedup vs 1 worker | 5.60× | 1.00× | 0.65× | 0.31× |
Larger k: N=2,048, Q=256, D=16, k=32
| Metric | Original serial | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|---|
| Median ± MAD (ms) | 55.395 ± 0.24417 | 55.540 ± 0.78779 | 53.664 ± 0.18900 | 28.392 ± 0.20646 | 14.391 ± 0.054626 |
| Repetitions | 7 | 7 | 7 | 7 | 7 |
| Speedup vs new serial | 1.00× | 1.00× | 1.03× | 1.96× | 3.86× |
| Speedup vs 1 worker | 0.97× | 0.97× | 1.00× | 1.89× | 3.73× |
| Speedup vs original serial | 1.00× | 1.00× | 1.03× | 1.95× | 3.85× |
| Metric | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|
| Median ± MAD (ms) | 55.987 ± 0.69383 | 54.854 ± 0.51783 | 28.191 ± 0.30167 | 18.797 ± 0.47346 |
| Repetitions | 7 | 7 | 7 | 7 |
| Speedup vs new serial | 1.00× | 1.02× | 1.99× | 2.98× |
| Speedup vs 1 worker | 0.98× | 1.00× | 1.95× | 2.92× |
All neighbors: N=256, Q=256, D=1, k=256
| Metric | Original serial | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|---|
| Median ± MAD (ms) | 1.9744 ± 0.033499 | 0.19479 ± 0.0045010 | 0.20475 ± 0.0010000 | 0.11012 ± 0.0028330 | 0.092625 ± 0.011875 |
| Repetitions | 15 | 15 | 15 | 15 | 15 |
| Speedup vs new serial | 0.10× | 1.00× | 0.95× | 1.77× | 2.10× |
| Speedup vs 1 worker | 0.10× | 1.05× | 1.00× | 1.86× | 2.21× |
| Speedup vs original serial | 1.00× | 10.14× | 9.64× | 17.93× | 21.32× |
| Metric | New serial | 1 worker | 2 workers | 4 workers |
|---|---|---|---|---|
| Median ± MAD (ms) | 2.0483 ± 0.071041 | 2.0332 ± 0.043875 | 1.1706 ± 0.061917 | 0.58092 ± 0.012124 |
| Repetitions | 15 | 15 | 15 | 15 |
| Speedup vs new serial | 1.00× | 1.01× | 1.75× | 3.53× |
| Speedup vs 1 worker | 0.99× | 1.00× | 1.74× | 3.50× |
Tiny queries and pool setup
N=32, Q=1, D=4, k=5. One query supplies one independent lane, even when a context owns four workers. A parallel pool adds scheduling cost here; more requested workers do not create additional query rows.
Cold calls include creating and destroying their pool. Reused calls exclude that setup. These are separately timed modes; their difference is not an isolated measurement of thread startup.
| Prediction call | 1 worker | 2 workers | 4 workers |
|---|---|---|---|
| Reused pool: median ± MAD (ms) | 0.0071670 ± 0.0017500 | 0.0073330 ± 0.0012500 | 0.021000 ± 0.0047910 |
| Create and destroy pool: median ± MAD (ms) | 0.035333 ± 0.0018750 | 0.053083 ± 0.0092910 | 0.084959 ± 0.010374 |
| Prediction call | 1 worker | 2 workers | 4 workers |
|---|---|---|---|
| Reused pool: median ± MAD (ms) | 0.0053750 ± 0.00091600 | 0.0082910 ± 0.0021240 | 0.017125 ± 0.0068750 |
| Create and destroy pool: median ± MAD (ms) | 0.036042 ± 0.0037500 | 0.048750 ± 0.0055840 | 0.079875 ± 0.0099590 |
Workspace memory
The separate native probe checks the exact admission boundary on the recorded 64-bit ABI: each prediction call accepts the reported byte budget and rejects one byte less. The table reports charged workspace in KiB (1 KiB = 1,024 bytes), including result storage, lane buffers and admission metadata.
| Workload / weights | Serial (KiB) | 1 worker (KiB) | 2 workers (KiB) | 4 workers (KiB) |
|---|---|---|---|---|
| Batch / uniform | 34.508 | 34.508 | 66.680 | 131.023 |
| Batch / distance | 34.625 | 34.625 | 66.914 | 131.492 |
| Wide features / uniform | 65.008 | 65.008 | 129.180 | 257.523 |
| Wide features / distance | 65.125 | 65.125 | 129.414 | 257.992 |
On this ABI, charged prediction bytes are 344 + 8Q + P × (136 + 16N + 8C + (distance ? 24C : 0)), with P=1 for serial and P=min(requested workers,Q) for a context. The fixed and lane terms include control metadata. This formula is specific to the recorded ABI and vector capacities.
Charged workspace is not peak process RSS. It excludes fitted model storage, input/host caches, executor internals, thread stacks, allocator overhead and other process memory. The default 64 MiB (67,108,864 bytes) is a cap, not a fixed allocation. More admitted lanes need their own scratch; requests that exceed the budget fail rather than silently reducing requested concurrency.
Both original and updated models retain the same vector-capacity payload: 8ND + 16N bytes in this harness. That is 288 KiB for Batch and 2,112 KiB for Wide. The fitted-state object grows from 96 to 104 bytes on this ABI, including flag padding; the change adds no new N-element model buffer.
The new serial uniform prediction retains the original numerical-buffer payload: 34,856 bytes for Batch and 66,088 bytes for Wide. The charged totals also include control metadata, so comparing a charged total directly with an old payload figure would mix accounting scopes. Distance weighting adds 24C bytes of scratch per active lane: 120 bytes for these five-class cases.
Workspace memory was probed for the two primary workloads only. The other three workloads have timing evidence, with no additional memory measurement presented here.
Determinism and numerical checks
Both uniform and distance modes independently match their own serial results bitwise. Successful labels, probabilities, neighbor indices and distances were compared bit for bit across admitted 1, 2 and 4 worker contexts in the same binary and floating-point environment. The workspace probe checks prediction labels at each exact admitted budget; probabilities, neighbor indices and neighbor distances are compared with serial at the default budget. This does not promise agreement across different platform math libraries.
The recorded validation includes 107427 native checks and 165 language checks, plus sanitizer and independent numerical tests. A 1,100-digit Decimal reference checked 39 weighted cases and 132 probabilities. Maximum observed errors were 2 ULPs for normal outputs and 0 for the tested subnormal outputs; enforced fixture bounds were 4 and 1 ULP respectively. These are tested observations, not a universal correctly-rounded guarantee.
Distance ties retain fitting-row order, and exact computed class-score ties choose the smallest class ID. Only selected exact-zero-distance neighbors vote in a weighted exact-match case. Cancellation returns no partial successful Tensor, and numerical failures report the lowest failing query row after workers join.
Timing scope and reproduction
These are prediction-only native-call times on macOS arm64 with AppleClang 21, Release -O3, matched ThinLTO and strict -fno-fast-math / -ffp-contract=off. Fitting, input creation, independent verification and process startup are outside the primary timer. Cold modes additionally include context creation and destruction. The linked timing report retains raw samples, source/binary identities, commands and exploratory history; this page uses only the five integrated-* primary groups.
The recorded apple-m1 target is a compiler instruction baseline, not identification of the physical CPU model. Worker counts are admitted concurrency, not a claim of pinned or dedicated cores. These local observations do not establish GPU, cluster, other-platform or universal speedups.
Every query still calculates distance to every fitted row. For fixed k, average total work remains O(QND); parallel execution changes elapsed time, not the exhaustive distance count. Workspace grows with active lanes: O(P(N + C) + Q) for prediction. Neither GPU nor cluster KNN is implemented, and these native library kernels do not extend the bounded aner build Tensor/model subset.
See the KNN contract, implementation and validation notes, and complete local execution example. The benchmark driver, native timing harness, and high-precision verification tool are available for independent inspection.
The workspace probe source reproduces the two-workload admission checks. Build it against the same Release Aner headers and libraries with C++20 and the recorded strict floating-point flags; its formula requires the documented ABI and admission for four CPU workers. It verifies prediction budget-minus-one rejection and exact-budget acceptance. All four readouts are also compared with serial at the default budget. It does not collect prediction timings or process RSS.