Skip to content

perf(scripts): add a device-free PNG crop benchmark - #2505

Merged
thymikee merged 2 commits into
t3code/optimize-crop-performancefrom
perf/png-crop-benchmark
Sep 12, 2026
Merged

perf(scripts): add a device-free PNG crop benchmark#2505
thymikee merged 2 commits into
t3code/optimize-crop-performancefrom
perf/png-crop-benchmark

Conversation

@thymikee

@thymikee thymikee commented Sep 11, 2026

Copy link
Copy Markdown
Member

Summary

Adds pnpm bench:png-crop, which runs both crop pipelines over the same bytes as PNG worker jobs in one process, so content, deflate stream, thread and machine stay fixed. Both sides pay the job round trip the shipped command pays:

  • whole-image — the previous crop: one job decodes the whole capture to RGBA, the box rows are copied out of that bitmap, a second job encodes the box.
  • region — the shipped crop: one job reads the box's rows and encodes them.

Neither writes a file; publishing the artifact is the same work on both sides, so it would only dilute the ratio, and each row reports the encoded byte length instead. Neither side reads less of the capture either: a deflate stream has to be inflated to its end, so both inflate the whole compressed image, and the region path inflates it into a buffer sized for every filtered row. What stops at the box is the pixel work.

Six generated captures at iPhone, Android-phone and iPad resolutions cross three crop-box shapes (card, header, control) that match what --crop-on resolves.

pnpm bench:png-crop -- --rounds 5 --file ./real-capture.png

The corpus is generated, so a run costs seconds with no device. Every corpus entry prints its own compressed size against a real capture's, which is how a corpus that stopped resembling a device becomes visible instead of flattering. Real captures join the same table with --file, and they decide the verdict.

12 files, 581 gross lines: ten new tooling files plus one package script and one gate registration; no production path is touched.

Validation

Validated at the stack head 03baaa59c3, on base 71a4386108 from #2504. This layer carries no production code; the parser fix and the live Android crop evidence landed in the lower layer.

pnpm check:affected --run runs 368 test files / 2,350 tests plus format, oxlint, typecheck, layering, di-seams and the eager-closure budget suite clean. mutation-model and production-exports fail identically at origin/main in this worktree (63 export findings at the merge base, none in png-* or scripts/png-crop-benchmark) — pre-existing, unrelated. pnpm check:fallow --base origin/main reports no issues in 29 changed files. scripts/png-crop-benchmark/*.test.ts (12 tests) run in unit-core via the chore(gates) commit here.

Numbers quoted in the README come from 7-round runs at this commit over three real captures: 2.6x to 5.6x faster and up to 2.16x smaller on a 245 kB iOS UI capture, 1.78x to 2.52x faster and up to 1.31x smaller on a 3 MB iOS photo capture, and a wash on time (0.98x to 1.64x) with a crop up to ~13% larger on a noisy 1.4 MB Android screencap. The README states that losing case rather than averaging it away, and says to check your own captures with --file before reading a win or a loss into any number here.

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-12 18:23 UTC

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.50 MB 4.50 MB +91 B
Package (unpacked) 4.50 MB 4.50 MB +91 B
Package (download) 1.32 MB 1.32 MB +33 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 28.2 ms 26.5 ms -1.7 ms
CLI --help 79.5 ms 74.7 ms -4.8 ms

@thymikee

thymikee commented Sep 11, 2026

Copy link
Copy Markdown
Member Author

The benchmark compares different execution paths at acabc0f: wholeImage uses synchronous decode/copy/encode on the main thread, while region calls the shipped worker-backed cropPngFile. The actual predecessor also used PNG worker jobs, so this is neither a matched kernel benchmark nor a before/after measurement of the shipped paths. Run both algorithms at the same execution boundary, or benchmark the real predecessor, then update the reported speedups. Clarify that the new reader still inflates the complete stream; only scanline reconstruction stops at the crop boundary.

Coverage also fails the five eager-import budget checks inherited from #2504. Resolve those in the lower PR before merging this layer.

This head now conflicts with its updated base. Reconcile it with #2504 before merging.

@thymikee
thymikee force-pushed the perf/png-crop-benchmark branch 2 times, most recently from 46cfcd1 to 910b2d4 Compare September 12, 2026 06:05
@thymikee
thymikee force-pushed the perf/png-crop-benchmark branch from 910b2d4 to 4749932 Compare September 12, 2026 06:11
@thymikee

Copy link
Copy Markdown
Member Author

Commit identity: this layer's head is 4749932304, on base 4d849a3a3a from #2504. The base conflict is reconciled — the two benchmark commits are rebased onto the updated lower layer.

Matched boundary. Both pipelines now run as PNG worker jobs over the same bytes in one process. whole-image is the real predecessor rather than a main-thread stand-in: decodePngAsync of the whole capture, copy the box rows out of that bitmap, encodePngAsync of the box, so it pays the two job round trips the shipped command used to pay. region pays the one job it pays (cropPngBytesAsync). Neither writes a file any more; publication is identical work on both sides and would only dilute the ratio, so each row reports the encoded byte length instead.

Updated numbers, 7 rounds at that boundary over three real captures, replacing the 1.9x to 5.1x this PR quoted, which was measured across the mismatched boundary:

  • 245 kB iOS UI capture: 2.6x to 5.6x faster, crop 1.67x to 2.16x smaller.
  • 3.0 MB iOS photo capture: 1.78x to 2.52x faster, crop 1.04x to 1.31x smaller.
  • 1.4 MB Android screencap: 0.98x to 1.64x, a wash, and the crop comes out up to ~13% larger there.

The README says that losing case plainly instead of averaging it away: inflating and reconstructing that much entropy dominates both pipelines, and the None-filter writer cannot beat the general writer's filter search on content that noisy.

Scope wording. A "What is actually saved" section says neither pipeline reads less of the file, because a deflate stream has to be inflated to its end; what stops at the box is reconstruction and allocation, plus one worker round trip and the RGBA re-encode. The reader's module doc says the same thing where the code is.

Eager-import budgets were inherited from #2504 and are resolved there at 4d849a3a3a: the crop fallback in png-worker-client became a dynamic import inside the worker-unavailable branch, and the 590-check eager-closure suite passes on this head. The Tested at <sha> sentence is out of the description as well, per the request on #2504.

@thymikee
thymikee force-pushed the perf/png-crop-benchmark branch from 4749932 to 3b121f7 Compare September 12, 2026 12:11
@thymikee

Copy link
Copy Markdown
Member Author

The benchmark correction is sound at 3b121f7: both pipelines now use the shipped worker boundaries, and the documentation states the full inflate cost and the reported noisy-capture tradeoff. No code findings in this layer; current-head checks pass and its base conflict is resolved. Merge still depends on resolving the remaining parser finding and readiness requirements in #2504.

`pnpm bench:png-crop` runs the whole-image pipeline and the shipped region crop
over the same bytes in one process, so the comparison holds the capture content,
the deflate stream, and the machine fixed. The corpus is generated, which keeps a
run at seconds with no device; real captures join the same table via `--file`, and
each corpus entry prints its compressed size so an unrealistic corpus is visible.

The README records what the measurements said, including the case the encoder
policy loses: `None` on every scanline is faster everywhere but writes about 1.7x
more bytes than a filtered encoding on smooth low-frequency content.
Registers scripts/png-crop-benchmark/*.test.ts so the timing summary that the
report is built from stays covered without a device lane.
@thymikee
thymikee force-pushed the perf/png-crop-benchmark branch from 3b121f7 to 03baaa5 Compare September 12, 2026 16:26
@thymikee

Copy link
Copy Markdown
Member Author

Nothing to change in this layer, so it is unchanged: the head is 03baaa59c3 on base 71a4386108 from #2504, which carries the trailing-IEND parser fix and the live Android crop evidence (Pixel 7 CI emulator, android-helper backend, two crops at 436x71 and 585x51, magick compare -metric AE = 0 against ImageMagick's crop of the same capture).

The description's validation section now names these heads instead of an earlier push. Local 667 tests across packages/capture-kit/src/png-*, this benchmark and the eager-closure suite pass on this head, and check:fallow --base origin/main reports no issues in 30 changed files. The two open items are both in #2504: the parser finding, and the budget decision (exception or three-layer split).

@thymikee

Copy link
Copy Markdown
Member Author

The benchmark layer remains code-clean at 03baaa5; its logical patch is unchanged by the rebase onto the corrected PNG reader. There are no conflicts. The remaining prerequisite is #2504’s size decision. The current iOS failure is the alert-replacement test reaching its deadline before activation, which appears unrelated to this tooling layer and still needs to pass before merge.

@thymikee
thymikee added this pull request to stack #2536 September 12, 2026 18:21
@thymikee
thymikee merged commit 9aa6465 into main Sep 12, 2026
20 of 21 checks passed
@thymikee
thymikee deleted the perf/png-crop-benchmark branch September 12, 2026 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant