-
Notifications
You must be signed in to change notification settings - Fork 127
Avoid wrapping List<int>/Stream<List<int>> response bodies in a CastList/CastStream #178
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
Avoid wrapping List<int>/Stream<List<int>> response bodies in a CastList/CastStream #178
Conversation
lib/src/body.dart
Outdated
@@ -52,6 +54,13 @@ class Body { | |||
contentLength = encoded.length; | |||
stream = Stream.fromIterable([encoded]); | |||
} | |||
} else if (body is Uint8List) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or could this just be is List<int>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh yeah, changed!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's update the changelog and pubspec to prep for release.
1.1.1 ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a CHANGELOG entry and bump the patch version?
} else if (body is List) { | ||
contentLength = body.length; | ||
stream = Stream.fromIterable([body.cast()]); | ||
stream = Stream.value(body.cast()); | ||
} else if (body is Stream) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder, while we are here should we assume that Stream.cast
is likely to have similar overhead?
} else if (body is Stream) { | |
} else if (body is Stream<int>) { | |
stream = body; | |
} else if (body is Stream) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely, seems worth the optimization
test/response_test.dart
Outdated
expect( | ||
await response.read().single, | ||
isA<List<int>>() | ||
.having((List<int> values) => values, 'values', [1, 2, 3, 4])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.having((List<int> values) => values, 'values', [1, 2, 3, 4])); | |
.having((values) => values, 'values', [1, 2, 3, 4])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
I don't have any merge permissions btw 😄 |
} else if (body is List) { | ||
contentLength = body.length; | ||
stream = Stream.fromIterable([body.cast()]); | ||
stream = Stream.value(body.cast()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is likely to be iterated over multiple times, you may be better to eagerly copy the list with List.from
instead of using cast
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My hunch is this isn't a problem. I don't expect multiple iterations in most cases. Usually when we would need this cast it would be a Response
which will be passed to dart:io
which I expect iterates once. Otherwise, when this stream is getting passed to user code (where it may be iterated more than once) I'd expect the optimizations in this PR should kick in since it will most often be a Stream<List<int>>
coming from dart:io
to start with.
This is published |
(Allows flutter/flutter#81010 to be fixed, though we need to change some code on the flutter side too)
TLDR: when writing data to a socket in dart, anything besides a Uint8List or Int8List will be necessarily copied (c.f. https://github.com/dart-lang/sdk/blob/3f0ad61daaac68e591bf01d0a3096926c30c1d42/sdk/lib/io/common.dart#L92)
shelf will unnecessarily wrap all Lists in a CastList which will perform runtime checks for every single element.
The combination of these behaviors causes requests for large files in the flutter run web server to be incredibly slow, observed to be on the order of multiple minutes locally. With the combination of this change and a patch to the tool to avoid creating Uint8List views, the time to serve the app drops to a few seconds.
I took a CPU profile starting from when chrome opened until the app's first frame. I could upload the actual timeline data too, but a picture something something a thousand words
Before:
After: