Skip to content

Commit 8e0e112

Browse files
matte1782claude
andcommitted
fix(w47): Day 2 R2 hostile review — percentile interpolation, input validation
- M1: percentile() now uses linear interpolation (was ceil-based, P99=max for small N) - M2: benchADC/benchTraining guard iterations/nCodes/nQueries >= 1 - m6: fixed misleading "partial sort" comment in computeGroundTruth - m7: removed unnecessary Float32Array copy in encode loop Hostile review: R2 CONDITIONAL GO → all fixes applied + Playwright verified Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 051f157 commit 8e0e112

1 file changed

Lines changed: 17 additions & 5 deletions

File tree

tests/wasm/pq_bench.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,21 @@ function generateSyntheticVectors(n, dims, seed) {
6161
}
6262

6363
/**
64-
* Compute percentile from sorted array.
64+
* Compute percentile from sorted array using linear interpolation.
65+
* For small sample sizes (< 100), P99 will interpolate between the
66+
* highest values rather than always returning the max.
6567
* @param {number[]} sorted - sorted array of values
6668
* @param {number} p - percentile (0-100)
6769
* @returns {number}
6870
*/
6971
function percentile(sorted, p) {
70-
const idx = Math.ceil((p / 100) * sorted.length) - 1;
71-
return sorted[Math.max(0, idx)];
72+
if (sorted.length === 0) return 0;
73+
if (sorted.length === 1) return sorted[0];
74+
const rank = (p / 100) * (sorted.length - 1);
75+
const lo = Math.floor(rank);
76+
const hi = Math.ceil(rank);
77+
const frac = rank - lo;
78+
return sorted[lo] + frac * (sorted[hi] - sorted[lo]);
7279
}
7380

7481
/**
@@ -89,6 +96,9 @@ function percentile(sorted, p) {
8996
* @returns {Promise<object>} timing results
9097
*/
9198
async function benchADC(nCodes, nQueries, iterations, opts = {}) {
99+
if (iterations < 1) throw new Error('iterations must be >= 1');
100+
if (nCodes < 1) throw new Error('nCodes must be >= 1');
101+
if (nQueries < 1) throw new Error('nQueries must be >= 1');
92102
const dims = opts.dims || 768;
93103
const m = opts.m || 8;
94104
const ksub = opts.ksub || 256;
@@ -118,7 +128,7 @@ async function benchADC(nCodes, nQueries, iterations, opts = {}) {
118128
const allCodes = new Uint8Array(nCodes * m);
119129
for (let i = 0; i < nCodes; i++) {
120130
const vec = data.subarray(i * dims, (i + 1) * dims);
121-
const code = codebook.encodePq(new Float32Array(vec));
131+
const code = codebook.encodePq(vec);
122132
allCodes.set(code, i * m);
123133
}
124134
const encMs = performance.now() - encStart;
@@ -181,6 +191,8 @@ async function benchADC(nCodes, nQueries, iterations, opts = {}) {
181191
* @returns {Promise<object>} timing results
182192
*/
183193
async function benchTraining(nVectors, iterations, opts = {}) {
194+
if (iterations < 1) throw new Error('iterations must be >= 1');
195+
if (nVectors < 1) throw new Error('nVectors must be >= 1');
184196
const dims = opts.dims || 768;
185197
const m = opts.m || 8;
186198
const ksub = opts.ksub || 256;
@@ -256,7 +268,7 @@ function computeGroundTruth(vectors, n, dims, queries, k) {
256268
dists[i] = sum;
257269
}
258270

259-
// Find top-k (partial sort via selection)
271+
// Find top-k via full sort (acceptable: ground truth cost excluded from timing)
260272
const indices = Array.from({ length: n }, (_, i) => i);
261273
indices.sort((a, b) => dists[a] - dists[b]);
262274
results.push(indices.slice(0, k));

0 commit comments

Comments
 (0)