Skip to content

Commit 8283b17

Browse files
committed
optimize number of buffer allocations
1 parent caaac1e commit 8283b17

File tree

5 files changed

+63
-6
lines changed

5 files changed

+63
-6
lines changed

core/src/main/java/io/grpc/internal/MessageFramer.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ void deliverFrame(
7575
// effectively final. Can only be set once.
7676
private int maxOutboundMessageSize = NO_MAX_OUTBOUND_MESSAGE_SIZE;
7777
private WritableBuffer buffer;
78+
/**
79+
* if > 0 - the number of bytes to allocate for the current known-length message.
80+
*/
81+
private int knownLengthPendingAllocation;
7882
private Compressor compressor = Codec.Identity.NONE;
7983
private boolean messageCompression = true;
8084
private final OutputStreamAdapter outputStreamAdapter = new OutputStreamAdapter();
@@ -120,6 +124,8 @@ public void setMaxOutboundMessageSize(int maxSize) {
120124
maxOutboundMessageSize = maxSize;
121125
}
122126

127+
private static final int COMPRESSION_MIN_LENGTH = 20;
128+
123129
/**
124130
* Writes out a payload message.
125131
*
@@ -137,7 +143,7 @@ public void writePayload(InputStream message) {
137143
int messageLength = -2;
138144
try {
139145
messageLength = getKnownLength(message);
140-
if (messageLength != 0 && compressed) {
146+
if (messageLength >= COMPRESSION_MIN_LENGTH && compressed) {
141147
written = writeCompressed(message, messageLength);
142148
} else {
143149
written = writeUncompressed(message, messageLength);
@@ -222,13 +228,25 @@ private int writeKnownLengthUncompressed(InputStream message, int messageLength)
222228
headerScratch.put(UNCOMPRESSED).putInt(messageLength);
223229
// Allocate the initial buffer chunk based on frame header + payload length.
224230
// Note that the allocator may allocate a buffer larger or smaller than this length
231+
knownLengthPendingAllocation = HEADER_LENGTH + messageLength;
225232
if (buffer == null) {
226-
buffer = bufferAllocator.allocate(headerScratch.position() + messageLength);
233+
buffer = allocateKnownLength();
227234
}
228235
writeRaw(headerScratch.array(), 0, headerScratch.position());
229236
return writeToOutputStream(message, outputStreamAdapter);
230237
}
231238

239+
/**
240+
* Allocate buffer according to {@link #knownLengthPendingAllocation} which is decremented after
241+
* that.
242+
*/
243+
private WritableBuffer allocateKnownLength() {
244+
WritableBuffer newBuffer = bufferAllocator.allocateKnownLength(knownLengthPendingAllocation);
245+
knownLengthPendingAllocation -= Math.min(knownLengthPendingAllocation,
246+
newBuffer.writableBytes());
247+
return newBuffer;
248+
}
249+
232250
/**
233251
* Write a message that has been serialized to a sequence of buffers.
234252
*/
@@ -243,7 +261,7 @@ private void writeBufferChain(BufferChainOutputStream bufferChain, boolean compr
243261
}
244262
headerScratch.clear();
245263
headerScratch.put(compressed ? COMPRESSED : UNCOMPRESSED).putInt(messageLength);
246-
WritableBuffer writeableHeader = bufferAllocator.allocate(HEADER_LENGTH);
264+
WritableBuffer writeableHeader = bufferAllocator.allocateKnownLength(HEADER_LENGTH);
247265
writeableHeader.write(headerScratch.array(), 0, headerScratch.position());
248266
if (messageLength == 0) {
249267
// the payload had 0 length so make the header the current buffer.
@@ -289,7 +307,9 @@ private void writeRaw(byte[] b, int off, int len) {
289307
}
290308
if (buffer == null) {
291309
// Request a buffer allocation using the message length as a hint.
292-
buffer = bufferAllocator.allocate(len);
310+
buffer = knownLengthPendingAllocation > 0
311+
? allocateKnownLength()
312+
: bufferAllocator.allocate(len);
293313
}
294314
int toWrite = min(len, buffer.writableBytes());
295315
buffer.write(b, off, toWrite);
@@ -397,7 +417,7 @@ private final class BufferChainOutputStream extends OutputStream {
397417
* {@link #write(byte[], int, int)}.
398418
*/
399419
@Override
400-
public void write(int b) throws IOException {
420+
public void write(int b) {
401421
if (current != null && current.writableBytes() > 0) {
402422
current.write((byte)b);
403423
return;

core/src/main/java/io/grpc/internal/WritableBufferAllocator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,10 @@ public interface WritableBufferAllocator {
2727
* free to return a buffer with a greater or lesser capacity.
2828
*/
2929
WritableBuffer allocate(int capacityHint);
30+
31+
/**
32+
* Request a new {@link WritableBuffer} with the given {@code capacityHint}. This method is
33+
* similar to {@link #allocate(int)}, but there is no need to allocate greater capacity.
34+
*/
35+
WritableBuffer allocateKnownLength(int capacityHint);
3036
}

netty/src/main/java/io/grpc/netty/NettyWritableBufferAllocator.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
*/
3434
class NettyWritableBufferAllocator implements WritableBufferAllocator {
3535

36+
// Use 4k as our minimum buffer size.
37+
private static final int MIN_BUFFER = 4 * 1024;
38+
3639
// Set the maximum buffer size to 1MB.
3740
private static final int MAX_BUFFER = 1024 * 1024;
3841

@@ -44,6 +47,12 @@ class NettyWritableBufferAllocator implements WritableBufferAllocator {
4447

4548
@Override
4649
public WritableBuffer allocate(int capacityHint) {
50+
capacityHint = Math.min(MAX_BUFFER, Math.max(MIN_BUFFER, capacityHint));
51+
return new NettyWritableBuffer(allocator.buffer(capacityHint, capacityHint));
52+
}
53+
54+
@Override
55+
public WritableBuffer allocateKnownLength(int capacityHint) {
4756
capacityHint = Math.min(MAX_BUFFER, capacityHint);
4857
return new NettyWritableBuffer(allocator.buffer(capacityHint, capacityHint));
4958
}

okhttp/src/main/java/io/grpc/okhttp/OkHttpWritableBufferAllocator.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.grpc.internal.WritableBuffer;
2020
import io.grpc.internal.WritableBufferAllocator;
2121
import okio.Buffer;
22+
import okio.Segment;
2223

2324
/**
2425
* The default allocator for {@link OkHttpWritableBuffer}s used by the OkHttp transport. OkHttp
@@ -42,6 +43,12 @@ class OkHttpWritableBufferAllocator implements WritableBufferAllocator {
4243
*/
4344
@Override
4445
public WritableBuffer allocate(int capacityHint) {
46+
capacityHint = Math.min(MAX_BUFFER, Math.max(Segment.SIZE, capacityHint));
47+
return new OkHttpWritableBuffer(new Buffer(), capacityHint);
48+
}
49+
50+
@Override
51+
public WritableBuffer allocateKnownLength(int capacityHint) {
4552
capacityHint = Math.min(MAX_BUFFER, capacityHint);
4653
return new OkHttpWritableBuffer(new Buffer(), capacityHint);
4754
}

servlet/src/main/java/io/grpc/servlet/ServletServerStream.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import io.grpc.internal.TransportFrameUtil;
3939
import io.grpc.internal.TransportTracer;
4040
import io.grpc.internal.WritableBuffer;
41+
import io.grpc.internal.WritableBufferAllocator;
4142
import java.io.IOException;
4243
import java.nio.charset.StandardCharsets;
4344
import java.util.Collections;
@@ -72,7 +73,7 @@ final class ServletServerStream extends AbstractServerStream {
7273
Attributes attributes,
7374
String authority,
7475
InternalLogId logId) throws IOException {
75-
super(ByteArrayWritableBuffer::new, statsTraceCtx);
76+
super(ALLOCATOR, statsTraceCtx);
7677
transportState =
7778
new ServletTransportState(maxInboundMessageSize, statsTraceCtx, new TransportTracer());
7879
this.attributes = attributes;
@@ -161,6 +162,20 @@ public void deframeFailed(Throwable cause) {
161162
}
162163
}
163164

165+
private static final WritableBufferAllocator ALLOCATOR = new WritableBufferAllocator() {
166+
private static final int MIN_BUFFER = 4096;
167+
168+
@Override
169+
public WritableBuffer allocate(int capacityHint) {
170+
return new ByteArrayWritableBuffer(max(MIN_BUFFER, capacityHint));
171+
}
172+
173+
@Override
174+
public WritableBuffer allocateKnownLength(int capacityHint) {
175+
return new ByteArrayWritableBuffer(capacityHint);
176+
}
177+
};
178+
164179
private static final class ByteArrayWritableBuffer implements WritableBuffer {
165180

166181
private final int capacity;

0 commit comments

Comments
 (0)