Skip to main content

max / audiofiles

Add benchmarks/ with the first clean ingest baseline Saves AF_BENCH_JSON output durably, which nothing did before: baselines landed in /tmp and were gone by the next session. Convention is <mode>-<date>-<short-sha>.json, with the README carrying the naming rule and, more usefully, the rules for when two runs are comparable at all. The baseline: 2,241 files, 1.42 GB in 13.3s, 168.4 files/s, 106.5 MB/s. Machine and drive verified idle first, fresh vault, T9 exFAT at 10 Gb/s. Also lands the pre-fanout control alongside it. The two were taken back to back on the identical corpus with a fresh vault each, and read 168.3 against 168.4 files/s, so hash-prefix sharding costs nothing measurable at this size. That is the wanted result: the layout change buys the 289k-file case without charging small libraries for it. Recording the control matters because this nearly read as a regression. The older figure in wiki af-benchmarks is 204 files/s over 1,761 files, where this run counts 2,241, the difference being the 480 files of the four format arms that live under samples/ and get picked up by the ingest walk. Comparing across those two corpora would have shown a 17% loss that does not exist. Nothing in the JSON records corpus contents, only the drive, so the README states the rule plainly: rebuild the old commit and re-measure both arms rather than trusting a stored file across a corpus change.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-29 21:30 UTC
Signed with PGP, not checked
Commit: 0aadac69210329018d38ccc238e1d53268665cc0
Parent: 1258f9e
3 files changed, +163 insertions, -0 deletions
@@ -1,0 +1,75 @@
1 + # Benchmark baselines
2 +
3 + Saved `AF_BENCH_JSON` output from `audiofiles-bench`, kept so a later run has
4 + something to be compared against. Small, self-describing files: each records the
5 + metrics plus the drive, filesystem, USB link speed and RAM it was taken on.
6 +
7 + ## Naming
8 +
9 + ```
10 + <mode>-<YYYY-MM-DD>-<short-sha>.json
11 + ```
12 +
13 + `mode` is the bench mode (`ingest`, `analysis`, `accuracy`). The sha is the commit
14 + the binary was built from. Both matter: a filename alone should say what was
15 + measured and which code produced it.
16 +
17 + ## Producing one
18 +
19 + Set `AF_BENCH_JSON` and copy the result in. The corpus lives outside the repo
20 + (gitignored, per-machine), so paths differ per box.
21 +
22 + ```
23 + AF_BENCH_CORPUS=/media/max/T9/af-corpus/samples \
24 + AF_BENCH_VAULT=/media/max/T9/af-bench-fresh \
25 + AF_BENCH_BATCH=400 AF_BENCH_ANALYZE=600 \
26 + AF_BENCH_JSON=/tmp/run.json \
27 + cargo run --release -p audiofiles-bench -- ingest
28 + ```
29 +
30 + Diffing two runs:
31 +
32 + ```
33 + diff <(jq -S . benchmarks/ingest-2026-07-29-1258f9e.json) <(jq -S . /tmp/run.json)
34 + ```
35 +
36 + ## What makes a run comparable
37 +
38 + Only against another run on the same machine, the same drive, and the **same
39 + corpus contents**. All three are part of the measurement, and only the first two
40 + are recorded in the file.
41 +
42 + The corpus one has already bitten. The 2026-07-29 baselines here read 2,241 files
43 + where the earlier figure in wiki `af-benchmarks` read 1,761, because the four
44 + 120-file format arms added for the per-format decode section landed inside
45 + `samples/` and the ingest walk picks them up. Aggregate throughput came out 168
46 + files/s against the older 204, which looks like a 17% regression and is not one:
47 + it is a different set of files, weighted differently. An A/B of the same corpus
48 + across the two commits showed the change cost nothing (see below).
49 +
50 + Rules that follow from that:
51 +
52 + - **Take a baseline with the machine and drive idle.** Import is I/O bound. A run
53 + taken while a dataset was downloading to the same drive read 40.7 files/s
54 + against 204 for the same corpus, with no code change.
55 + - **Use a fresh vault path.** Pointing at an existing scratch vault measures
56 + dedup, not import, because the store finds the blobs already there.
57 + - **Do not compare across corpora.** If the corpus changed, re-measure both arms
58 + rather than reasoning about the delta.
59 + - **A/B across commits by rebuilding, not by trusting an old file.** Checking out
60 + the older commit and running it back to back on the same corpus is a few
61 + minutes and removes every confound at once.
62 +
63 + ## Current files
64 +
65 + | file | what |
66 + |---|---|
67 + | `ingest-2026-07-29-785fbeb.json` | Pre-fanout control: flat blob store root. |
68 + | `ingest-2026-07-29-1258f9e.json` | The baseline. Hash-prefix sharded store. |
69 +
70 + Taken back to back, machine and drive idle, identical corpus and batch size, a
71 + fresh vault each. They come out 168.3 against 168.4 files/s and 106.5 MB/s each,
72 + so sharding the blob directory costs nothing measurable at this size. That is the
73 + expected result rather than a disappointing one: at 2,241 blobs a flat directory
74 + was never the problem. The layout change is for the 289k-file case, where the flat
75 + root lost about 90% of its throughput.
@@ -1,0 +1,44 @@
1 + {
2 + "mem_total_mb": 15270.0,
3 + "metrics": {
4 + "analysis_files": 600,
5 + "analysis_files_per_sec": 145.8,
6 + "analysis_persist_s": 0.06,
7 + "count_star_ms": 0.0,
8 + "import_bytes": 1417761488,
9 + "import_files": 2241,
10 + "import_files_per_sec": 168.4,
11 + "import_mb_per_sec": 106.5,
12 + "import_throughput_drift_pct": 5.3,
13 + "search_bpm_ms": 0.15,
14 + "search_class_ms": 0.11,
15 + "search_text_ms": 0.53,
16 + "search_unfiltered_ms": 1.71
17 + },
18 + "mode": "ingest",
19 + "peak_rss_mb": 550.5859375,
20 + "schema": 1,
21 + "storage": {
22 + "corpus": {
23 + "disk": "sda",
24 + "fs_type": "exfat",
25 + "model": "PSSD T9",
26 + "mount_point": "/media/max/T9",
27 + "path": "/media/max/T9/af-corpus/samples",
28 + "rotational": false,
29 + "source": "/dev/sda1",
30 + "usb_speed_mbps": 10000
31 + },
32 + "vault": {
33 + "disk": "sda",
34 + "fs_type": "exfat",
35 + "model": "PSSD T9",
36 + "mount_point": "/media/max/T9",
37 + "path": "/media/max/T9/af-bench-baseline",
38 + "rotational": false,
39 + "source": "/dev/sda1",
40 + "usb_speed_mbps": 10000
41 + }
42 + },
43 + "unix_time": 1785359714
44 + }
@@ -1,0 +1,44 @@
1 + {
2 + "mem_total_mb": 15270.0,
3 + "metrics": {
4 + "analysis_files": 600,
5 + "analysis_files_per_sec": 139.4,
6 + "analysis_persist_s": 0.06,
7 + "count_star_ms": 0.0,
8 + "import_bytes": 1417761488,
9 + "import_files": 2241,
10 + "import_files_per_sec": 168.3,
11 + "import_mb_per_sec": 106.5,
12 + "import_throughput_drift_pct": -13.9,
13 + "search_bpm_ms": 0.14,
14 + "search_class_ms": 0.1,
15 + "search_text_ms": 0.41,
16 + "search_unfiltered_ms": 1.25
17 + },
18 + "mode": "ingest",
19 + "peak_rss_mb": 504.265625,
20 + "schema": 1,
21 + "storage": {
22 + "corpus": {
23 + "disk": "sda",
24 + "fs_type": "exfat",
25 + "model": "PSSD T9",
26 + "mount_point": "/media/max/T9",
27 + "path": "/media/max/T9/af-corpus/samples",
28 + "rotational": false,
29 + "source": "/dev/sda1",
30 + "usb_speed_mbps": 10000
31 + },
32 + "vault": {
33 + "disk": "sda",
34 + "fs_type": "exfat",
35 + "model": "PSSD T9",
36 + "mount_point": "/media/max/T9",
37 + "path": "/media/max/T9/af-bench-preflat",
38 + "rotational": false,
39 + "source": "/dev/sda1",
40 + "usb_speed_mbps": 10000
41 + }
42 + },
43 + "unix_time": 1785359806
44 + }