Skip to content

crypto: optimize normalizeAlgorithm dispatch hot path - #62756

Closed
panva wants to merge 2 commits into
nodejs:mainfrom
panva:webcrypto-refactors
Closed

crypto: optimize normalizeAlgorithm dispatch hot path#62756
panva wants to merge 2 commits into
nodejs:mainfrom
panva:webcrypto-refactors

Conversation

@panva

@panvapanva commented Apr 15, 2026

Copy link
Copy Markdown
Member

Replace the O(n) case-insensitive algorithm-name scan with an O(1) SafeMap lookup. The map is pre-built at module init alongside kSupportedAlgorithms.

Use null-prototype objects for the generated operation tables and cached dictionary metadata so these internal lookups never consult Object.prototype. This causes a severe performance regression.

Hoist the opts object literal used in normalizeAlgorithm to module level to avoid allocating identical { prefix, context } objects on every call.

Pre-compute ObjectKeys() for simpleAlgorithmDictionaries entries at module init to avoid allocating a new keys array on every normalizeAlgorithm call.


Replace object spread in nested WebIDL conversion options with stable-shape ordinary objects. This keeps hot dictionary and sequence conversion paths from allocating null-prototype spread results.

Apply the same pattern to Web Crypto converter wrappers that override allowResizable or enable [EnforceRange].

@panvapanva added crypto Issues and PRs related to the crypto subsystem. webcrypto commit-queue-rebase Add this label to allow the Commit Queue to land a PR in several commits. labels Apr 15, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/crypto

@nodejs-github-botnodejs-github-bot added the needs-ci PRs that need a full CI run. label Apr 15, 2026
@panva
panvaforce-pushed the webcrypto-refactors branch from c23467e to 62dec56CompareApril 15, 2026 13:05
@panva
panva marked this pull request as draft April 15, 2026 13:16
@panva
panvaforce-pushed the webcrypto-refactors branch from 62dec56 to 8cfb8efCompareApril 15, 2026 13:27
@panva
panva marked this pull request as ready for review April 15, 2026 13:31
@panvapanva changed the title crypto: optimize SubtleCrypto hot pathscrypto: optimize SubtleCrypto dispatch hot pathsApr 15, 2026
@codecov

codecovBot commented Apr 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.84615% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.67%. Comparing base (e0200f2) to head (17a9267).
⚠️ Report is 23 commits behind head on main.

Files with missing linesPatch %Lines
lib/internal/crypto/util.js87.71%6 Missing and 1 partial ⚠️
lib/internal/webidl.js96.87%1 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #62756 +/- ##
==========================================
- Coverage 89.69% 89.67% -0.02% 
==========================================
Files 712 712 Lines 221273 221300 +27 Branches 42392 42386 -6 ==========================================
- Hits 198473 198457 -16 - Misses 14634 14671 +37 - Partials 8166 8172 +6 
Files with missing linesCoverage Δ
lib/internal/crypto/webidl.js99.35% <100.00%> (+0.01%)⬆️
lib/internal/webidl.js99.44% <96.87%> (+<0.01%)⬆️
lib/internal/crypto/util.js96.97% <87.71%> (+0.46%)⬆️

... and 53 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.

@panvapanva added the request-ci Add this label to start a Jenkins CI on a PR. label Apr 15, 2026
@panva
panva requested a review from aduh95April 16, 2026 09:13
@panvapanva added review wanted PRs that need reviews. and removed review wanted PRs that need reviews. labels Apr 19, 2026
@panva
panva marked this pull request as draft April 19, 2026 15:13
@panva
panvaforce-pushed the webcrypto-refactors branch from 8cfb8ef to cc1451bCompareApril 19, 2026 15:32
@panva
panva marked this pull request as ready for review April 19, 2026 15:32
@panva
panvaforce-pushed the webcrypto-refactors branch 2 times, most recently from f98babc to 06e2662CompareApril 20, 2026 08:57
@panva

This comment was marked as outdated.

@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Apr 21, 2026
@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@panvapanva added author ready PRs that have at least one approval, no pending requests for changes, and a CI started. review wanted PRs that need reviews. labels Apr 21, 2026
@panva
panva marked this pull request as draft April 26, 2026 10:03
@panvapanva removed review wanted PRs that need reviews. author ready PRs that have at least one approval, no pending requests for changes, and a CI started. labels May 4, 2026
@panva
panvaforce-pushed the webcrypto-refactors branch from 6ab9ee9 to 47714a5CompareMay 4, 2026 15:58
Replace the O(n) case-insensitive algorithm-name scan
with an O(1) SafeMap lookup. The map is pre-built at
module init alongside kSupportedAlgorithms.
Hoist the opts object literal used in normalizeAlgorithm
to module level to avoid allocating identical
{ prefix, context } objects on every call.
Pre-compute ObjectKeys() for simpleAlgorithmDictionaries
entries at module init to avoid allocating a new keys
array on every normalizeAlgorithm call.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
@panva
panvaforce-pushed the webcrypto-refactors branch from 47714a5 to 292bebdCompareMay 4, 2026 16:36
Replace object spread in nested WebIDL conversion
options with stable-shape ordinary objects. This keeps
hot dictionary and sequence conversion paths from
allocating null-prototype spread results.
Apply the same pattern to Web Crypto converter wrappers
that override allowResizable or enable [EnforceRange].
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
@panvapanva added the commit-queue-rebase Add this label to allow the Commit Queue to land a PR in several commits. label May 4, 2026
@panva

panva commented May 4, 2026

Copy link
Copy Markdown
MemberAuthor

With only the first commit:

https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1844/

 confidence improvement accuracy (*) (**) (***)
misc/webcrypto-webidl.js n=1000000 op='normalizeAlgorithm-dict' *** 64.05 % ±3.60% ±4.82% ±6.34%
misc/webcrypto-webidl.js n=1000000 op='normalizeAlgorithm-string' *** 164.69 % ±2.27% ±3.04% ±3.98%
misc/webcrypto-webidl.js n=1000000 op='webidl-algorithm-identifier-object' -0.43 % ±4.16% ±5.53% ±7.21%
misc/webcrypto-webidl.js n=1000000 op='webidl-algorithm-identifier-string' -1.70 % ±6.44% ±8.57% ±11.15%
misc/webcrypto-webidl.js n=1000000 op='webidl-dict-enforce-range' -1.01 % ±1.65% ±2.21% ±2.90%
misc/webcrypto-webidl.js n=1000000 op='webidl-dict-ensure-sha' 1.23 % ±3.18% ±4.23% ±5.52%
misc/webcrypto-webidl.js n=1000000 op='webidl-dict-null' 0.42 % ±2.46% ±3.27% ±4.26%
misc/webcrypto-webidl.js n=1000000 op='webidl-dict' * -2.32 % ±2.10% ±2.81% ±3.69%

With both commits:

https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1846

 confidence improvement accuracy (*) (**) (***)
misc/webcrypto-webidl.js n=1000000 op='normalizeAlgorithm-dict' *** 194.86 % ±0.88% ±1.17% ±1.52%
misc/webcrypto-webidl.js n=1000000 op='normalizeAlgorithm-string' *** 479.28 % ±6.94% ±9.35% ±12.40%
misc/webcrypto-webidl.js n=1000000 op='webidl-algorithm-identifier-object' 2.99 % ±4.42% ±5.89% ±7.67%
misc/webcrypto-webidl.js n=1000000 op='webidl-algorithm-identifier-string' -3.32 % ±7.10% ±9.44% ±12.29%
misc/webcrypto-webidl.js n=1000000 op='webidl-dict-enforce-range' *** 708.45 % ±8.80% ±11.86% ±15.74%
misc/webcrypto-webidl.js n=1000000 op='webidl-dict-ensure-sha' *** 119.44 % ±2.77% ±3.70% ±4.84%
misc/webcrypto-webidl.js n=1000000 op='webidl-dict-null' * -2.83 % ±2.55% ±3.40% ±4.42%
misc/webcrypto-webidl.js n=1000000 op='webidl-dict' *** 392.81 % ±5.83% ±7.83% ±10.35%
misc/webidl-buffer-source.js n=1000000 input='arraybuffer' -0.79 % ±6.02% ±8.01% ±10.43%
misc/webidl-buffer-source.js n=1000000 input='bigint64array' -1.95 % ±3.61% ±4.80% ±6.26%
misc/webidl-buffer-source.js n=1000000 input='biguint64array' -0.48 % ±5.87% ±7.81% ±10.17%
misc/webidl-buffer-source.js n=1000000 input='buffer' 4.02 % ±6.73% ±8.96% ±11.67%
misc/webidl-buffer-source.js n=1000000 input='dataview' 0.26 % ±4.87% ±6.48% ±8.44%
misc/webidl-buffer-source.js n=1000000 input='float16array' -2.07 % ±5.64% ±7.55% ±9.92%
misc/webidl-buffer-source.js n=1000000 input='float32array' 2.25 % ±4.01% ±5.33% ±6.94%
misc/webidl-buffer-source.js n=1000000 input='float64array' 0.15 % ±4.53% ±6.03% ±7.85%
misc/webidl-buffer-source.js n=1000000 input='int16array' 1.64 % ±6.27% ±8.38% ±11.00%
misc/webidl-buffer-source.js n=1000000 input='int32array' -1.22 % ±3.59% ±4.78% ±6.22%
misc/webidl-buffer-source.js n=1000000 input='int8array' -3.88 % ±3.93% ±5.24% ±6.83%
misc/webidl-buffer-source.js n=1000000 input='uint16array' -1.56 % ±5.58% ±7.44% ±9.70%
misc/webidl-buffer-source.js n=1000000 input='uint32array' 3.82 % ±4.74% ±6.32% ±8.24%
misc/webidl-buffer-source.js n=1000000 input='uint8array' 2.09 % ±3.88% ±5.17% ±6.74%
misc/webidl-buffer-source.js n=1000000 input='uint8clampedarray' 0.57 % ±6.30% ±8.40% ±10.96%
misc/webidl-convert-to-int.js n=1000000 input='clamp' converter='byte' 0.55 % ±3.67% ±4.89% ±6.37%
misc/webidl-convert-to-int.js n=1000000 input='clamp' converter='long long' -2.35 % ±2.78% ±3.71% ±4.84%
misc/webidl-convert-to-int.js n=1000000 input='clamp' converter='octet' -1.07 % ±2.65% ±3.53% ±4.59%
misc/webidl-convert-to-int.js n=1000000 input='clamp' converter='unsigned long' 0.99 % ±2.24% ±2.99% ±3.89%
misc/webidl-convert-to-int.js n=1000000 input='clamp' converter='unsigned short' -0.22 % ±2.16% ±2.88% ±3.75%
misc/webidl-convert-to-int.js n=1000000 input='enforce-range' converter='byte' 0.80 % ±1.63% ±2.17% ±2.82%
misc/webidl-convert-to-int.js n=1000000 input='enforce-range' converter='long long' 0.89 % ±1.62% ±2.16% ±2.82%
misc/webidl-convert-to-int.js n=1000000 input='enforce-range' converter='octet' -0.46 % ±2.35% ±3.13% ±4.09%
misc/webidl-convert-to-int.js n=1000000 input='enforce-range' converter='unsigned long' -0.58 % ±1.98% ±2.63% ±3.43%
misc/webidl-convert-to-int.js n=1000000 input='enforce-range' converter='unsigned short' -0.78 % ±2.30% ±3.06% ±3.98%
misc/webidl-convert-to-int.js n=1000000 input='fractional' converter='byte' 0.05 % ±4.46% ±5.97% ±7.81%
misc/webidl-convert-to-int.js n=1000000 input='fractional' converter='long long' -1.33 % ±2.78% ±3.70% ±4.82%
misc/webidl-convert-to-int.js n=1000000 input='fractional' converter='octet' -0.03 % ±2.22% ±2.95% ±3.84%
misc/webidl-convert-to-int.js n=1000000 input='fractional' converter='unsigned long' -0.08 % ±1.49% ±1.98% ±2.58%
misc/webidl-convert-to-int.js n=1000000 input='fractional' converter='unsigned short' -1.57 % ±4.49% ±6.00% ±7.86%
misc/webidl-convert-to-int.js n=1000000 input='integer' converter='byte' -1.31 % ±3.13% ±4.19% ±5.50%
misc/webidl-convert-to-int.js n=1000000 input='integer' converter='long long' -0.27 % ±2.35% ±3.13% ±4.09%
misc/webidl-convert-to-int.js n=1000000 input='integer' converter='octet' 1.85 % ±3.62% ±4.86% ±6.43%
misc/webidl-convert-to-int.js n=1000000 input='integer' converter='unsigned long' -0.63 % ±3.03% ±4.05% ±5.32%
misc/webidl-convert-to-int.js n=1000000 input='integer' converter='unsigned short' -1.56 % ±3.68% ±4.92% ±6.45%
misc/webidl-convert-to-int.js n=1000000 input='object' converter='byte' -1.79 % ±2.72% ±3.62% ±4.71%
misc/webidl-convert-to-int.js n=1000000 input='object' converter='long long' -0.95 % ±1.99% ±2.65% ±3.44%
misc/webidl-convert-to-int.js n=1000000 input='object' converter='octet' 0.43 % ±2.33% ±3.10% ±4.04%
misc/webidl-convert-to-int.js n=1000000 input='object' converter='unsigned long' 0.29 % ±1.97% ±2.62% ±3.41%
misc/webidl-convert-to-int.js n=1000000 input='object' converter='unsigned short' 0.03 % ±2.52% ±3.36% ±4.38%
misc/webidl-convert-to-int.js n=1000000 input='wrap' converter='byte' 1.40 % ±4.41% ±5.87% ±7.65%
misc/webidl-convert-to-int.js n=1000000 input='wrap' converter='long long' 1.73 % ±1.76% ±2.35% ±3.06%
misc/webidl-convert-to-int.js n=1000000 input='wrap' converter='octet' -3.02 % ±4.19% ±5.57% ±7.25%
misc/webidl-convert-to-int.js n=1000000 input='wrap' converter='unsigned long' -0.10 % ±4.44% ±5.91% ±7.70%
misc/webidl-convert-to-int.js n=1000000 input='wrap' converter='unsigned short' -1.54 % ±4.61% ±6.14% ±7.99%

@nodejs-github-bot

This comment was marked as outdated.

@panvapanva added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label May 4, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@panvapanva added the commit-queue Add this label to land a pull request using GitHub Actions. label May 6, 2026
@nodejs-github-botnodejs-github-bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label May 6, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in e80b5d2...fa17dc2

nodejs-github-bot pushed a commit that referenced this pull request May 6, 2026
Replace the O(n) case-insensitive algorithm-name scan
with an O(1) SafeMap lookup. The map is pre-built at
module init alongside kSupportedAlgorithms.
Hoist the opts object literal used in normalizeAlgorithm
to module level to avoid allocating identical
{ prefix, context } objects on every call.
Pre-compute ObjectKeys() for simpleAlgorithmDictionaries
entries at module init to avoid allocating a new keys
array on every normalizeAlgorithm call.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
nodejs-github-bot pushed a commit that referenced this pull request May 6, 2026
Replace object spread in nested WebIDL conversion
options with stable-shape ordinary objects. This keeps
hot dictionary and sequence conversion paths from
allocating null-prototype spread results.
Apply the same pattern to Web Crypto converter wrappers
that override allowResizable or enable [EnforceRange].
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
@panva
panva deleted the webcrypto-refactors branch May 6, 2026 13:01
aduh95 pushed a commit that referenced this pull request May 19, 2026
Replace the O(n) case-insensitive algorithm-name scan
with an O(1) SafeMap lookup. The map is pre-built at
module init alongside kSupportedAlgorithms.
Hoist the opts object literal used in normalizeAlgorithm
to module level to avoid allocating identical
{ prefix, context } objects on every call.
Pre-compute ObjectKeys() for simpleAlgorithmDictionaries
entries at module init to avoid allocating a new keys
array on every normalizeAlgorithm call.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request May 19, 2026
Replace object spread in nested WebIDL conversion
options with stable-shape ordinary objects. This keeps
hot dictionary and sequence conversion paths from
allocating null-prototype spread results.
Apply the same pattern to Web Crypto converter wrappers
that override allowResizable or enable [EnforceRange].
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request May 19, 2026
Replace the O(n) case-insensitive algorithm-name scan
with an O(1) SafeMap lookup. The map is pre-built at
module init alongside kSupportedAlgorithms.
Hoist the opts object literal used in normalizeAlgorithm
to module level to avoid allocating identical
{ prefix, context } objects on every call.
Pre-compute ObjectKeys() for simpleAlgorithmDictionaries
entries at module init to avoid allocating a new keys
array on every normalizeAlgorithm call.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request May 19, 2026
Replace object spread in nested WebIDL conversion
options with stable-shape ordinary objects. This keeps
hot dictionary and sequence conversion paths from
allocating null-prototype spread results.
Apply the same pattern to Web Crypto converter wrappers
that override allowResizable or enable [EnforceRange].
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Jun 25, 2026
Replace the O(n) case-insensitive algorithm-name scan
with an O(1) SafeMap lookup. The map is pre-built at
module init alongside kSupportedAlgorithms.
Hoist the opts object literal used in normalizeAlgorithm
to module level to avoid allocating identical
{ prefix, context } objects on every call.
Pre-compute ObjectKeys() for simpleAlgorithmDictionaries
entries at module init to avoid allocating a new keys
array on every normalizeAlgorithm call.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Jun 25, 2026
Replace object spread in nested WebIDL conversion
options with stable-shape ordinary objects. This keeps
hot dictionary and sequence conversion paths from
allocating null-prototype spread results.
Apply the same pattern to Web Crypto converter wrappers
that override allowResizable or enable [EnforceRange].
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Jul 30, 2026
Replace the O(n) case-insensitive algorithm-name scan
with an O(1) SafeMap lookup. The map is pre-built at
module init alongside kSupportedAlgorithms.
Hoist the opts object literal used in normalizeAlgorithm
to module level to avoid allocating identical
{ prefix, context } objects on every call.
Pre-compute ObjectKeys() for simpleAlgorithmDictionaries
entries at module init to avoid allocating a new keys
array on every normalizeAlgorithm call.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Jul 30, 2026
Replace object spread in nested WebIDL conversion
options with stable-shape ordinary objects. This keeps
hot dictionary and sequence conversion paths from
allocating null-prototype spread results.
Apply the same pattern to Web Crypto converter wrappers
that override allowResizable or enable [EnforceRange].
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: #62756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs that have at least one approval, no pending requests for changes, and a CI started.commit-queue-rebaseAdd this label to allow the Commit Queue to land a PR in several commits.cryptoIssues and PRs related to the crypto subsystem.needs-ciPRs that need a full CI run.performanceIssues and PRs related to the performance of Node.js.review wantedPRs that need reviews.webcrypto

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@panva@nodejs-github-bot@anonrig