Uh oh!
There was an error while loading. Please reload this page.
src: avoid allocation in the Size method for BASE64URL and BASE64 - #53550
src: avoid allocation in the Size method for BASE64URL and BASE64#53550lemire wants to merge 16 commits into
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
lemire
commented
Jun 22, 2024
For people who care about history, Size has been expensive since the very beginning (we trace it back to the initial commit by @isaacs in 2013): caseBASE64: {
String::AsciiValue value(str);
data_size = base64_decoded_size(*value, value.length());
break;
} |
This comment was marked as outdated.
This comment was marked as outdated.
for the record, Size() wasn't just doing an unnecessary allocation, it was also returning the wrong size, causing us to allocate a much larger buffer than necessary. str->Length() % 4 <= 1 ? str->Length() / 4 * 3
: str->Length() / 4 * 3 + (str->Length() % 4) - 1)which is only an upper bound, as it doesn't look at the actual data, and so it assumes the worst case (all characters are data characters, i.e. no padding or whitespace). the math looks correct to me. bear in mind that while Size() is allowed to return an upper bound, it is expected to return an exact prediction most of the time. if this is not the case, we do another allocation + memcpy to a new backing store with the actual size. for base64url the prediction is usually correct as it rarely has padding, but for base64 it will miss 2/3 of the time (or always, if the input contains whitespace). |
lemire
commented
Jun 22, 2024
As far as I can tell, Node never returned the exact size. Doing so requires scanning the entire input, checking for characters to discard. |
mildsunrise
commented
Jun 22, 2024
wasn't |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
jasnell
commented
Sep 8, 2024
PR is currently blocked from landing due to unreliable CI |
This comment was marked as outdated.
This comment was marked as outdated.
lemire
commented
Sep 8, 2024
@jasnell Indeed. |
anonrig
commented
Sep 8, 2024
@lemire can you rebase and force-push if you don't mind? |
lemire
commented
Sep 8, 2024
@anonrig I synced. |
This comment was marked as outdated.
This comment was marked as outdated.
lemire
commented
Sep 8, 2024
@anonrig Looks like it is turning green... what did you do???? ❤️ |
anonrig
commented
Sep 8, 2024
lemire
commented
Sep 9, 2024
@anonrig It still won't complete the tests though. |
nodejs-github-bot
commented
Sep 9, 2024
lemire
commented
Sep 9, 2024
@anonrig Stuck. |
anonrig
commented
Sep 9, 2024
It seems that all macOS machines are down/offline at the moment. nodejs/build#3887 |
nodejs-github-bot
commented
Sep 10, 2024
lemire
commented
Sep 10, 2024
@anonrig This will never go through, will it? |
nodejs-github-bot
commented
Sep 11, 2024
aduh95
commented
Sep 17, 2024
This needs a rebase. |
lemire
commented
Sep 17, 2024
anonrig
commented
Sep 17, 2024
This PR avoids calling simdutf on base64 encodings. It is not obsolete! |
lemire
commented
Sep 17, 2024
lemire
commented
Sep 17, 2024
@anonrig It is possible I missed something, if so, let me know. |

For large base64 strings, this PR multiplies the performance of
Buffer.from(..., "base64");by making the Size function simpler and non-allocating.This will reduce the gap with Bun from being over 3x slower to being about only 40% slower for large inputs. For modest inputs, Node.js is still about 2x slower than Bun. Note that both Bun and Node.js use the same underlying library (simdutf) for base64 decoding so any difference is entirely due to the runtime (not to the base64 decoding per se).
Benchmark (from bun):
Node.js 22:
Node.js with this PR:
Bun canary (upcoming release)
To get the Bun results, you need the canary which you may get with
bun upgrade --canary.