-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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 some extra clones/allocations #2865
Conversation
@novacrazy I am interested how did you get to figure out where allocations occur. Did you overwrite the global allocator to make it tracks allocation? |
@pickfire I had a hunch it was inefficient somewhere, so I put a When I have some more time I’d like to figure out how to remove even more clones. 1-2 should be possible. |
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.
Hey, thanks for the PR! I'm not intimately familiar with the code pieces you're changing here. Would you mind splitting this PR into smaller ones? It seems like there's a bunch of independent changes here that would be easier to discuss separately.
Honestly, I don't think it's necessary to split up. It's only 45 lines changed. The first change was to get rid of the Second, notice the method It's also worth noting this old code: let route = handler.clone().into_route(state);
return RouteFuture::from_future(route.clone().oneshot_inner($req)) which clones the handler, applies state to it, then clones it again for no reason. I fixed that as well. Lastly, I avoided cloning the |
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.
Chancha
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.
This is looking good to me, but I'm no expert here.
@novacrazy could you check the conflicts with |
@novacrazy I've created #2968 that would merge your changes with |
match endpoint { | ||
Endpoint::MethodRouter(method_router) => { | ||
Ok(method_router.call_with_state(req, state)) | ||
} | ||
Endpoint::Route(route) => Ok(route.clone().call(req)), | ||
Endpoint::Route(route) => Ok(route.clone().call_owned(req)), |
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.
This change is not reflected in any tests. Do you think we could test this?
/// Variant of [`Route::call`] that takes ownership of the route to avoid cloning. | ||
pub(crate) fn call_owned(self, req: Request<Body>) -> RouteFuture<E> { | ||
let req = req.map(Body::new); | ||
RouteFuture::from_future(self.oneshot_inner_owned(req)) |
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.
When comparing with main
, it would now be self.oneshot_inner_owned(req).not_top_level()
.
I'm not sure about the .not_top_level()
. And there are no tests checking if this is necessary or not.
Do you think we could test this?
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.
This is a note for me.
The current .not_top_level()
is covered by axum/src/routing/tests/mod.rs:1086
fn call_with_state(&mut self, req: Request, state: S) -> RouteFuture<E> { | ||
fn call_with_state(self, req: Request, state: S) -> RouteFuture<E> { | ||
match self { | ||
Fallback::Default(route) | Fallback::Service(route) => { | ||
RouteFuture::from_future(route.oneshot_inner(req)) | ||
RouteFuture::from_future(route.oneshot_inner_owned(req)) | ||
} | ||
Fallback::BoxedHandler(handler) => { | ||
let mut route = handler.clone().into_route(state); | ||
RouteFuture::from_future(route.oneshot_inner(req)) | ||
let route = handler.into_route(state); | ||
RouteFuture::from_future(route.oneshot_inner_owned(req)) |
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.
There are no tests covering this change. Do you think we could test this?
Given latest comments, I want to emphasize once again that this would be much easier to get merged as several smaller PRs, however small the diff may already be. |
Okay, I'll close this in favor of the new PR then. |
This is an extraction of a part of #2865
I've extracted one part that is covered by tests in #2969. |
This is an extraction of a part of #2865
This is an extraction of a part of #2865
This is an extraction of a part of #2865
Motivation
I noticed when writing a
Layer
implementation that it requiresClone
, and after running a small hello world noticed it cloned a lot. After looking at the code I saw that some of the clones were pointless, just to get a mutable reference that then cloned again internally.Solution
This adds a couple specialized routines that help avoid a few clones, and cleans up some code elsewhere to avoid a couple more allocations.
The most contentious change might be toEdit: Partially reverted, overlooked something simple.MethodRouter::call_with_state
, but amatch
might be more efficient, and the new macro should more easily help catch issues the comment about destructuring was after.I'm still looking for places where clones/allocations can be avoided.