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

Avoid some extra clones/allocations #2865

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

novacrazy
Copy link

@novacrazy novacrazy commented Aug 7, 2024

Motivation

I noticed when writing a Layer implementation that it requires Clone, 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 to MethodRouter::call_with_state, but a match might be more efficient, and the new macro should more easily help catch issues the comment about destructuring was after. Edit: Partially reverted, overlooked something simple.

I'm still looking for places where clones/allocations can be avoided.

@pickfire
Copy link

pickfire commented Aug 9, 2024

@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?

@novacrazy
Copy link
Author

@pickfire I had a hunch it was inefficient somewhere, so I put a println! in my layer/service Clone implementation to confirm. Then I just read through Axum’s code, starting at the Router’s own Service call until I saw all the cloning going on. Nothing special.

When I have some more time I’d like to figure out how to remove even more clones. 1-2 should be possible.

Copy link
Member

@jplatte jplatte left a 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.

@novacrazy
Copy link
Author

novacrazy commented Aug 9, 2024

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 req.uri().path().to_owned(), which was a pointless allocation. By splitting the request into its parts and body, we can borrow from parts.uri while also having mutable access to the parts.extensions, then just recombine the request when it needs to be sent downstream. Zero cloning.

Second, notice the method oneshot_inner, which calls self.0.get_mut() to acquire mutable access to a mutex's inner value, only to immediately clone it and call oneshot. Instead of get_mut(), we can use into_inner() to simply move the inner value in cases where we have ownership of it, which is what oneshot_inner_owned and call_owned do. Then I replaced some code that cloned and called oneshot_inner/Route::call with those, respectively, since the clone already occurred and we have ownership in those places.

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 Method for use within the call! macro in MethodRouter::call_with_state. Most of the time cloning that is simple, but Method is not Copy, because of custom methods, so may as well just avoid the cloning.

Copy link

@Bmwx12013 Bmwx12013 left a comment

Choose a reason for hiding this comment

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

Chancha

Copy link
Contributor

@yanns yanns left a 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 novacrazy requested a review from jplatte August 31, 2024 19:13
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.

5 participants