Experiment 065: JSON1 bulk shapes re-evaluation

Date: 2026-04-16

Status: Rejected (confirms experiment 031's original conclusion)

Problem

Experiment 031 rejected SQLite's built-in json_group_array(json_object(...)) approach for bulk JSON output as "mixed and workload-specific." That evaluation was against the pre-Ryu/pre-SWAR serializer which took ~5.70ms for 10k rows.

After experiments 041 (Ryu) and 043 (SWAR+LUT), our custom selectBytes path is 44% faster at ~3.07ms for 10k rows. This could change the comparison:

This experiment re-measures the comparison with the updated custom path.

Approach

Pure measurement, no code changes. Benchmark two paths for the same JSON output:

Results

Consistent across 3 runs:

RowsCustom (selectBytes)JSON1 (select)Custom/JSON1 Ratio
1000.07ms0.10-0.11ms1.46-1.59x faster
1,0000.42-0.43ms0.43-0.48ms1.03-1.12x faster
10,0004.18-4.49ms4.18-4.44ms0.98-1.05x (tied)

Our custom path is strictly better or equal to JSON1 across all sizes. No scenario where JSON1 wins meaningfully.

At small result sizes, the custom path wins by ~50% โ€” FFI overhead dominates, and JSON1 doesn't save any crossings since the select() call still has to marshal a single-row result back.

At medium sizes (1k rows), the custom path is marginally faster by 3-12%.

At large sizes (10k rows), the two are essentially tied. SQLite's tight C loop for JSON building is efficient, but our SWAR+Ryu-optimized serializer matches it.

Decision

Rejected. Round 1's optimizations widened the gap between our custom path and JSON1 to the point where JSON1 is never a win. Experiment 031's original rejection stands and is now even stronger.

This is a "negative result with conviction" โ€” the path is closed. Future evaluations of custom vs JSON1 aren't needed unless SQLite's JSON1 implementation changes substantially or our custom path regresses.