Skip to content

crypto: optimize SubtleCrypto dispatch hot paths#62756

Open
panva wants to merge 2 commits intonodejs:mainfrom
panva:webcrypto-refactors
Open

crypto: optimize SubtleCrypto dispatch hot paths#62756
panva wants to merge 2 commits intonodejs:mainfrom
panva:webcrypto-refactors

Conversation

@panva
Copy link
Copy Markdown
Member

@panva panva commented Apr 15, 2026

This improves two hot paths in Web Cryptography


crypto: optimize normalizeAlgorithm hot path

Replace O(n) for...in loop with case-insensitive StringPrototypeToUpperCase comparisons per algorithm with 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.


crypto: reduce allocations in WebIDL dictionary converter

Replace SafeArrayIterator with index-based loop in the inner function returned by createDictionaryConverter, avoiding iterator object creation on every dictionary conversion.

Replace object spread ({ ...opts, context }) with explicit property assignment when passing opts to member converters. This avoids allocating a full spread copy per dictionary member.

@panva panva 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-bot nodejs-github-bot added the needs-ci PRs that need a full CI run. label Apr 15, 2026
Replace O(n) for...in loop with case-insensitive
StringPrototypeToUpperCase comparisons per algorithm
with 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 panva force-pushed the webcrypto-refactors branch from c23467e to 62dec56 Compare April 15, 2026 13:05
@panva panva marked this pull request as draft April 15, 2026 13:16
Replace SafeArrayIterator with index-based loop in the inner function
returned by createDictionaryConverter, avoiding iterator object creation
on every dictionary conversion.

Replace object spread (`{ ...opts, context }`) with explicit property
assignment when passing opts to member converters. This avoids allocating
a full spread copy per dictionary member.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
@panva panva force-pushed the webcrypto-refactors branch from 62dec56 to 8cfb8ef Compare April 15, 2026 13:27
@panva panva marked this pull request as ready for review April 15, 2026 13:31
@panva
Copy link
Copy Markdown
Member Author

panva commented Apr 15, 2026

'use strict';

const common = require('../common.js');

const bench = common.createBenchmark(main, {
  op: [
    'normalizeAlgorithm-string',
    'normalizeAlgorithm-dict',
    'webidl-dict',
  ],
  n: [1e6],
}, { flags: ['--expose-internals'] });

function main({ n, op }) {
  const { normalizeAlgorithm } = require('internal/crypto/util');

  switch (op) {
    case 'normalizeAlgorithm-string': {
      // String shortcut + null dictionary (cheapest path).
      bench.start();
      for (let i = 0; i < n; i++)
        normalizeAlgorithm('SHA-256', 'digest');
      bench.end(n);
      break;
    }
    case 'normalizeAlgorithm-dict': {
      // Object input with a dictionary type (no BufferSource members).
      const alg = { name: 'ECDSA', hash: 'SHA-256' };
      bench.start();
      for (let i = 0; i < n; i++)
        normalizeAlgorithm(alg, 'sign');
      bench.end(n);
      break;
    }
    case 'webidl-dict': {
      // WebIDL dictionary converter in isolation.
      const webidl = require('internal/crypto/webidl');
      const input = { name: 'AES-GCM', iv: new Uint8Array(12) };
      const opts = { prefix: 'test', context: 'test' };
      bench.start();
      for (let i = 0; i < n; i++)
        webidl.converters.AeadParams(input, opts);
      bench.end(n);
      break;
    }
  }
}
                           main (avg)    optimized (avg)    improvement
normalizeAlgorithm-string   1,261,089        2,392,303        +89.7%
normalizeAlgorithm-dict       397,734          551,582        +38.7%
webidl-dict                   901,744          959,238         +6.4%
  • normalizeAlgorithm-string: normalizeAlgorithm('SHA-256', 'digest') — string shortcut with null dictionary, exercises the O(1) lookup fast path
  • normalizeAlgorithm-dict: normalizeAlgorithm({ name: 'ECDSA', hash: 'SHA-256' }, 'sign') — object input with HashAlgorithmIdentifier member, exercises dict iteration with pre-computed keys
  • webidl-dict: webidl.converters.AeadParams(input, opts) — WebIDL dictionary converter in isolation, exercises the index loop and explicit property assignment

@panva panva changed the title crypto: optimize SubtleCrypto hot paths crypto: optimize SubtleCrypto dispatch hot paths Apr 15, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 15, 2026

Codecov Report

❌ Patch coverage is 88.33333% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.61%. Comparing base (ed05549) to head (8cfb8ef).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/crypto/util.js 87.50% 6 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #62756      +/-   ##
==========================================
- Coverage   89.69%   89.61%   -0.08%     
==========================================
  Files         706      706              
  Lines      218127   217978     -149     
  Branches    41734    41652      -82     
==========================================
- Hits       195651   195345     -306     
- Misses      14400    14564     +164     
+ Partials     8076     8069       -7     
Files with missing lines Coverage Δ
lib/internal/crypto/webidl.js 98.42% <100.00%> (+<0.01%) ⬆️
lib/internal/crypto/util.js 96.02% <87.50%> (+0.50%) ⬆️

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

@panva panva added the request-ci Add this label to start a Jenkins CI on a PR. label Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commit-queue-rebase Add this label to allow the Commit Queue to land a PR in several commits. crypto Issues and PRs related to the crypto subsystem. needs-ci PRs that need a full CI run. request-ci Add this label to start a Jenkins CI on a PR. webcrypto

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants