-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
Optimize buffer usage in MessageFramer #8
Comments
Rough design: introduce WritableBuffer (likely renaming current Buffer to ReadableBuffer) that only allows writes. MessageFramer writes to that buffer, and uses a provided buffer allocator to get instances. The provided allocator would generally use the transport's "native" buffer pool. Currently the MessageFramer re-uses the buffer, but this shouldn't happen in the future. It only works today because the various transports make a copy of the buffer, which is what this feature would be trying to prevent. Instead, it should use the allocator provided and use a new buffer each time. In the case that a transport does want to reuse, then it can have a simple allocator that "pools" a single buffer instance. Using a new buffer each time means that MessageFramer won't need to be closed when the Call is closed, which turns out to be quite helpful from a threading perspective since MessaegFramer is currently run within the application thread and so we can't clean it explicitly without making it thread-safe. |
@ejona86 I am currently working on this and I was wondering how you thought piggy-backing the END_STREAM flag would work without close/dispose and a new buffer on every write? |
I think it is reasonable to assume a flush/close/dispose will always be coming. |
so we still need the flush/close/dispose on the MessageFramer to behave the same as they did until know, correct? I don't understand the part of "that MessageFramer won't need to be closed when the Call is closed". Could you please clarify this? Thanks @ejona86 |
Basic flow (but, of course, with framing mixed in): void write(blah) {
while (blah.moreToWrite()) {
if (buffer != null && buffer.full()) flush();
if (buffer == null) buffer = alloc();
writeAsMuchAsPossible(buffer, blah);
}
}
void flush() {
if (buffer != null) {
send(buffer);
buffer = null;
}
} In that code if the message is precisely the size of the buffer then it isn't flushed. That is more "optimal" but doesn't really matter. I wrote it like that here since I think that is what the current code is doing. |
@ejona86 ahh got ya. yeah that's what I thought. thank you! :-). |
On "idle" there won't be a buffer. When a message is sent, a flush/close must be coming basically immediately after. So dispose() would only be necessary in case of exception or something. We wouldn't need to call dispose() to clean up when the stream is torn down (which I can't remember if we are doing now, since the current buffer is on the heap) |
@ejona86 I was just thinking. What's the point of having a |
We could, and considered it. The main problem is that the API becomes a lot more interesting when you can read and write at the same time. Consider what be necessary to use JDK's ByteBuffer in such a circumstance. All very doable, but it seemed keeping them separate was no more work, simpler, and had no real deficiencies. |
WritableBuffer is a generic interface that allows to transfer data from gRPC directly to the native transport's buffer implementation.
WritableBuffer is a generic interface that allows to transfer data from gRPC directly to the native transport's buffer implementation.
WritableBuffer is a generic interface that allows to transfer data from gRPC directly to the native transport's buffer implementation.
WritableBuffer is a generic interface that allows to transfer data from gRPC directly to the native transport's buffer implementation.
WritableBuffer is a generic interface that allows to transfer data from gRPC directly to the native transport's buffer implementation.
WritableBuffer is a generic interface that allows to transfer data from gRPC directly to the native transport's buffer implementation.
We currently allocate a large non-direct buffer per stream due to MessageFramer, only to copy immediately out of it. We should instead write directly to the transport-native buffer, which will cause us to have a WriteableBuffer or some such like our current Buffer class that is ready-only.
The text was updated successfully, but these errors were encountered: