|
1 | 1 | # Ops Calibration Benchmark |
2 | 2 |
|
3 | | -Measures warm probe time per `(index_kind, query_kind)` at multiple N values and derives |
4 | | -the `per_ns` constants used in `CostFactors` (`src/planner/calibration.rs`). |
| 3 | +Measures the 9 `CostFactors` ns/op constants used by the query planner's cost model |
| 4 | +(`src/planner/cost.rs`, `src/planner/calibration.rs`). |
5 | 5 |
|
6 | | -## What it measures |
7 | | - |
8 | | -| Constant | Index | Query kind | Dataset | |
9 | | -|----------|-------|------------|---------| |
10 | | -| `kdtree_knn_ns` | KDTree | kNN | clustered points | |
11 | | -| `kdtree_range_ns` | KDTree | range | clustered points | |
12 | | -| `rtree_knn_ns` | RTree | kNN | polygons | |
13 | | -| `rtree_range_ns` | RTree | range | polygons | |
14 | | -| `grid_range_ns` | Grid | range | uniform points | |
15 | | -| `scan_ns_per_item` | BruteForce | kNN | uniform points | |
16 | | -| `build_ns_per_item` | KDTree | — | clustered points | |
17 | | - |
18 | | -Brute-force scan is only measured up to `--brute-max-n` (default 100,000) since it grows |
19 | | -as `Q×N` and becomes impractical at large N. |
| 6 | +## Method |
20 | 7 |
|
21 | | -All data is generated over `[0, 1]²`. Range queries use a `0.1 × 0.1` bbox (selectivity ≈ 0.01). |
22 | | -kNN uses k=5. Each timing is the median of `--runs` repetitions. |
| 8 | +- Each dataset size generates synthetic data with numpy (uniform or clustered points) or |
| 9 | + `shapely.box` (polygons), then wraps it in a `SpatialFrame`/`Engine`. |
| 10 | +- Build cost times only `engine.build_index()` on a fresh engine. Probe cost times the repeated queries against a pre-built index. |
| 11 | +- The constant is `time / workload_term`, where the term is read directly off the cost |
| 12 | + model formula in `cost.rs` (e.g. `Q * N` for `scan_ns_per_item`). |
| 13 | +- This ratio is computed at multiple dataset sizes and we return the median as the suggested value. |
23 | 14 |
|
24 | | -## How constants are derived |
25 | 15 |
|
26 | | -Each measured warm time `T` (ms) is inverted through the cost model formula: |
| 16 | +## What it measures |
27 | 17 |
|
28 | | -| Constant | Formula | |
29 | | -|----------|---------| |
30 | | -| `kd_knn_ns`, `rt_knn_ns` | `T × 1e6 / (Q × (log₂N + k))` | |
31 | | -| `kd_range_ns`, `rt_range_ns` | `T × 1e6 / (Q × (log₂N + sel×N))` | |
32 | | -| `grid_range_ns` | `T × 1e6 / (Q × sel×N)` | |
33 | | -| `scan_ns_per_item` | `T × 1e6 / (Q × N)` | |
34 | | -| `build_ns_per_item` | `T × 1e6 / (N × log₂N)` | |
| 18 | +| Constant | Op timed | Dataset | Term | |
| 19 | +|---|---|---|---| |
| 20 | +| `scan_ns_per_item` | brute-force kNN, no index | uniform points | `Q * N` | |
| 21 | +| `grid_build_ns_per_item` | build | uniform points | `N` | |
| 22 | +| `kdtree_build_ns_per_item` | build | clustered points | `N * log2(N)` | |
| 23 | +| `rtree_build_ns_per_item` | build | polygons | `N * log2(N)` | |
| 24 | +| `grid_range_ns` | range probe | uniform points | true hit total | |
| 25 | +| `kdtree_range_ns` | range probe | clustered points | `Q * log2(N)` + true hit total | |
| 26 | +| `rtree_range_ns` | range probe | polygons | `Q * log2(N)` + true hit total | |
| 27 | +| `kdtree_knn_ns` | kNN probe | clustered points | `Q * (log2(N) + k)` | |
| 28 | +| `rtree_knn_ns` | kNN probe | polygons | `Q * (log2(N) + k)` | |
35 | 29 |
|
36 | 30 | ## Running |
37 | 31 |
|
38 | 32 | ``` |
39 | 33 | uv run python -m bench.ops |
40 | 34 | ``` |
41 | 35 |
|
42 | | -Optional flags: |
| 36 | +Flags: |
43 | 37 |
|
44 | 38 | ``` |
45 | | ---sizes N [N ...] dataset sizes to sweep (default: 10000 100000 500000 1000000) |
46 | | ---queries Q queries per timing call (default: 500) |
47 | | ---runs R repetitions per measurement, median taken (default: 3) |
48 | | ---brute-max-n N skip brute-force above this N (default: 100000) |
| 39 | +--runs R timing repetitions per measurement, the minimum is taken (default: 3) |
| 40 | +--seed S RNG seed for data and query generation (default: 42) |
49 | 41 | ``` |
| 42 | + |
| 43 | +## Example Output |
| 44 | + |
| 45 | +``` |
| 46 | +Suggested CostFactors (copy into src/planner/calibration.rs): |
| 47 | +
|
| 48 | +Brute Force |
| 49 | + scan_ns_per_item: 100.80, |
| 50 | +
|
| 51 | +Points |
| 52 | + grid_build_ns_per_item: 84.74, |
| 53 | + kdtree_build_ns_per_item: 4.36, |
| 54 | + grid_range_ns: 211.38, |
| 55 | + kdtree_range_ns: 81.19, |
| 56 | + kdtree_knn_ns: 148.71, |
| 57 | +
|
| 58 | +Polygons |
| 59 | + rtree_build_ns_per_item: 74.17, |
| 60 | + rtree_range_ns: 176.16, |
| 61 | + rtree_knn_ns: 1299.75, |
| 62 | +
|
| 63 | +elapsed: 20.3 s peak RSS: 268.9 MiB |
| 64 | +``` |
0 commit comments