-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Add tokio-buf and a BufStream
trait
#611
Conversation
@seanmonstar I have updated |
The problem, however, is now |
Perhaps, |
tokio-buf/src/buf_stream/mod.rs
Outdated
/// | ||
/// Once a stream is finished, i.e. `Ready(None)` has been returned, further | ||
/// calls to `poll` may result in a panic or other "bad behavior". | ||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error>; |
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.
Should this be poll_next
like the upcoming change to Stream
? Or perhaps a different name, since a type could conceivably implement both Stream
and BufStream
, and if both traits are in scope, so there is no conflict?
tokio-buf/src/buf_stream/mod.rs
Outdated
/// calls to `poll` may result in a panic or other "bad behavior". | ||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error>; | ||
|
||
/// Returns the bounds on the remaining length of the iterator. |
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.
Calls this an "iterator"
pub struct SizeHint { | ||
available: usize, | ||
lower: usize, | ||
upper: Option<usize>, |
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.
For use in hyper to set the content-length, this being a usize
does mean that 32bit platforms can't set the content-length automatically if sending over 4GBs. Also, things like implementing BufStream
for tokio::fs::File
would cause problems there as well, since Metadata::len()
returns a u64
.
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.
Changing size_hint
to u64
has the interesting property of making collect()
fallible.
@seanmonstar I switched I resolved this by adding an error associated type to This ended up complicating the About half of those changes are handling potential overflow and the other half is factoring |
Then the next question... should |
I added a Now, to collect a body stream, you can do: let body = body_stream
.limit(mb_100)
.collect(); |
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.
s/sneding/sending
I think I'm going to get rid of |
/// if it could produce data at that point. If it chooses to return | ||
/// `NotReady`, when `consume_hint` is called with a non-zero argument, the | ||
/// task must be notified in order to respect the `poll_buf` contract. | ||
fn consume_hint(&mut self, amount: usize) { |
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.
A possible alternative to this function is passing some hint as an argument to poll_buf
. Like, stream.poll_buf(1024 * 16)
or something...
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.
The idea here (with h2) is this function would be called separately from reading. When a window update frame is received, it would call consume_hint
w/ the total window size.
An argument to poll_buf
would also work... but then it would be required to pass something. With a separate function consume_hint
, calling it isn't required.
There are pros / cons to both, what are you thinking?
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.
Another con is that middleware must remember to implement consume_hint
as there is a default implementation.
I'm actually wondering if the default implementation should be omitted. Same with size_hint
... I would expect it to be rarer to implement vs. use.
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.
Is there an example of this providing value somewhere? I get the intention, but I'd personally prefer this left off until there's an implementation showing how it can be helpful.
Notably, removing hyper's optimization for |
@seanmonstar what is the additional cost from? |
@seanmonstar it seems highly suspect that using |
So, some of the slow down I measured was that internally a optimized fast path was not being taken. Having refactored to basically this: match body.poll_data()? {
Async::Ready(Some(data)) => {
if body.is_end_stream() {
self.write_full_msg(head, data);
}
}
} This has improved it somewhat, but there is still ~5% slowdown compared to when using |
@seanmonstar that is pretty surprising and would be worth digging into. |
@seanmonstar I dug in a bit, and it looks like the comparison isn't quite fair. The "slow path" could probably be optimized quite a bit more by avoiding much work in I did a bunch of experimentation myself, and I saw a 5ns difference between using |
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.
Approving with the caveat that consume_hint
should be looked at before publishing 0.1 to crates.io.
This is a work in progress.
The description will be updated over time.
TODO
SizeHint
guaranteesconsume_hint
take au64
orusize
?BufStream::limit()
tests