Repository navigation
Write strings to utf-8 in one pass, straight into the pool - #1335
Conversation
|
We can still add the StringView and ASCII test as a faster fast path but then we need to introduce baically NativeString's that exist in a group so either all succeed or all fail with the fallback. And those can only be used for String-only arguments (functions that only take Strings like getHeader, writeHeader) |
|
Wait, no they work on all functions but the strings will need to be parsed out last of all. In a group. |
|
I don't know if you missed it, but #1302 was kind of doing what this PR does, while preventing the worst slow path. |
You are right, I missed #1302, sorry. It is the same idea, and your worst-case check avoids a bad case that #1335 has: when the string is bigger than what is left of the pool, the first write fills the pool and is thrown away (up to 128 KB), then the string is measured and written again. |
|
I'll let @uNetworkingAB decide what he wants to do. |
|
Ah! That's a good one for slow slow path |
|
The drawback of 3x requirement is that you hit the fast path less likely. So if we have 128kb we only get a maximum of 40kb per string. So it becomes more sensitive to lowering the pool size (if you make the pool size 1kb you have almost no room). But it's still a good trade off, most of that pool will never be used and is not even mapped in at all. Ok, yeah let's do it. |
|
The Length() check does not have this drawback: the first write is still tried for every string that can fit, so ASCII keeps the fast path up to all the free pool. It skips only the strings that surely do not fit, with more characters than free bytes. What is left is a non-ASCII string a bit bigger than the free space, and there the waste is at most the free space. |
|
@nigrosimone My PR has a different architecture from what has been merged. #1302 actually do:
|
That's ideal. But your PR is really messy and changes naming of things without any need (allocated gets renamed to needsFree without any motivation). If you want that PR to land, please make it a minimal change that only changes logic, not names. |
I renamed it "needsFree" because its purpose is different from "allocated". I also had to change the pool system from If you have preferred naming just tell me, or you can also edit the PR. |
Follow-up of #1332, with the approach you suggested there.
NativeString wrote every string twice:
Utf8Length()to size it, thenWriteUtf8V2()into the pool. Now it callsWriteUtf8V2()once, straight into what is left of the pool, and advances the offset by the bytes written. If not all characters were processed the string did not fit, and it is measured and written again into malloc'd memory, like before. Before V8 14 it does the same withWriteUtf8()andnchars_ref.tests/strings.jscompares the bytes on the wire for ASCII, Latin-1, BMP, astral, NUL, empty and strings past the pool. It passes on master and on this branch, and fails the two past-the-pool cases on a build that skips the did-it-fit check. It runs in build.yml after smoke.js.Measured on Node 26 win-x64, min of 7 alternating runs, two rounds in reversed order:
req.getHeaderwith a 10 byte key from 66-69 to 59 ns, with a 1 KB key from 62-66 to 50-51 ns. It also compiles against the Node 22 and 24 headers.