|
1 | | -# ClickBench privacy benchmark |
| 1 | +# ClickBench AS Benchmark |
2 | 2 |
|
3 | | -`pac_clickhouse_benchmark` runs the ClickHouse `hits` (ClickBench) queries through the |
4 | | -`privacy` extension and measures runtime (and, where applicable, utility) for several |
5 | | -privacy mechanisms against a non-private baseline. |
6 | | - |
7 | | -## Running |
| 3 | +`as_clickbench_benchmark` compares plain DuckDB with handwritten stochastic-aggregation forms of the aggregate |
| 4 | +ClickBench queries. Queries without an aggregate are skipped. |
8 | 5 |
|
9 | 6 | ```bash |
10 | | -# build first: GEN=ninja make |
11 | | -build/release/extension/privacy/pac_clickhouse_benchmark [options] |
| 7 | +cmake --build build/release --target as_clickbench_benchmark |
| 8 | +build/release/extension/privacy/as_clickbench_benchmark \ |
| 9 | + --config benchmark/configs/utility/as_clickbench_graviton.json --dry-run |
| 10 | +build/release/extension/privacy/as_clickbench_benchmark \ |
| 11 | + --config benchmark/configs/utility/as_clickbench_graviton.json |
12 | 12 | ``` |
13 | 13 |
|
14 | | -Options: |
15 | | - |
16 | | -| Flag | Meaning | |
17 | | -|---|---| |
18 | | -| `--micro` | Use a smaller dataset (`clickbench_micro.db`) for quick testing | |
19 | | -| `--db <path>` | DuckDB database file | |
20 | | -| `--queries <dir>` | Directory with `create.sql`, `load.sql`, `queries.sql`, `setup.sql` | |
21 | | -| `--out <csv>` | Output CSV path (auto-named if omitted) | |
22 | | -| `--modes <csv>` | Which mechanisms to measure: subset of `pac,dp_standard,dp_elastic,dp_sass` (default: all four) | |
23 | | -| `--run-naive` | Also run the explicit sample-table-join "naive" variants for the sampling mechanisms in `--modes` | |
24 | | - |
25 | | -The non-private **`baseline`** always runs regardless of `--modes`. |
26 | | - |
27 | | -Each selected DP/PAC mechanism runs **once per query** at a single operating point — |
28 | | -`dp_epsilon = 1.0` and the inferred "perfect" sensitivity bound (no epsilon/sensitivity sweep). |
29 | | -The DP mechanisms use a common ClickBench support set for reporting; queries outside that |
30 | | -intersection are recorded as failed for all DP modes instead of letting one mechanism report a |
31 | | -larger successful-query set than another. |
32 | | - |
33 | | -## Modes in the output `mode` column |
34 | | - |
35 | | -- `baseline` — plain DuckDB, no privacy. |
36 | | -- `PAC`, `dp_standard`, `dp_elastic`, `dp_sass` — the four mechanisms (vectorized rewrite path). |
37 | | -- `naive_pac`, `naive_dp` — explicit-SQL sample-and-aggregate baselines (only with `--run-naive`). |
38 | | - |
39 | | -## Naive queries |
40 | | - |
41 | | -The naive variants live in: |
42 | | - |
43 | | -- `clickbench_naive_pac_queries/qNN.sql` |
44 | | -- `clickbench_naive_dp_queries/qNN.sql` |
45 | | - |
46 | | -They exist only for the **privatizable aggregate** queries (`SUM`/`COUNT`/`AVG` over `hits` |
47 | | -grouping on non-protected, non-`UserID` columns). Queries with no naive form — `COUNT(DISTINCT |
48 | | -UserID)`, `MIN`/`MAX`, row dumps (`ORDER BY … LIMIT`), and `GROUP BY UserID` — have no file and |
49 | | -are skipped automatically by the runner. |
50 | | - |
51 | | -Both families share one skeleton and differ only in the terminal UDF: |
52 | | - |
53 | | -- **PAC naive**: 128 random sub-samples per privacy unit (`WHERE random() < 0.5`), per-sub-sample |
54 | | - `SUM`/`COUNT`/`AVG`, then `pac_aggregate(answers, counts, mi, k)` → mean-of-sub-samples + PAC noise. |
55 | | -- **DP naive**: 64 lanes, each privacy unit assigned to ~8 of them via an **approximate** SQL hash |
56 | | - (`hash(UserID, lane_id) % 64 < 8`), per-lane answers rescaled by `64/8`, then |
57 | | - `dp_aggregate(answers, counts, eps, delta, lanes, lower, upper)` → clipped median of the 64 |
58 | | - lane answers + smooth-sensitivity Laplace noise (wraps `dp_smooth_median_noise`; the `counts` |
59 | | - argument is present only for signature symmetry with `pac_aggregate` and is unused by the |
60 | | - median path). |
| 14 | +If the configured database does not exist, the runner downloads `hits.parquet` and loads `hits`. It records the median |
| 15 | +of five hot runs in `query,mode,m,median_ms` format. Use `benchmark/configs/sanity/as_clickbench_m_sweep.json` for a |
| 16 | +focused `m=64,512` check. |
61 | 17 |
|
62 | | -> **The DP naive variant is a runtime / structural baseline, not a utility-faithful one.** Its |
63 | | -> lane assignment is approximate (the real `DpSampleHash` 6-bit-chunk bitmask is C++-only and |
64 | | -> cannot be reproduced in plain SQL), and its domain bounds are hand-set generous constants |
65 | | -> rather than the per-query inferred bounds used by the real `dp_sass` rewrite. The runner records |
66 | | -> only **timing + success** for naive rows; their numeric values are expected to be noise-dominated |
67 | | -> and should not be compared to the vectorized path for accuracy. |
| 18 | +See [repro.md](../../repro.md) for the reported machine settings and plotting command. |
0 commit comments