Experiment 149: Six-parameter batch packing

Date: 2026-06-08

Status: In Review

Direction:parameter-encoding-and-binding

Benchmark Run: Tracelite profile, exp149-six-param-batch-packing

Problem

Experiments 125 and 126 justified direct payload packing for large wide batches,

while experiment 146 rejected broadening the ASCII fast path to small/narrow

batch writes. That left a middle shape uncovered: app-like merge batches that

are not 20-parameter ORM rows, but still bind enough repeated text parameters

for temporary UTF-8 list allocation to matter.

The Tracelite profile workload from exp 143 exposed exactly that shape. Its

merge_rounds phase repeatedly executes 100-row INSERT OR REPLACE batches

against a six-parameter row:

On current main, that path still used the generic batch encoder because the

ASCII fast path required at least eight parameters and 8,192 total bound

parameters.

Hypothesis

Lowering the guarded ASCII batch-packing threshold from 8 / 8192 to 6 / 600

should improve the profile merge-round path without reviving exp 146's rejected

small/narrow broadening. The new guard admits repeated six-parameter batches

where Tracelite shows material writer work, while keeping one-, two-, and

three-parameter batch writes on the generic path.

Reject if the profile merge-round improvement does not reproduce, if focused

batch correctness fails, or if the implementation requires new API surface or

complex shape-specialization code.

Approach

Created two resqlite worktrees from origin/main at

841e3623f0c05cb978624ebdcd84b9dfe2c36eaf:

Candidate patch:

 -const int _asciiBatchMinParamCount = 8; -const int _asciiBatchMinTotalParamCount = 8192; +const int _asciiBatchMinParamCount = 6; +const int _asciiBatchMinTotalParamCount = 600; 

A focused database test adds a six-column executeBatch case with 100 rows per

batch. The first batch exercises the ASCII fast path, and the second includes a

non-ASCII string plus blobs so the UTF-8 fallback stays covered at the new

threshold.

The integrated Tracelite A/B strict batch scenario was deliberately not used as

the proof for this change: narrow-batch-insert is a two-parameter lane, and

sync-burst is a three-parameter lane below this guard. Exp 146 already showed

that broad small/narrow admission is not justified. This run instead uses the

Tracelite profile workload that actually exposed the six-parameter merge cost.

Results

Paired profile command:

 /Users/dan/Coding/flutter_arm64/bin/cache/dart-sdk/bin/dart run \ benchmark/profile/run_tracelite_profile.dart \ --tracelite-root=/Users/dan/Coding/tracelite \ --dart=/Users/dan/Coding/flutter_arm64/bin/cache/dart-sdk/bin/dart \ --label=exp149-six-param-batch-packing-candidate \ --out-dir=build/tracelite-profile/exp149-six-param-batch-packing-candidate \ --allow-unpinned-tracelite \ --no-graph-data 

Tracelite revision:

 8fbfac7675348fa2a765e3b0c36f3a5a22c67fc0 

Profile artifacts:

build/tracelite-profile/exp149-six-param-batch-packing-baseline/workload-summary.json

build/tracelite-profile/exp149-six-param-batch-packing-candidate/workload-summary.json

build/tracelite-profile/exp149-six-param-batch-packing-candidate/insights.md

Workload C, merge_rounds:

MetricBaselineCandidateDelta
executeBatch p5088 us75 us-14.8%
executeBatch work p5075 us62 us-17.3%
executeBatch p9096 us84 us-12.5%
writer_sqlite_us87,895 us75,947 us-13.6%

Other profile lanes did not move in a way that changes the decision:

Decision

Accept for review.

This is the narrow version of the batch-threshold change that exp 146 was

missing. It does not reopen the rejected small/narrow path; it admits the

specific six-parameter repeated merge shape that Tracelite showed was spending

real writer-side time in SQLite-facing batch execution.

The implementation cost is low: two threshold constants and one focused

correctness test. The existing generic encoder remains the fallback for

non-ASCII text and for narrow batch shapes.

Future Notes

Do not lower this below six parameters without a new workload and a Tracelite

decision artifact that clears the primary gate. Exp 146 remains the guardrail:

small/narrow writes should stay generic unless a real workload proves otherwise.

The current Tracelite suite still lacks a strict policy scenario for the

six-parameter merge-round profile shape. If this path becomes a recurring

product workload, add that as a Tracelite suite scenario instead of reusing

narrow-batch-insert as a proxy.

Validation

benchmark/profile/run_tracelite_profile.dart --label=exp149-six-param-batch-packing-baseline

benchmark/profile/run_tracelite_profile.dart --label=exp149-six-param-batch-packing-candidate