Skip to content

Comments

Add setReadbufferSize API to CronetChannelBuilder#12199

Open
aymanm-google wants to merge 1 commit intogrpc:masterfrom
aymanm-google:cronet_api_for_read_buffer
Open

Add setReadbufferSize API to CronetChannelBuilder#12199
aymanm-google wants to merge 1 commit intogrpc:masterfrom
aymanm-google:cronet_api_for_read_buffer

Conversation

@aymanm-google
Copy link

By default, CronetClientStreams would use a 4KB buffer to read data from Cronet. This can be inefficient especially if the amount of data being read is huge (~MBs) as each read callback operation incur overhead from Cronet itself (e.g. Context switch, JNI calls). The alternative would be to immediately bump the default to a bigger number but that would incur an increase in memory usage.

If the API is not called then the default of 4KB is used.

@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Jul 2, 2025

CLA Signed

The committers listed above are authorized under a signed CLA.

  • ✅ login: aymanm-google / name: Mohannad Farrag (b79ffbe)

@ejona86
Copy link
Member

ejona86 commented Jul 24, 2025

I think this is a bit more fundamental and we need to think about it some. I agree there's a problem here to be solved, but I'm not all that confident that setting a read buffer size is what we want. In some ways I wouldn't mind adding this, but we would want to be able to remove it in the future.

I'm assuming the latency here is between the application and network thread. There had been some work on MigratingDeframer. I wonder if it helps this problem. (If you revert #7198 you might be able to see if the performance improves.)

@aymanm-google
Copy link
Author

So I understand that adding a new API is a little bit difficult here. However, this API is purely for performance reasons, which means that it can be deprecated at any time and it would not affect the correctness. If we conclude that the difference in performance was not that reasonable then we can just put a @deprecated above it and say that the default is still 4KB?

I'm assuming the latency here is between the application and network thread. There had been some work on MigratingDeframer. I wonder if it helps this problem. (If you revert #7198 you might be able to see if the performance improves.)

I don't understand how this is related? The current performance overhead is between the application and Cronet's network thread which gRPC does not have control over.

@ejona86
Copy link
Member

ejona86 commented Jul 24, 2025

The current performance overhead is between the application and Cronet's network thread which gRPC does not have control over.

I wasn't convinced that was the full problem. I see now that gRPC is starting the next read() directly from the onReadCompleted() callback.

gRPC does know how many bytes are remaining for the current message, but we avoid using it to avoid penalizing streaming. So something along the lines of this PR does seem necessary.

Should this setting be per-stream instead of per-channel? That can be communicated via CallOptions, like done with CRONET_ANNOTATIONS_KEY. The caller then uses stub.withOption(THE_KEY, 4096) to configure it on the stub. So they can still use the same setting for multiple RPCs.

@groakley
Copy link
Contributor

Should this setting be per-stream instead of per-channel?

I agree that a per-stream setting would make sense. A single service may have one method that expects particularly large message sizes, but others that have more conventional payload sizes where the extra buffer space would be wasteful.

Configuring this with a CallOption doesn't prevent setting this once for a channel if that's the desired behavior. You could wrap the channel with a ClientInterceptor that sets the option for all methods and avoid setting it on each stub.

My concern with the CallOption approach would be if it prevents the Cronet channel from pooling these buffers across streams. But from what I can tell looking through CronetClientStream, there is no buffer pooling today; each new stream allocates a new buffer anyways.

@aymanm-google aymanm-google force-pushed the cronet_api_for_read_buffer branch from 561b86c to 5982a0c Compare January 22, 2026 14:23
@aymanm-google
Copy link
Author

So I'm reviving this thread again. I've applied the recommended approach here: #12199 (comment) where there's an optionKey that the user can apply to change the per-stream buffer size.

Let me know if this is okay or there's more changes needed.

By default, CronetClientStreams would use a 4KB buffer to read data
from Cronet. This can be inefficient especially if the amount of data
being read is huge (~MBs) as each read callback operation incur overhead
from Cronet itself (e.g. Context switch, JNI calls). The alternative
would be to immediately bump the default to a bigger number but that
would incur an increase in memory usage.

So in order to safely experiment on this, An OptionKey is introduced
which allows setting a per-stream value which will be controlled in a
controlled environment to ensure we find the best new default.
@aymanm-google aymanm-google force-pushed the cronet_api_for_read_buffer branch from 5982a0c to b79ffbe Compare January 22, 2026 14:43
@aymanm-google
Copy link
Author

My concern with the CallOption approach would be if it prevents the Cronet channel from pooling these buffers across streams. But from what I can tell looking through CronetClientStream, there is no buffer pooling today; each new stream allocates a new buffer anyways.

Buffering across streams will be really complicated and it is not really clear whether it would have a significant improvement or not. One possible improvement here is instead of creating a new buffer every single time to read, we could potentially reuse the same buffer passed to Cronet. This is fine since this is a behaviour that we've maintained in Cronet where we pass the same buffer which the user passed to us after filling it with data.

@aymanm-google
Copy link
Author

Friendly ping for review!

Copy link
Member

@ejona86 ejona86 left a comment

Choose a reason for hiding this comment

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

Whoops, sorry. I missed the "Add CRONET_READ_BUFFER_SIZE_KEY API to CronetClientStream" addition.

The change right now looks fine, except there's no way for it to actually be used. Should be good after that is resolved.

* Sets the read buffer size which the GRPC layer will use to read data from Cronet. Higher buffer
* size leads to less overhead but more memory consumption. The current default value is 4KB.
*/
public static final CallOptions.Key<Integer> CRONET_READ_BUFFER_SIZE_KEY =
Copy link
Member

Choose a reason for hiding this comment

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

Note that this is not accessible to others, because it is in a package-private class. We want this class to be package-private. If we want this API for internal-only (at least for now), then you can add accessor methods to InternalCronetCallOptions.

@ejona86 ejona86 added the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Feb 19, 2026
@grpc-kokoro grpc-kokoro removed the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label Feb 19, 2026
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.

4 participants