Skip to content
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

Future::boxed and Stream::boxed should prevent double boxing #512

Closed
wants to merge 2 commits into from

Conversation

stepancheg
Copy link
Contributor

@stepancheg stepancheg commented Jun 11, 2017

Fixes #511

travis-ci also runs tests on rust 1.10. In rust 1.10 TypeId::of requires Reflect trait. I have not found an easy way to work around this limitation, so I decided to make boxed prevent double boxing only starting from rust 1.18 (which is latest stable at the moment). Some older version between 1.10 and 1.18 could be chosen for switch, but because travis-ci does not test these versions, I think it is reasonable to enable this behavior only from 1.18.

@stepancheg stepancheg changed the title Future::boxed and Stream::box should prevent double boxing Future::boxed and Stream::boxed should prevent double boxing Jun 11, 2017
@stepancheg stepancheg force-pushed the boxed branch 2 times, most recently from 990539f to be4984a Compare June 11, 2017 02:29
@sinkuu
Copy link

sinkuu commented Jun 11, 2017

Reflect bound was removed in 1.13 (according to this comment). Linux distros usually provides old versions of Rust, so including them may profit some.

@@ -250,7 +250,40 @@ pub trait Stream {
fn boxed(self) -> BoxStream<Self::Item, Self::Error>
where Self: Sized + Send + 'static,
{
::std::boxed::Box::new(self)
#[cfg(rust_at_least_1_18)]
fn boxed<S>(stream: S) -> BoxStream<S::Item, S::Error>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be a fully copy/paste version of boxed() for Future except the result type itself. I'd rather decompose them both to a some type-parametrized function.

Copy link
Contributor Author

@stepancheg stepancheg Jun 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I also tried to write generic version and couldn't.

&mut r as *mut BoxFuture<F::Item, F::Error> as *mut u8 as *mut F,
1);
mem::forget(future);
r
Copy link

@3Hren 3Hren Jun 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just return future here without unsafe magic?
Understood.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because compiler doesn't know that future can be cast to BoxFuture<...>. Moreover, even transmute doesn't work, because compiler cannot prove that type sizes are equal.

@stepancheg
Copy link
Contributor Author

OK, updated the PR with slightly more generic version.

@stepancheg stepancheg force-pushed the boxed branch 3 times, most recently from 5d593ef to c1b5845 Compare June 11, 2017 15:35
@alexcrichton
Copy link
Member

Are there measureable gains from this PR? I agree that theoretically this is a plausible optimization but I'd much rather prefer the solution in https://github.com/alexcrichton/futures-rs/pull/513/files

@alexcrichton
Copy link
Member

I also fear that most users aren't actually using .boxed() because they don't want Send trait objects. For example all throughout sccache this method is barely used, if at all. In that sense I'm not sure this is worth the weight of the implementation.

@stepancheg
Copy link
Contributor Author

Are there measureable gains from this PR?

I've updated PR with synthetic test. Before this PR:

test boxed             ... bench:       2,087 ns/iter (+/- 420)
test boxed_boxed       ... bench:       3,106 ns/iter (+/- 577)
test boxed_boxed_boxed ... bench:       5,536 ns/iter (+/- 1,032)
test plain             ... bench:         791 ns/iter (+/- 190)

So, accidental additional unnecessary boxing adds significant overhead for simple streams.

It is hard to say what's performance impact of accidental boxing in real applications. However, the problem is that from user point of view it is easier to simply use safe boxed instead of thinking, whether each boxed call is expensive or could be avoided.

@stepancheg
Copy link
Contributor Author

I also fear that most users aren't actually using .boxed() because they don't want Send trait objects. For example all throughout sccache this method is barely used, if at all. In that sense I'm not sure this is worth the weight of the implementation.

I also simply call Box::new(..), but if boxed will provide double boxing prevention I will.

About Send, probably, another function should be added like boxed_unsend until Rust type system allows writing generic functions parameterized by optional Send, simething like:

fn boxed(self) -> Box<Future<Item=Self::Item, Error=Self::Eror> + SendIfSend<Self>>
        where Self: Sized + 'static { ... }

@stepancheg
Copy link
Contributor Author

Updated the PR with specialization instead of transmute.

@alexcrichton
Copy link
Member

Yes I think it's obvious that this is a performance gain in microbenchmarks, but my worry is that this doesn't actually get exercised that much higher up the stack. I'm ok with the specialization approach but I would not want to land it until specialization is stable. I'm ok leaving this PR open, however.

@carllerche
Copy link
Member

I would rather see boxed deprecated and eventually removed (#228). It's probably been the (or close to) number one source of confusion of people in the gitter channel.

@khuey
Copy link
Contributor

khuey commented Jun 17, 2017

FWIW I usually skip boxed because I don't want 'static.

@alexcrichton
Copy link
Member

Currently I agree with @carllerche that we're highly likely to remove/deprecate boxed as in #228, so I'm going to close this. Thanks though for this PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants