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

fix(codec): Allocate inbound buffer once #578

Merged
merged 1 commit into from
Apr 7, 2021
Merged

fix(codec): Allocate inbound buffer once #578

merged 1 commit into from
Apr 7, 2021

Conversation

athre0z
Copy link
Contributor

@athre0z athre0z commented Mar 10, 2021

During profiling for bottlenecks in my tonic powered message queue, I noticed that a large amount of compute was spent in reallocating and memmoving over read buffers.

This PR gets rid of the reallocs entirely by pre-allocating the buffer for the entire message body directly after the message size is known instead of only reserving space for the next chunk when they come in.

In my testing with 8MiB messages, this improved throughput from 370MiB/s to 560MiB/s, which is roughly a 50% improvement. I originally mistyped the numbers, thus the "30-40%" in the commit message -- didn't want to force-push just for that.

Gets rid of reallocs when receiving large messages, increasing throughput by about 30%-40%.
Copy link
Member

@davidpdrsn davidpdrsn left a comment

Choose a reason for hiding this comment

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

I think this looks good and I assume some tests would fail if it was wrong.

But would like to hear what @LucioFranco thinks.

@@ -180,6 +180,7 @@ impl<T> Streaming<T> {
}
};
let len = self.buf.get_u32() as usize;
self.buf.reserve(len);
Copy link
Member

Choose a reason for hiding this comment

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

Would this be a vector for attack? Since a user could pass any number here and reserve as much memory as possible even if they can't send that on the wire?

Copy link
Contributor Author

@athre0z athre0z Apr 7, 2021

Choose a reason for hiding this comment

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

Yes and no. As far as I see, even before this PR, there was also no protection against the user accumulating an arbitrary large buffer (by, well, just actually sending it). So in this way, this PR doesn't make the situation worse, except that it lowers the attacker's effort.

On macOS and Linux, allocating huge buffers doesn't actually fault & reserve the corresponding pages until they are actually written to (overcommit), so the situation doesn't change at all. On Windows, only the size of the page file can be overcommitted, so this would crash immediately on very large values (if the user is low on RAM already).

Unless I'm wrong and this is actually verified (but I just didn't see where, which is very much possible!), I'd argue: yes, this clearly is an issue, but one that should probably be discussed and fixed in a dedicated issue?

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the great write up. Yeah, I think this makes sense then!

Copy link
Member

@LucioFranco LucioFranco left a comment

Choose a reason for hiding this comment

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

Thanks again!

@LucioFranco LucioFranco changed the title Reserve receive buffer just once fix(codec): Allocate inbound buffer once Apr 7, 2021
@LucioFranco LucioFranco merged commit 1d2754f into hyperium:master Apr 7, 2021
@athre0z athre0z deleted the better-reserve branch April 7, 2021 23:44
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.

3 participants