Skip to content

Remove per-macroblock allocations from the lossy WebP encoder - #3192

Merged
JimBobSquarePants merged 2 commits into
mainfrom
js/webp-encoder-allocations
Sep 14, 2026
Merged

JimBobSquarePants merged 2 commits into
mainfrom
js/webp-encoder-allocations

Conversation

@JimBobSquarePants

Copy link
Copy Markdown
Member

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

The VP8 encoder allocated on every macroblock. The three mode pickers in QuantEnc created new Vp8ModeScore and Vp8Residual objects, RecordResiduals created a residual per macroblock in every stat pass, and macroblock analysis created a Vp8Histogram object per candidate mode. The GC collected all of it, so this was churn, not a leak, but it showed up as gigabytes of allocation in profiles of large batch jobs (see #3191).

Changes:

  • Vp8EncIterator now owns three Vp8ModeScore scratch accumulators and one Vp8Residual. QuantEnc reuses them. The intra16 picker hands the rejected accumulator back to the iterator, so the existing reference swap still avoids a copy.
  • OneStatPass creates one residual per pass and passes it to RecordResiduals.
  • Vp8Histogram is now a struct. The two alternating histograms in MbAnalyzeBestIntra4Mode live on the stack, and Merge takes the accumulator by reference.

Output is unchanged. All 435 WebP tests pass.

EncodeWebp lossy benchmark, Png/Bike.png, .NET 10:

Mean Allocated Gen0 / Gen1 per 1000 ops
Before 56.02 ms 14.77 MB 900 / 400
After 53.42 ms 468.79 KB 0 / 0

The VP8 mode pickers created new Vp8ModeScore and Vp8Residual objects for
every macroblock, and RecordResiduals created a residual per macroblock in
every stat pass. Hold the scratch accumulators on Vp8EncIterator, create the
stat-pass residual once per pass, and reuse them.
Macroblock analysis created a Vp8Histogram object for every candidate mode
and a two-element array for every 4x4 block. The type only holds two ints,
so make it a value type, keep the two alternating histograms on the stack,
and merge by reference.
@JimBobSquarePants JimBobSquarePants added this to the v5.0.0 milestone Sep 14, 2026
@JimBobSquarePants
JimBobSquarePants merged commit 89483d1 into main Sep 14, 2026
12 checks passed
@JimBobSquarePants
JimBobSquarePants deleted the js/webp-encoder-allocations branch September 14, 2026 12:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant