-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WHATWG Streams #9
Comments
Every stream creates an Error, and creating errors is slow in Node.js because of how we construct them. We also override the base types; I think we should not do that. |
Further info: nodejs/undici#1203 (comment) Every stream creates a |
I'm going to work on this in the next couple of weeks. @RafaelGSS Can you share where stream creates a |
I did a small perf change to ReadableStream (nodejs/node#45489) |
I think you are confusing Readable and ReadableStream. That PR has nothing to do with WHATWG streams as far as I can see. |
Please correct me if I'm wrong but are @RafaelGSS referring to these errors https://github.com/nodejs/node/blob/1bbd14eac20684a733250629c7896262c0ca4576/lib/internal/webstreams/readablestream.js#L33-L38? An example from If these are the errors, these should be somehow lazy-loaded or replaced by the common |
No. Actually, per spec when a default writable/readable is released an error should be propagated https://streams.spec.whatwg.org/#writable-stream-default-writer-release, and nowadays this is quite expensive. My initial analysis expands on this. |
Honestly, even changing to common Errors, we won't see much improvement. |
Have you tried with the primoridial |
Yes, it improves, but it's not a significant improvement that would make sense to break the spec. The problem isn't the kind of Error utilized, it's more about propagating an error (or rejecting a promise) even in the happy path -- which is quite expensive. I'm tempted to consider a WebStreams implementation on C++, but this is just a guess. |
The problem is not breaking the spec. We override all the Errors in node.. even the ones that we shouldn't. |
Noob question, but the Can be used bypassing the call to https://github.com/nodejs/node/blob/1bbd14eac20684a733250629c7896262c0ca4576/lib/internal/errors.js#L360 and having at least no performance regression meanwhile respecting the spec, it or will have the same impact? |
Guys, It's on my radar. I'll be working on it when I have time. Just created a benchmark PR nodejs/node#45876. |
Are you planning to have some specific action items once the assessment? I’m more than happy to help you with if possible 🙂 |
I'll try to collect some insights and post them here. Eventually, it can be a list of actionable |
So, I've spent some time on it again, and here's the summary:
I'll keep investigating other clear wins when I have time, but these two are great starting points. |
ReadableStreamDefaultReader.releaseLock performance can be improved by only creating errors as needed: diffdiff --git a/lib/internal/webstreams/readablestream.js b/lib/internal/webstreams/readablestream.js
index 5344785b90..18c86163c5 100644
--- a/lib/internal/webstreams/readablestream.js
+++ b/lib/internal/webstreams/readablestream.js
@@ -2029,15 +2029,22 @@ function readableStreamDefaultReaderRelease(reader) {
readableStreamReaderGenericRelease(reader);
readableStreamDefaultReaderErrorReadRequests(
reader,
- new ERR_INVALID_STATE.TypeError('Releasing reader')
+ 'Releasing reader'
);
}
-function readableStreamDefaultReaderErrorReadRequests(reader, e) {
- for (let n = 0; n < reader[kState].readRequests.length; ++n) {
- reader[kState].readRequests[n][kError](e);
+function readableStreamDefaultReaderErrorReadRequests(reader, m) {
+ const { readRequests } = reader[kState];
+
+ if (readRequests.length) {
+ const error = new ERR_INVALID_STATE.TypeError(m);
+
+ for (let n = 0; n < readRequests.length; ++n) {
+ readRequests[n][kError](error);
+ }
+
+ readRequests.length = 0
}
- reader[kState].readRequests = [];
}
function readableStreamBYOBReaderRelease(reader) { For a simple use case, this no longer creates an error: const readable = getReadableOrSomething()
const reader = readable.getReader()
await reader.read()
reader.releaseLock() Although I haven't benchmarked the code and haven't run any tests other than the WPTs (which all passed). I don't think releaseLock is a hot path either but it's the first thing I noticed while reading Rafael's comment above. |
@KhafraDev I think that might be a following up pr after nodejs/node#46086, but instead of creating an error, re-use the one mentioned. But honestly, I don't think it will increase the performance because we're now re-using the errors. |
I see that is just landed! I guess my solution is a viable alternative if there is an issue with re-using errors. 😄 |
I have hunch it's related to a lot of promise creation (for example the ready promise on writable) node streams do not uses promises extensively IIRC |
WHATWG Streams are slow, and one of the primary reasons for slow performance with fetch in Node.
The text was updated successfully, but these errors were encountered: