You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Here are my benchmarks
Smth happened with Node.js TextEncoder.encode with recent changes
Also, when doing any benchmarks, it's better to test both ascii and non-ascii modes, separately
E.g. Node.js TextDecoder is significantly faster on ASCII-only input than browsers, but slower on Unicode.
Note: here, all instances are in utf8 mode, just some input is ascii-only.
There was an error in the bench which affected utf8toString, ascii: Buffer rows, fixed that.
It was unrelated to the observed regression though
Also re-ran the benchmarks on charger
That didn't change the observation but changed the numbers a bit
@erikcorry ... Just FYI in case you have any insight here. tl;dr it looks like there's a significant performance regression from the original WriteUtf8 to WriteUtf8V2
Longer term, we can optimize the code that @drives optimized even further, not just for ASCII, but for the general case as well. There is @anonrig's work in the OP, but we will build better support in future releases of simdutf.
(This is not a criticism of @drives's excellent work. Just an indication that we can do even better in the future.)
Ok, that patch is merged. I've been chatting with the v8 people about potential improvements to the general case using simdutf as much as possible and will follow up.
@RafaelGSS Is #183 still relevant?
Also, ascii TextDecoder perf in Node.js already looks like a memcpy with check (I didn't look at impl though)
Non-ascii is 2x slower than Chrome and 2.3 slower than Firefox (ref: #192 (comment))
It couldn't be that the ascii fast path check is slowing down the non-ascii perf that much
If simdutf::validate_ascii has a version to return the max length of ascii prefix instead of true/false (i.e. the length at which it stopped being ascii), then patch by @drcarney66 could be optimized further to also memcpy ascii prefixes, in case if string has a long ascii prefix.
Non-ascii path is slow and could be likely improved ~7x as @anonrig said.
Ideally that also should happen on v8 side (same method), and not in Node.js -- that will fix other things too.
There is still some regression remaining from 22, but not so drastic anymore.
In both asci and non-ascii modes, by ~20-30%.
For TextDecoder:
ascii codepath is quite fast but likely could be faster
Non-ascii codepath is slow. Likely could be likewise improved ~7x.
Likely the logic with ascii prefixes could be also applied there
If simdutf::validate_ascii has a version to return the max length of ascii prefix instead of true/false
Yes:
std::string bad_ascii = "\x20\x20\x20\x20\x20\xff\x20\x20\x20";
simdutf::result res = simdutf::validate_ascii_with_errors(bad_ascii.data(), bad_ascii.size());
if(res.error != simdutf::error_code::SUCCESS) {
std::cerr << "error at index " << res.count << std::endl;
}
But my overall message is that a lot of optimization work will be easier if you just wait a few months for the next simdutf releases. So I recommend waiting a few months before doing complicated work.
FYI: We had regressions for small strings and longer strings that weren't ascii in the one byte case, so I'm merging this. Has similar enough performance to simdutf + memcpy for the ascii case but bails out fast for all other cases.
Still some work to do but we're still working on it.
Activity
ChALkeR commentedon Nov 3, 2025
TextEncoder perf drastically dropped in 25 compared to 22
RafaelGSS commentedon Nov 3, 2025
Also related #183
ChALkeR commentedon Nov 3, 2025
Here are my benchmarks
Smth happened with Node.js
TextEncoder.encodewith recent changesAlso, when doing any benchmarks, it's better to test both ascii and non-ascii modes, separately
E.g. Node.js
TextDecoderis significantly faster on ASCII-only input than browsers, but slower on Unicode.Note: here, all instances are in
utf8mode, just some input is ascii-only.targos commentedon Nov 4, 2025
@ChALkeR Can you share the benchmark code?
ChALkeR commentedon Nov 4, 2025
@targos yes, it also tested a bunch of other impls, I need to detach it from those other impls first to make it stand-alone
For now I confirmed that this is not caused by allocUnsafe regression, as the numbers are still the same on 25.1.0
ChALkeR commentedon Nov 4, 2025
@targos https://gist.github.com/ChALkeR/f49edd556ff3d08638eb3e81b43ce178
Output (cleaned up a bit):
utf8fromString, ascii: TextEncoderregressedlemire commentedon Nov 4, 2025
There is definitively a performance problem. I quickly verified it for myself. @ChALkeR's results are solid.
Let us address it.
targos commentedon Nov 4, 2025
From tests on my side, the main regression happens with version 24.0.0.
ChALkeR commentedon Nov 4, 2025
There was an error in the bench which affected
utf8toString, ascii: Bufferrows, fixed that.It was unrelated to the observed regression though
Also re-ran the benchmarks on charger
That didn't change the observation but changed the numbers a bit
ChALkeR commentedon Nov 4, 2025
nodejs/node@b81697d (nodejs/node#58070) is the cause.
Using deprecated
str->WriteUtf8instead ofstr->WriteUtf8v2restores the perf.str->WriteUtf8:str->WriteUtf8v2:There are more places that use
WriteUtf8V2, are they also degraded perhaps?anonrig commentedon Nov 4, 2025
Yes. We can avoid this by using valueview in the referenced pull request.
jasnell commentedon Nov 5, 2025
We should first try reaching out to the v8 team to see if there is anything we can do about the perf regression with the WriteUtf8V2 API.
@ChALkeR:
I'd be very surprised if they weren't.
ChALkeR commentedon Nov 5, 2025
This seems to be an overkill when we know that the input width is 1 byte:
https://github.com/nodejs/node/blob/f4145f04518f6da697344029a963e24b95ef4276/deps/v8/src/objects/string.cc#L1109-L1112
Old impl optimizes it here with an
& 0x80check:https://github.com/nodejs/node/blob/f4145f04518f6da697344029a963e24b95ef4276/deps/v8/src/api/api.cc#L5767-L5785
This template is missing a similar
memcpyoptimization forkSourceIsOneByte+ mask check:https://github.com/nodejs/node/blob/f4145f04518f6da697344029a963e24b95ef4276/deps/v8/src/strings/unicode-inl.h#L209-L213
jasnell commentedon Nov 5, 2025
@erikcorry ... Just FYI in case you have any insight here. tl;dr it looks like there's a significant performance regression from the original WriteUtf8 to WriteUtf8V2
4 remaining items
jasnell commentedon Nov 5, 2025
Heh, i think @drcarney66's patch works...
lemire commentedon Nov 5, 2025
Longer term, we can optimize the code that @drives optimized even further, not just for ASCII, but for the general case as well. There is @anonrig's work in the OP, but we will build better support in future releases of simdutf.
(This is not a criticism of @drives's excellent work. Just an indication that we can do even better in the future.)
ChALkeR commentedon Nov 5, 2025
Before patch:
After patch:
The patch fixes the main regression from 22 to 24.
There seems to be some other things going on though, will also take a look later
22 was still faster on other usage:
drcarney66 commentedon Nov 5, 2025
Ok, that patch is merged. I've been chatting with the v8 people about potential improvements to the general case using simdutf as much as possible and will follow up.
ChALkeR commentedon Nov 5, 2025
@jasnell @anonrig does Node.js want to cherry-pick that perhaps?)
RafaelGSS commentedon Nov 5, 2025
Yes. Let's make sure to add lts-watch on the PR (unless manual backport to other V8 lines is needed).
I'm wondering if the fix would be the same for TextDecoder? #183
ChALkeR commentedon Nov 5, 2025
@RafaelGSS Is #183 still relevant?
Also, ascii TextDecoder perf in Node.js already looks like a memcpy with check (I didn't look at impl though)
Non-ascii is 2x slower than Chrome and 2.3 slower than Firefox (ref: #192 (comment))
It couldn't be that the ascii fast path check is slowing down the non-ascii perf that much
RafaelGSS commentedon Nov 5, 2025
I will re-do my benchmarks tonight!
ChALkeR commentedon Nov 5, 2025
For
TextEncoder:simdutf::validate_asciihas a version to return the max length of ascii prefix instead of true/false (i.e. the length at which it stopped being ascii), then patch by @drcarney66 could be optimized further to also memcpy ascii prefixes, in case if string has a long ascii prefix.Ideally that also should happen on v8 side (same method), and not in Node.js -- that will fix other things too.
In both asci and non-ascii modes, by ~20-30%.
For
TextDecoder:lemire commentedon Nov 5, 2025
@ChALkeR
Yes:
But my overall message is that a lot of optimization work will be easier if you just wait a few months for the next simdutf releases. So I recommend waiting a few months before doing complicated work.
jasnell commentedon Nov 5, 2025
I'll have a PR that adds the v8 patch open a bit later today.
anonrig commentedon Nov 6, 2025
I rebased workerd after the optimization. cloudflare/workerd#5448 (comment) is still 30-70% faster for ascii inputs.
jasnell commentedon Nov 6, 2025
I strongly suspect there are additional improvements to be made on the v8 side to reclaim more of the perf loss before we explore further workarounds.
drcarney66 commentedon Nov 19, 2025
FYI: We had regressions for small strings and longer strings that weren't ascii in the one byte case, so I'm merging this. Has similar enough performance to simdutf + memcpy for the ascii case but bails out fast for all other cases.
Still some work to do but we're still working on it.
ChALkeR commentedon Dec 16, 2025
Related: nodejs/node#61041