Skip to content

Commit ff0045e

Browse files
carl-mastrangelonormanmaurer
authored andcommitted
Use Table lookup for HPACK decoder (netty#9307)
Motivation: Table based decoding is fast. Modification: Use table based decoding in HPACK decoder, inspired by https://github.com/python-hyper/hpack/blob/master/hpack/huffman_table.py This modifies the table to be based on integers, rather than 3-tuples of bytes. This is for two reasons: 1. It's faster 2. Using bytes makes the static intializer too big, and doesn't compile. Result: Faster Huffman decoding. This only seems to help the ascii case, the other decoding is about the same. Benchmarks: ``` Before: Benchmark (limitToAscii) (sensitive) (size) Mode Cnt Score Error Units HpackDecoderBenchmark.decode true true SMALL thrpt 20 426293.636 ± 1444.843 ops/s HpackDecoderBenchmark.decode true true MEDIUM thrpt 20 57843.738 ± 725.704 ops/s HpackDecoderBenchmark.decode true true LARGE thrpt 20 3002.412 ± 16.998 ops/s HpackDecoderBenchmark.decode true false SMALL thrpt 20 412339.400 ± 1128.394 ops/s HpackDecoderBenchmark.decode true false MEDIUM thrpt 20 58226.870 ± 199.591 ops/s HpackDecoderBenchmark.decode true false LARGE thrpt 20 3044.256 ± 10.675 ops/s HpackDecoderBenchmark.decode false true SMALL thrpt 20 2082615.030 ± 5929.726 ops/s HpackDecoderBenchmark.decode false true MEDIUM thrpt 10 571640.454 ± 26499.229 ops/s HpackDecoderBenchmark.decode false true LARGE thrpt 20 92714.555 ± 2292.222 ops/s HpackDecoderBenchmark.decode false false SMALL thrpt 20 1745872.421 ± 6788.840 ops/s HpackDecoderBenchmark.decode false false MEDIUM thrpt 20 490420.323 ± 2455.431 ops/s HpackDecoderBenchmark.decode false false LARGE thrpt 20 84536.200 ± 398.714 ops/s After(bytes): Benchmark (limitToAscii) (sensitive) (size) Mode Cnt Score Error Units HpackDecoderBenchmark.decode true true SMALL thrpt 20 472649.148 ± 7122.461 ops/s HpackDecoderBenchmark.decode true true MEDIUM thrpt 20 66739.638 ± 341.607 ops/s HpackDecoderBenchmark.decode true true LARGE thrpt 20 3139.773 ± 24.491 ops/s HpackDecoderBenchmark.decode true false SMALL thrpt 20 466933.833 ± 4514.971 ops/s HpackDecoderBenchmark.decode true false MEDIUM thrpt 20 66111.778 ± 568.326 ops/s HpackDecoderBenchmark.decode true false LARGE thrpt 20 3143.619 ± 3.332 ops/s HpackDecoderBenchmark.decode false true SMALL thrpt 20 2109995.177 ± 6203.143 ops/s HpackDecoderBenchmark.decode false true MEDIUM thrpt 20 586026.055 ± 1578.550 ops/s HpackDecoderBenchmark.decode false false SMALL thrpt 20 1775723.270 ± 4932.057 ops/s HpackDecoderBenchmark.decode false false MEDIUM thrpt 20 493316.467 ± 1453.037 ops/s HpackDecoderBenchmark.decode false false LARGE thrpt 10 85726.219 ± 402.573 ops/s After(ints): Benchmark (limitToAscii) (sensitive) (size) Mode Cnt Score Error Units HpackDecoderBenchmark.decode true true SMALL thrpt 20 615549.006 ± 5282.283 ops/s HpackDecoderBenchmark.decode true true MEDIUM thrpt 20 86714.630 ± 654.489 ops/s HpackDecoderBenchmark.decode true true LARGE thrpt 20 3984.439 ± 61.612 ops/s HpackDecoderBenchmark.decode true false SMALL thrpt 20 602489.337 ± 5397.024 ops/s HpackDecoderBenchmark.decode true false MEDIUM thrpt 20 88399.109 ± 241.115 ops/s HpackDecoderBenchmark.decode true false LARGE thrpt 20 3875.729 ± 103.057 ops/s HpackDecoderBenchmark.decode false true SMALL thrpt 20 2092165.454 ± 11918.859 ops/s HpackDecoderBenchmark.decode false true MEDIUM thrpt 20 583465.437 ± 5452.115 ops/s HpackDecoderBenchmark.decode false true LARGE thrpt 20 93290.061 ± 665.904 ops/s HpackDecoderBenchmark.decode false false SMALL thrpt 20 1758402.495 ± 14677.438 ops/s HpackDecoderBenchmark.decode false false MEDIUM thrpt 10 491598.099 ± 5029.698 ops/s HpackDecoderBenchmark.decode false false LARGE thrpt 20 85834.290 ± 554.915 ops/s ```
1 parent 18e4121 commit ff0045e

18 files changed

+4749
-227
lines changed

NOTICE.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,22 @@ the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at:
205205
* license/LICENSE.hpack.txt (Apache License 2.0)
206206
* HOMEPAGE:
207207
* https://github.com/twitter/hpack
208+
209+
This product contains a modified version of 'HPACK', a Java implementation of
210+
the HTTP/2 HPACK algorithm written by Cory Benfield. It can be obtained at:
211+
212+
* LICENSE:
213+
* license/LICENSE.hyper-hpack.txt (MIT License)
214+
* HOMEPAGE:
215+
* https://github.com/python-hyper/hpack/
216+
217+
This product contains a modified version of 'HPACK', a Java implementation of
218+
the HTTP/2 HPACK algorithm written by Tatsuhiro Tsujikawa. It can be obtained at:
219+
220+
* LICENSE:
221+
* license/LICENSE.nghttp2-hpack.txt (MIT License)
222+
* HOMEPAGE:
223+
* https://github.com/nghttp2/nghttp2/
208224

209225
This product contains a modified portion of 'Apache Commons Lang', a Java library
210226
provides utilities for the java.lang API, which can be obtained at:

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import io.netty.util.internal.UnstableApi;
2222

2323
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
24-
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_INITIAL_HUFFMAN_DECODE_CAPACITY;
2524
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_RESERVED_STREAMS;
2625
import static io.netty.handler.codec.http2.Http2PromisedRequestVerifier.ALWAYS_VERIFY;
2726
import static io.netty.util.internal.ObjectUtil.checkNotNull;
@@ -64,7 +63,6 @@
6463
* <li>{@link #headerSensitivityDetector(SensitivityDetector)}</li>
6564
* <li>{@link #encoderEnforceMaxConcurrentStreams(boolean)}</li>
6665
* <li>{@link #encoderIgnoreMaxHeaderListSize(boolean)}</li>
67-
* <li>{@link #initialHuffmanDecodeCapacity(int)}</li>
6866
* </ul>
6967
*
7068
* <h3>Exposing necessary methods in a subclass</h3>
@@ -106,7 +104,6 @@ public abstract class AbstractHttp2ConnectionHandlerBuilder<T extends Http2Conne
106104
private SensitivityDetector headerSensitivityDetector;
107105
private Boolean encoderEnforceMaxConcurrentStreams;
108106
private Boolean encoderIgnoreMaxHeaderListSize;
109-
private int initialHuffmanDecodeCapacity = DEFAULT_INITIAL_HUFFMAN_DECODE_CAPACITY;
110107
private Http2PromisedRequestVerifier promisedRequestVerifier = ALWAYS_VERIFY;
111108
private boolean autoAckSettingsFrame = true;
112109

@@ -357,13 +354,12 @@ protected B encoderIgnoreMaxHeaderListSize(boolean ignoreMaxHeaderListSize) {
357354
}
358355

359356
/**
360-
* Sets the initial size of an intermediate buffer used during HPACK huffman decoding.
361-
* @param initialHuffmanDecodeCapacity initial size of an intermediate buffer used during HPACK huffman decoding.
362-
* @return this.
357+
* Does nothing, do not call.
358+
*
359+
* @deprecated Huffman decoding no longer depends on having a decode capacity.
363360
*/
361+
@Deprecated
364362
protected B initialHuffmanDecodeCapacity(int initialHuffmanDecodeCapacity) {
365-
enforceNonCodecConstraints("initialHuffmanDecodeCapacity");
366-
this.initialHuffmanDecodeCapacity = checkPositive(initialHuffmanDecodeCapacity, "initialHuffmanDecodeCapacity");
367363
return self();
368364
}
369365

@@ -442,7 +438,7 @@ private T buildFromConnection(Http2Connection connection) {
442438
Long maxHeaderListSize = initialSettings.maxHeaderListSize();
443439
Http2FrameReader reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(isValidateHeaders(),
444440
maxHeaderListSize == null ? DEFAULT_HEADER_LIST_SIZE : maxHeaderListSize,
445-
initialHuffmanDecodeCapacity));
441+
/* initialHuffmanDecodeCapacity= */ -1));
446442
Http2FrameWriter writer = encoderIgnoreMaxHeaderListSize == null ?
447443
new DefaultHttp2FrameWriter(headerSensitivityDetector()) :
448444
new DefaultHttp2FrameWriter(headerSensitivityDetector(), encoderIgnoreMaxHeaderListSize);

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import io.netty.util.internal.UnstableApi;
2121

2222
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
23-
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_INITIAL_HUFFMAN_DECODE_CAPACITY;
2423
import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
2524
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
2625
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
@@ -57,7 +56,7 @@ public DefaultHttp2HeadersDecoder(boolean validateHeaders) {
5756
* (which is dangerous).
5857
*/
5958
public DefaultHttp2HeadersDecoder(boolean validateHeaders, long maxHeaderListSize) {
60-
this(validateHeaders, maxHeaderListSize, DEFAULT_INITIAL_HUFFMAN_DECODE_CAPACITY);
59+
this(validateHeaders, maxHeaderListSize, /* initialHuffmanDecodeCapacity= */ -1);
6160
}
6261

6362
/**
@@ -67,11 +66,11 @@ public DefaultHttp2HeadersDecoder(boolean validateHeaders, long maxHeaderListSiz
6766
* This is because <a href="https://tools.ietf.org/html/rfc7540#section-6.5.1">SETTINGS_MAX_HEADER_LIST_SIZE</a>
6867
* allows a lower than advertised limit from being enforced, and the default limit is unlimited
6968
* (which is dangerous).
70-
* @param initialHuffmanDecodeCapacity Size of an intermediate buffer used during huffman decode.
69+
* @param initialHuffmanDecodeCapacity Does nothing, do not use.
7170
*/
7271
public DefaultHttp2HeadersDecoder(boolean validateHeaders, long maxHeaderListSize,
73-
int initialHuffmanDecodeCapacity) {
74-
this(validateHeaders, new HpackDecoder(maxHeaderListSize, initialHuffmanDecodeCapacity));
72+
@Deprecated int initialHuffmanDecodeCapacity) {
73+
this(validateHeaders, new HpackDecoder(maxHeaderListSize));
7574
}
7675

7776
/**

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ final class HpackDecoder {
9090
private static final byte READ_LITERAL_HEADER_VALUE = 9;
9191

9292
private final HpackDynamicTable hpackDynamicTable;
93-
private final HpackHuffmanDecoder hpackHuffmanDecoder;
9493
private long maxHeaderListSize;
9594
private long maxDynamicTableSize;
9695
private long encoderMaxDynamicTableSize;
@@ -102,23 +101,21 @@ final class HpackDecoder {
102101
* This is because <a href="https://tools.ietf.org/html/rfc7540#section-6.5.1">SETTINGS_MAX_HEADER_LIST_SIZE</a>
103102
* allows a lower than advertised limit from being enforced, and the default limit is unlimited
104103
* (which is dangerous).
105-
* @param initialHuffmanDecodeCapacity Size of an intermediate buffer used during huffman decode.
106104
*/
107-
HpackDecoder(long maxHeaderListSize, int initialHuffmanDecodeCapacity) {
108-
this(maxHeaderListSize, initialHuffmanDecodeCapacity, DEFAULT_HEADER_TABLE_SIZE);
105+
HpackDecoder(long maxHeaderListSize) {
106+
this(maxHeaderListSize, DEFAULT_HEADER_TABLE_SIZE);
109107
}
110108

111109
/**
112110
* Exposed Used for testing only! Default values used in the initial settings frame are overridden intentionally
113111
* for testing but violate the RFC if used outside the scope of testing.
114112
*/
115-
HpackDecoder(long maxHeaderListSize, int initialHuffmanDecodeCapacity, int maxHeaderTableSize) {
113+
HpackDecoder(long maxHeaderListSize, int maxHeaderTableSize) {
116114
this.maxHeaderListSize = checkPositive(maxHeaderListSize, "maxHeaderListSize");
117115

118116
maxDynamicTableSize = encoderMaxDynamicTableSize = maxHeaderTableSize;
119117
maxDynamicTableSizeChangeRequired = false;
120118
hpackDynamicTable = new HpackDynamicTable(maxHeaderTableSize);
121-
hpackHuffmanDecoder = new HpackHuffmanDecoder(initialHuffmanDecodeCapacity);
122119
}
123120

124121
/**
@@ -448,7 +445,7 @@ private void insertHeader(Sink sink, CharSequence name, CharSequence value, Inde
448445

449446
private CharSequence readStringLiteral(ByteBuf in, int length, boolean huffmanEncoded) throws Http2Exception {
450447
if (huffmanEncoded) {
451-
return hpackHuffmanDecoder.decode(in, length);
448+
return HpackHuffmanDecoder.decode(in, length);
452449
}
453450
byte[] buf = new byte[length];
454451
in.readBytes(buf);

0 commit comments

Comments
 (0)