-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Improvements to memory usage of HttpClient connections and requests #61223
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsgRPC supports bidirectional streaming over HTTP/2. One of the implications of streaming is an RPC call idling while reading data from the HTTP/2 response stream. For example, a frontend server could have many bidi streams open with a backend server. The gRPC client in the frontend server is waiting to read data from them and holding onto many buffers. The client app has higher memory usage and GC heap fragmentation. Customer reported fragmentation hotspots (note: they are using .NET 5, some of these may have been addressed in .NET 6):
cc @geoffkizer
|
There are several semi-related issues we should consider addressing, all around reducing memory usage and (in some cases) avoiding long-lived pinned buffers: (1) Zero-byte read in the HTTP2 connection receive logic. This would allow us to release the HTTP2 receive buffer back to the pool if we don't have any data to receive at the moment, and similarly for SslStream's receive buffer (SslStream._internalBuffer above). Also note the SslStream buffer will be pinned on Windows, since it's performing a read on NetworkStream (in the common case). However, this could introduce overhead in the receive path so we should measure the impact here and consider making this opt-in via a property on SocketsHttpHandler. |
A |
I've also been wanting to try a ref-counted receive buffer. This is a bit involved of an idea that needs massive proving out, but, it's:
For long-running H2 streams, this would allow us to not make two I/Os while also helping memory usage. |
It would. It would, however, also require more due diligence on returning arrays to the pool and on the pool keeping such arrays more aggressively (which would in turn have impact on how it's used). Such a pool would likely be backed by the pinned object heap, and current loosy-goosy-ness around how non-aggressively we return arrays to the pool as well as how liberally the pool is able to drop them could result in the POH becoming more heavily fragmented. This is one of the reasons we've shied away from just making ArrayPool.Shared itself use the POH. @brianrob has been doing some thinking around this. |
We may be able to eliminate |
@wfurt What did you have in mind specifically? |
If user buffer is big enough we can read into it and decrypt in place. |
We have also talked about allowing Stream users to get direct access to buffered data for Streams that have internal buffers like SslStream. This would allow the HTTP2 code to avoid allocating its own buffer in at least some cases. See #58216 -- "Peeking". |
I added an issue specifically to track response stream zero-byte read support (my bullet (2) above): #61475 |
@JamesNK #61913 added support for zero-byte reads on response content streams. gRPC should be able to take advantage of this. Since you're using a large copy buffer, this should account for most of your memory. YARP's StreamCopier may be used as a reference for similar copy loops. Potential improvements for reducing the memory footprint (some were already mentioned above):
Potential improvements:
|
Triage: We improved the E2E with zero-byte reads in #61475. Further buffer size adjustment (as suggested above by @MihaZupan) are Future. |
gRPC supports bidirectional streaming over HTTP/2. One of the implications of streaming is an RPC call idling while reading data from the HTTP/2 response stream. This is the client waiting for the next message. It could arrive immediately or in 5 minutes.
For example, a frontend server could have many bidi streams open with a backend server. The gRPC client in the frontend server is waiting to read data from them and holding onto many buffers. The client app has higher memory usage and GC heap fragmentation.
Customer reported fragmentation hotspots (note: they are using .NET 5, some of these may have been addressed in .NET 6):
cc @geoffkizer
The text was updated successfully, but these errors were encountered: