Skip to content

Commit deea51e

Browse files
carl-mastrangelonormanmaurer
authored andcommitted
Disable Huffman encoding for small headers (netty#9260)
Motivation: Huffman coding saves only a little space, but has a huge CPU cost Modification: Disable huff coding for headers smaller than 512 bytes. Also, add a configurable limit to the encoder. Result: Faster HPACK BEFORE: ``` Benchmark (duplicates) (limitToAscii) (sensitive) (size) Mode Cnt Score Error Units HpackEncoderBenchmark.encode true true true SMALL avgt 10 2572.595 ± 16.184 ns/op HpackEncoderBenchmark.encode true true true MEDIUM avgt 10 19580.815 ± 397.780 ns/op HpackEncoderBenchmark.encode true true true LARGE avgt 10 379456.381 ± 2059.919 ns/op HpackEncoderBenchmark.encode true true false SMALL avgt 10 730.579 ± 8.116 ns/op HpackEncoderBenchmark.encode true true false MEDIUM avgt 10 2087.590 ± 84.644 ns/op HpackEncoderBenchmark.encode true true false LARGE avgt 10 11725.228 ± 89.298 ns/op HpackEncoderBenchmark.encode true false true SMALL avgt 10 555.971 ± 5.120 ns/op HpackEncoderBenchmark.encode true false true MEDIUM avgt 10 2831.874 ± 41.801 ns/op HpackEncoderBenchmark.encode true false true LARGE avgt 10 36054.025 ± 179.504 ns/op HpackEncoderBenchmark.encode true false false SMALL avgt 10 340.337 ± 3.313 ns/op HpackEncoderBenchmark.encode true false false MEDIUM avgt 10 1006.817 ± 8.942 ns/op HpackEncoderBenchmark.encode true false false LARGE avgt 10 8784.168 ± 164.014 ns/op HpackEncoderBenchmark.encode false true true SMALL avgt 10 2561.934 ± 27.056 ns/op HpackEncoderBenchmark.encode false true true MEDIUM avgt 10 22061.105 ± 154.533 ns/op HpackEncoderBenchmark.encode false true true LARGE avgt 10 435744.897 ± 8853.388 ns/op HpackEncoderBenchmark.encode false true false SMALL avgt 10 2737.683 ± 47.142 ns/op HpackEncoderBenchmark.encode false true false MEDIUM avgt 10 22385.146 ± 98.430 ns/op HpackEncoderBenchmark.encode false true false LARGE avgt 10 408159.698 ± 12044.931 ns/op HpackEncoderBenchmark.encode false false true SMALL avgt 10 544.213 ± 3.279 ns/op HpackEncoderBenchmark.encode false false true MEDIUM avgt 10 2908.978 ± 31.026 ns/op HpackEncoderBenchmark.encode false false true LARGE avgt 10 36471.262 ± 1044.010 ns/op HpackEncoderBenchmark.encode false false false SMALL avgt 10 609.305 ± 4.371 ns/op HpackEncoderBenchmark.encode false false false MEDIUM avgt 10 3223.946 ± 23.505 ns/op HpackEncoderBenchmark.encode false false false LARGE avgt 10 39975.152 ± 655.196 ns/op ``` AFTER: ``` NEW AFTER Benchmark (duplicates) (limitToAscii) (sensitive) (size) Mode Cnt Score Error Units HpackEncoderBenchmark.encode true true true SMALL avgt 5 379.473 ± 133.815 ns/op HpackEncoderBenchmark.encode true true true MEDIUM avgt 5 1118.772 ± 89.258 ns/op HpackEncoderBenchmark.encode true true true LARGE avgt 5 5366.828 ± 89.746 ns/op HpackEncoderBenchmark.encode true true false SMALL avgt 5 284.401 ± 2.088 ns/op HpackEncoderBenchmark.encode true true false MEDIUM avgt 5 922.805 ± 10.796 ns/op HpackEncoderBenchmark.encode true true false LARGE avgt 5 8727.831 ± 462.138 ns/op HpackEncoderBenchmark.encode true false true SMALL avgt 5 337.093 ± 22.585 ns/op HpackEncoderBenchmark.encode true false true MEDIUM avgt 5 693.689 ± 16.351 ns/op HpackEncoderBenchmark.encode true false true LARGE avgt 5 5616.786 ± 98.647 ns/op HpackEncoderBenchmark.encode true false false SMALL avgt 5 286.708 ± 13.765 ns/op HpackEncoderBenchmark.encode true false false MEDIUM avgt 5 906.279 ± 32.338 ns/op HpackEncoderBenchmark.encode true false false LARGE avgt 5 8304.736 ± 128.584 ns/op HpackEncoderBenchmark.encode false true true SMALL avgt 5 351.381 ± 15.547 ns/op HpackEncoderBenchmark.encode false true true MEDIUM avgt 5 1188.166 ± 7.023 ns/op HpackEncoderBenchmark.encode false true true LARGE avgt 5 6876.009 ± 48.117 ns/op HpackEncoderBenchmark.encode false true false SMALL avgt 5 434.759 ± 8.619 ns/op HpackEncoderBenchmark.encode false true false MEDIUM avgt 5 954.588 ± 58.514 ns/op HpackEncoderBenchmark.encode false true false LARGE avgt 5 8534.017 ± 552.597 ns/op HpackEncoderBenchmark.encode false false true SMALL avgt 5 223.713 ± 4.823 ns/op HpackEncoderBenchmark.encode false false true MEDIUM avgt 5 1181.538 ± 11.851 ns/op HpackEncoderBenchmark.encode false false true LARGE avgt 5 6670.830 ± 267.927 ns/op HpackEncoderBenchmark.encode false false false SMALL avgt 5 424.609 ± 27.477 ns/op HpackEncoderBenchmark.encode false false false MEDIUM avgt 5 1003.578 ± 53.991 ns/op HpackEncoderBenchmark.encode false false false LARGE avgt 5 8428.932 ± 102.838 ns/op ```
1 parent 131be58 commit deea51e

File tree

4 files changed

+21
-9
lines changed

4 files changed

+21
-9
lines changed

codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ public DefaultHttp2HeadersEncoder(SensitivityDetector sensitivityDetector, boole
4343

4444
public DefaultHttp2HeadersEncoder(SensitivityDetector sensitivityDetector, boolean ignoreMaxHeaderListSize,
4545
int dynamicTableArraySizeHint) {
46-
this(sensitivityDetector, new HpackEncoder(ignoreMaxHeaderListSize, dynamicTableArraySizeHint));
46+
this(sensitivityDetector, ignoreMaxHeaderListSize, dynamicTableArraySizeHint, HpackEncoder.HUFF_CODE_THRESHOLD);
47+
}
48+
49+
public DefaultHttp2HeadersEncoder(SensitivityDetector sensitivityDetector, boolean ignoreMaxHeaderListSize,
50+
int dynamicTableArraySizeHint, int huffCodeThreshold) {
51+
this(sensitivityDetector,
52+
new HpackEncoder(ignoreMaxHeaderListSize, dynamicTableArraySizeHint, huffCodeThreshold));
4753
}
4854

4955
/**

codec-http2/src/main/java/io/netty/handler/codec/http2/HpackEncoder.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,15 @@
5454
import static java.lang.Math.min;
5555

5656
final class HpackEncoder {
57+
static final int HUFF_CODE_THRESHOLD = 512;
5758
// a linked hash map of header fields
5859
private final HeaderEntry[] headerFields;
5960
private final HeaderEntry head = new HeaderEntry(-1, AsciiString.EMPTY_STRING,
6061
AsciiString.EMPTY_STRING, Integer.MAX_VALUE, null);
6162
private final HpackHuffmanEncoder hpackHuffmanEncoder = new HpackHuffmanEncoder();
6263
private final byte hashMask;
6364
private final boolean ignoreMaxHeaderListSize;
65+
private final int huffCodeThreshold;
6466
private long size;
6567
private long maxHeaderTableSize;
6668
private long maxHeaderListSize;
@@ -76,13 +78,13 @@ final class HpackEncoder {
7678
* Creates a new encoder.
7779
*/
7880
HpackEncoder(boolean ignoreMaxHeaderListSize) {
79-
this(ignoreMaxHeaderListSize, 16);
81+
this(ignoreMaxHeaderListSize, 16, HUFF_CODE_THRESHOLD);
8082
}
8183

8284
/**
8385
* Creates a new encoder.
8486
*/
85-
HpackEncoder(boolean ignoreMaxHeaderListSize, int arraySizeHint) {
87+
HpackEncoder(boolean ignoreMaxHeaderListSize, int arraySizeHint, int huffCodeThreshold) {
8688
this.ignoreMaxHeaderListSize = ignoreMaxHeaderListSize;
8789
maxHeaderTableSize = DEFAULT_HEADER_TABLE_SIZE;
8890
maxHeaderListSize = MAX_HEADER_LIST_SIZE;
@@ -91,6 +93,7 @@ final class HpackEncoder {
9193
headerFields = new HeaderEntry[findNextPositivePowerOfTwo(max(2, min(arraySizeHint, 128)))];
9294
hashMask = (byte) (headerFields.length - 1);
9395
head.before = head.after = head;
96+
this.huffCodeThreshold = huffCodeThreshold;
9497
}
9598

9699
/**
@@ -250,8 +253,9 @@ private static void encodeInteger(ByteBuf out, int mask, int n, long i) {
250253
* Encode string literal according to Section 5.2.
251254
*/
252255
private void encodeStringLiteral(ByteBuf out, CharSequence string) {
253-
int huffmanLength = hpackHuffmanEncoder.getEncodedLength(string);
254-
if (huffmanLength < string.length()) {
256+
int huffmanLength;
257+
if (string.length() >= huffCodeThreshold
258+
&& (huffmanLength = hpackHuffmanEncoder.getEncodedLength(string)) < string.length()) {
255259
encodeInteger(out, 0x80, 7, huffmanLength);
256260
hpackHuffmanEncoder.encode(out, string);
257261
} else {

codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriterTest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package io.netty.handler.codec.http2;
1616

1717
import io.netty.buffer.ByteBuf;
18+
import io.netty.buffer.ByteBufUtil;
1819
import io.netty.buffer.Unpooled;
1920
import io.netty.buffer.UnpooledByteBufAllocator;
2021
import io.netty.channel.Channel;
@@ -65,17 +66,18 @@ public class DefaultHttp2FrameWriterTest {
6566
@Before
6667
public void setUp() throws Exception {
6768
MockitoAnnotations.initMocks(this);
69+
http2HeadersEncoder = new DefaultHttp2HeadersEncoder(
70+
Http2HeadersEncoder.NEVER_SENSITIVE, new HpackEncoder(false, 16, 0));
6871

69-
frameWriter = new DefaultHttp2FrameWriter();
72+
frameWriter = new DefaultHttp2FrameWriter(new DefaultHttp2HeadersEncoder(
73+
Http2HeadersEncoder.NEVER_SENSITIVE, new HpackEncoder(false, 16, 0)));
7074

7175
outbound = Unpooled.buffer();
7276

7377
expectedOutbound = Unpooled.EMPTY_BUFFER;
7478

7579
promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
7680

77-
http2HeadersEncoder = new DefaultHttp2HeadersEncoder();
78-
7981
Answer<Object> answer = new Answer<Object>() {
8082
@Override
8183
public Object answer(InvocationOnMock var1) throws Throwable {

codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public static HpackEncoder newTestEncoder() {
119119

120120
public static HpackEncoder newTestEncoder(boolean ignoreMaxHeaderListSize,
121121
long maxHeaderListSize, long maxHeaderTableSize) throws Http2Exception {
122-
HpackEncoder hpackEncoder = new HpackEncoder();
122+
HpackEncoder hpackEncoder = new HpackEncoder(false, 16, 0);
123123
ByteBuf buf = Unpooled.buffer();
124124
try {
125125
hpackEncoder.setMaxHeaderTableSize(buf, maxHeaderTableSize);

0 commit comments

Comments
 (0)