Experiment 210: Reused BLOB batch workload and object-identity prototype

Date: 2026-07-02T10:17:08Z

Status: Rejected

Direction:parameter-encoding-and-binding, measurement-system

Benchmark Run: focused

benchmark/experiments/batch_param_flatten.dart

BLOB modes plus

benchmark/profile/run_tracelite_profile.dart

blob_merge_rounds; see Results.

Archive:archive/exp-210

Problem

The current batch encoder already writes Uint8List values directly into the

single native parameter arena. That means ordinary BLOB params do not have the

same removable temporary utf8.encode() allocation that made the wide-text

packing wins in exp 125, exp 126, exp 149, and exp 150 worthwhile.

One narrower case still looked plausible: large executeBatch matrices that

reuse the same Uint8List object many times. The baseline copies that payload

into the native arena once per BLOB parameter even though the batch runner binds

and steps synchronously while the arena remains alive. A duplicate parameter

could safely point at the first native copy for the lifetime of that batch.

This idea had prior local evidence, but it was closed unmerged because it was

marshal-only and lacked a workload/profile lane proving the shape mattered

end-to-end. This run revisits it with the missing workload evidence.

Hypothesis

For repeated large-BLOB batches, object-identity reuse should reduce

parameter-marshalling time enough to improve a traced executeBatch workload

without changing public API semantics.

Reject if identity tracking only wins in marshal isolation, if 256-byte payloads

are below the tracking threshold, or if the Tracelite profile lane does not

improve once SQLite storage work is included.

Approach

The final branch keeps the measurement surface and rejects the runtime

prototype. The archived prototype changed allocateBatchParams only:

at least 4096 saved bytes;

offset;

native arena bytes.

The final branch adds durable workload coverage:

--blob-bytes, --blob-mode=fresh|reused, and

--measure=execute|marshal;

1 KB BLOB executeBatch lane.

Results

Full numbers are recorded in

benchmark/results/2026-07-02T10-17-08Z-exp210-reused-blob-batch-workload.md.

Marshal isolation

Median ms per allocateBatchParams / freeParamBuffer cycle, 1 KB reused

BLOB payloads:

ShapeBaselineCandidateDelta
1,000 rows x 8 params0.2970.210-29.3%
10,000 rows x 8 params3.5632.031-43.0%
1,000 rows x 20 params0.7320.510-30.3%
10,000 rows x 20 params10.4945.642-46.2%

The implementation does remove marshal work when the payload is large enough.

The threshold matters: a 256-byte sweep before narrowing the guard made the

candidate slower on the target 10,000 x 8 lane, so the broad 64-byte version

is not viable.

Execute sanity check

The same 1 KB reused-BLOB workload with SQLite execution included was mixed:

ShapeBaselineCandidateDelta
1,000 rows x 8 params14.87014.982+0.8%
10,000 rows x 8 params217.580197.346-9.3%
1,000 rows x 20 params38.06137.225-2.2%
10,000 rows x 20 params526.400540.200+2.6%

Once SQLite storage work is included, the marshal win is no longer a clean

end-to-end win.

Tracelite profile

The new blob_merge_rounds profile lane rejects the runtime prototype:

MetricBaselineCandidateDelta
blob_merge_rounds executeBatch p50909 us1177 us+29.5%
blob_merge_rounds executeBatch p903091 us3628 us+17.4%
writer_sqlite_us during BLOB lane642,217 us792,149 us+23.3%

The trace lane is the load-bearing result because it was the missing evidence

from the earlier local prototype. The candidate gets faster only when the

benchmark isolates marshalling; the workload that includes writer and SQLite

costs gets slower.

Decision

Rejected. Do not keep the runtime identity-map prototype.

The reusable contribution is the workload coverage. Future BLOB parameter

experiments can now separate marshal-only wins from traced workload wins:

batch_param_flatten.dart --cell-mode=blob --blob-mode=reused isolates the

encoder, and blob_merge_rounds shows whether that encoder signal survives the

writer/SQLite path.

Reopen only if a production trace shows reused large-BLOB batches hot enough

that marshal-only savings matter end-to-end, or if a cheaper plan avoids the

per-cell identity-map lookup overhead.

Test plan

benchmark/experiments/batch_param_flatten.dart

benchmark/profile/workloads.dart

benchmark/profile/run_tracelite_workloads.dart test/database_test.dart`

'wide executeBatch reuses repeated large blob parameters'`