-
Notifications
You must be signed in to change notification settings - Fork 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
refactor: Reduce how much code gets monomorphized #1032
Conversation
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.
Thanks for taking this on! It would be interesting to know why these changes reduce the llvm ir bloat.
@@ -58,66 +58,62 @@ where | |||
T: Encoder<Error = Status>, | |||
U: Stream<Item = Result<T::Item, Status>>, | |||
{ | |||
async_stream::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.
So if I understand correctly async-stream causes llvm ir bloat? Is this something we can fix in that crate? I personally prefer to maintain this code vs a combinator but I accept that this may be an artifact of using this type of code.
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.
It seems to bloat it a bit more than the combinator. I figured in this case the use of async_stream
was a bit unecessary and the combinator was clear enough. I tweaked it to use map
instead which removes the closure. Once IntoFuture
gets stable in 1.64 and_then
could be used without the closure as well (I think).
Every combination of types that these functions gets called with gets a unique instantiation of binary code (monomorphized). So if we can shrink these generic functions, usually be moving code into less/non-generic functions rust/llvm can re-use those new functions for every generic function. |
Did a couple of more tweaks to re-use code when the output type is the same. |
With the new changes (and using a different/newer compiler) we go from 47011 to 33329 (-29%) |
Nice! Seems like CI is failing? |
Seems to be some weirdness around type inference and `Into` causing the downcasts to fail
let mut buf = BytesMut::with_capacity(BUFFER_SIZE); | ||
|
||
let compression_encoding = if compression_override == SingleMessageCompressionOverride::Disable | ||
{ | ||
None | ||
} else { | ||
compression_encoding |
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.
So this code used to be executed lazily. Not sure how much it matters in practice, but I wonder if we want to move this stuff down into the closure?
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 think for now this is fine, we can follow up if need be.
Thanks! |
Motivation
tonic
has a lot of generic code which causes a lot of monormorphization and therefore bloated compile times and binaries.Solution
By extracting code which does not need to be generic over a type parameter into non generic (or less generic functions) we can reduce how much code gets instantiated and passed to LLVM.
This gives a ~23% reduction in the output of LLVM IR (according to
cargo llvm-lines -p examples --bin streaming-client
) and reduces the compilation time ofcargo build --release --bin streaming-client
by ~1.5s (21s to 19.5s). (Measured by just switching branches between this one and master to force recompilations)Before
After
Closes #849 (supersedes it)