Skip to content

node: replace nbytes with simdutf for base64 decoding#6497

Merged
danlapid merged 2 commits into
cloudflare:mainfrom
Aaron-json:node-api/simdutf-base64-decode
Apr 20, 2026
Merged

node: replace nbytes with simdutf for base64 decoding#6497
danlapid merged 2 commits into
cloudflare:mainfrom
Aaron-json:node-api/simdutf-base64-decode

Conversation

@Aaron-json

Copy link
Copy Markdown

This implementation aims to implement using simdutf for decoding base64 in the Node Buffer api which has been a TODO for a while.

I use the option "base64_default_or_url_accept_garbage" which behaves exactly like Nodejs. All tests for the buffer api pass successfully.

I also wrote a simple micro-benchmark to avoid regressions. The benchmark is only a sanity check and is unlikely to represent actual base64 strings in use. The results are below:

Original implementation using nbytes

Name Iterations Time
Small String 10,000 6 ms
Mixed URL-Safe 10,000 48 ms
Large String 10,000 322 ms

simdutf implementation

Name Iterations Time
Small String 10,000 7 ms
Mixed URL-Safe 10,000 26 ms
Large String 10,000 323 ms

We get a tiny regression in decoding strings with non mixed alphabets but a huge speedup in decoding strings with mixed alphabets. It is useful to note that the time shown here is the total for all runs. In this case (where we run 10,000 iterations) a ~1ms regression is actually 0.0001ms for every individual run. The benchmark code and the config are included below

import { Buffer } from 'node:buffer';

export default {
  async test() {
    const smallStr = 'SGVsbG8gV29ybGQ=';
    const mixedStr = 'SGVsbG8_SGVsbG8-'.repeat(1000);
    const largeStr = 'SGVsbG8gV29ybGQ='.repeat(100000);

    function runBench(name, fn, iters) {
      // Warmup runs. (also helps V8 optimize this path)
      for (let i = 0; i < 1000; i++) fn();

      const start = Date.now();
      for (let i = 0; i < iters; i++) fn();
      const end = Date.now();

      console.log(
        `${name.padEnd(20)} | ${iters.toLocaleString().padStart(10)} iters | ${end - start} ms`
      );
    }

    console.log('Running Base64 Benchmarks');
    runBench('Small String', () => Buffer.from(smallStr, 'base64'), 10_000);
    runBench('Mixed URL-Safe', () => Buffer.from(mixedStr, 'base64'), 10_000);
    runBench('Large String', () => Buffer.from(largeStr, 'base64'), 10_000);
  },
};
using Workerd = import "/workerd/workerd.capnp";

const config :Workerd.Config = (
  services = [
    (name = "test", worker = .testWorker)
  ]
);

const testWorker :Workerd.Worker = (
  modules = [
    (name = "bench-buffer.js", esModule = embed "bench-buffer.js")
  ],
  compatibilityDate = "2024-04-03",
  compatibilityFlags = ["nodejs_compat"],
);

@Aaron-json Aaron-json requested review from a team as code owners April 3, 2026 23:34
@github-actions

github-actions Bot commented Apr 3, 2026

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@Aaron-json

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

github-actions Bot added a commit that referenced this pull request Apr 3, 2026
Comment thread src/workerd/api/node/buffer.c++
@Aaron-json

Copy link
Copy Markdown
Author

It's been a while since this was approved. is there anything else I need to do to get this finalized?

@danlapid danlapid merged commit 9d1f25d into cloudflare:main Apr 20, 2026
36 of 39 checks passed
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.

4 participants