Skip to content

zlib: pledge input size in async zstdCompress() - #66358

Open
Cherry wants to merge 2 commits into
nodejs:mainfrom
Cherry:zlib-zstd-pledged-src-size
Open

Cherry wants to merge 2 commits into
nodejs:mainfrom
Cherry:zlib-zstd-pledged-src-size

Conversation

@Cherry

@Cherry Cherry commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

We precompress our site's static assets at build time, and compression-webpack-plugin calls zlib.zstdCompress() once per file. On the same files, that was 3-5x slower than zstdCompressSync().

zstdCompressSync() hands zstd the whole input in one ZSTD_e_end call, so zstd infers the size. zstdCompress() writes the input and ends the frame in separate calls, so zstd never learns the size and sizes its tables for an unbounded stream.

This defaults pledgedSrcSize to the input's byte length in zstdCompress(). Async output is now byte-identical to zstdCompressSync():

level before after
3 60ms 60ms
9 300ms 113ms
12 4521ms 194ms
19 9696ms 1474ms

That's 162 JS/CSS/SVG files from our production build (up to ~370 KB each), on Linux x64. Windows shows the same pattern. Single large inputs (1-64 MB) are unchanged apart from the few header bytes that record the size.

An explicit pledgedSrcSize is used as-is. Strings with a custom defaultEncoding aren't pledged, since their byte length depends on that encoding.

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. zlib Issues and PRs related to the zlib module and its compression dependencies. labels Sep 27, 2026
@Cherry
Cherry force-pushed the zlib-zstd-pledged-src-size branch from 0b56e6f to b4a8dbb Compare September 27, 2026 21:18
@codecov

codecov Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.66667% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.36%. Comparing base (a2a064c) to head (b22c547).
⚠️ Report is 34 commits behind head on main.

Files with missing lines Patch % Lines
lib/zlib.js 86.66% 4 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #66358   +/-   ##
=======================================
  Coverage   90.36%   90.36%           
=======================================
  Files         792      792           
  Lines      275386   275462   +76     
  Branches    52775    52791   +16     
=======================================
+ Hits       248843   248918   +75     
+ Misses      16979    16953   -26     
- Partials     9564     9591   +27     
Files with missing lines Coverage Δ
lib/zlib.js 98.12% <86.66%> (-0.31%) ⬇️

... and 42 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@H4ad H4ad left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only found one benchmark that covers zstd (https://github.com/Cherry/node/blob/b4a8dbbc5a6194bd36229ffaa3bf83dcdf14f2e0/benchmark/zlib/pipe.js), might be insteresting to add another one to cover this case (at least level 12, which is fast enough to not be too long for a benchmark)

Comment thread lib/zlib.js
@Cherry

Cherry commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

Added the benchmark - here's some results on my local machine for reference:

Node level input zstdCompress pledged speedup zstdCompressSync
v24.21.0 3 16 KiB 1,257 4,717 3.8x 12,087
v24.21.0 3 256 KiB 1,826 2,356 1.3x 3,772
v24.21.0 12 16 KiB 104 3,394 32.6x 5,796
v24.21.0 12 256 KiB 107 895 8.4x 829
v26.10.0 3 16 KiB 3,002 5,311 1.8x 14,086
v26.10.0 3 256 KiB 2,442 2,488 1.0x 3,897
v26.10.0 12 16 KiB 125 3,523 28.1x 6,791
v26.10.0 12 256 KiB 120 921 7.7x 1,068

@github-actions

This comment was marked as outdated.

Comment thread lib/zlib.js Outdated

@H4ad H4ad left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @nodejs/zlib

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Benchmark GHA (zlib / zstd-): https://github.com/nodejs/node/actions/runs/36421711232

Results

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

Benchmark results:

                                                                               confidence improvement accuracy (*)    (**)    (***)
zlib/zstd-compress.js n=100 inputLen=16384 level=12 method='zstdCompress'             ***    871.14 %      ±60.04% ±79.30% ±102.07%
zlib/zstd-compress.js n=100 inputLen=16384 level=12 method='zstdCompressSync'                 -4.36 %       ±7.22%  ±9.52%  ±12.22%
zlib/zstd-compress.js n=100 inputLen=16384 level=3 method='zstdCompress'                       4.19 %       ±9.19% ±12.11%  ±15.54%
zlib/zstd-compress.js n=100 inputLen=16384 level=3 method='zstdCompressSync'                   0.56 %      ±10.23% ±13.48%  ±17.30%
zlib/zstd-compress.js n=100 inputLen=262144 level=12 method='zstdCompress'            ***    548.69 %      ±40.54% ±53.51%  ±68.79%
zlib/zstd-compress.js n=100 inputLen=262144 level=12 method='zstdCompressSync'                 1.28 %       ±7.94% ±10.47%  ±13.43%
zlib/zstd-compress.js n=100 inputLen=262144 level=3 method='zstdCompress'                      0.44 %       ±9.22% ±12.15%  ±15.59%
zlib/zstd-compress.js n=100 inputLen=262144 level=3 method='zstdCompressSync'                  5.12 %       ±9.17% ±12.09%  ±15.52%

Be aware that when doing many comparisons the risk of a false-positive
result increases. In this case, there are 8 comparisons, you can thus
expect the following amount of false-positive results:
  0.40 false positives, when considering a   5% risk acceptance (*, **, ***),
  0.08 false positives, when considering a   1% risk acceptance (**, ***),
  0.01 false positives, when considering a 0.1% risk acceptance (***)

[!WARNING]
Do not take GHA benchmark results as face value, always confirm them
using a dedicated machine, e.g. Jenkins CI.

zstdCompress() writes its input and ends the frame in separate calls,
so zstd cannot infer the input size the way it does for
zstdCompressSync(), and sizes its tables for an unbounded stream.

Default pledgedSrcSize to the input's byte length. The output is now
identical to zstdCompressSync() and several times faster at higher
levels. An explicit pledgedSrcSize, or a string with a custom
defaultEncoding, keeps the current behavior.

Signed-off-by: James Ross <[email protected]>
@Cherry
Cherry force-pushed the zlib-zstd-pledged-src-size branch from 4b9e7ea to b22c547 Compare September 28, 2026 16:50
@H4ad H4ad added author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. labels Sep 28, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Sep 28, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@H4ad H4ad added the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Sep 29, 2026
@github-actions github-actions Bot removed the resume-ci Add this label to resume the latest eligible Jenkins CI run on a PR with an approving review. label Sep 29, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@H4ad H4ad added commit-queue PRs queued for automated landing through the Commit Queue. commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. labels Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. commit-queue PRs queued for automated landing through the Commit Queue. commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. needs-ci PRs that need a full CI run. zlib Issues and PRs related to the zlib module and its compression dependencies.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants