From 73689824647bbfc78e4a986b757afeb664358da5 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 1 Aug 2018 17:17:55 +0000 Subject: [PATCH] http2/hpack: reduce memory for huffman decoding table Reduces process-wide heap (inuse_space) by 60kB by using a pointer to a fixed-sized array instead of a slice of a fixed size. Before: 119.44kB 23.43% 23.43% 147.88kB 29.01% golang.org/x/net/http2/hpack.addDecoderNode After: 59.72kB 13.28% 39.85% 87.94kB 19.56% golang.org/x/net/http2/hpack.addDecoderNode (This is all work from an init func in http2/hpack) Doesn't seem to affect runtime performance. Measured with: $ cat huffman_test.go package main import ( "testing" _ "golang.org/x/net/http2" ) func TestMem(t *testing.T) {} $ GODEBUG=memprofilerate=1 go test -memprofilerate=1 -memprofile=mem.prof -v . === RUN TestMem --- PASS: TestMem (0.00s) PASS ok huffmem 0.052s $ go tool pprof --inuse_space mem.prof Change-Id: I5e56a5a2682f1063c955b342b37e97ca4c303dab Reviewed-on: https://go-review.googlesource.com/127235 Reviewed-by: Ian Lance Taylor --- hpack/hpack_test.go | 21 +++++++++++++++++++++ hpack/huffman.go | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/hpack/hpack_test.go b/hpack/hpack_test.go index 974c35f0..3f222744 100644 --- a/hpack/hpack_test.go +++ b/hpack/hpack_test.go @@ -462,6 +462,27 @@ func TestHuffmanDecode(t *testing.T) { } } +func BenchmarkHuffmanDecode(b *testing.B) { + b.StopTimer() + enc, err := hex.DecodeString(strings.Replace("94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07", + " ", "", -1)) + if err != nil { + b.Fatal(err) + } + b.ReportAllocs() + b.StartTimer() + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + if _, err := HuffmanDecode(&buf, enc); err != nil { + b.Fatalf("decode error: %v", err) + } + if string(buf.Bytes()) != "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" { + b.Fatalf("bogus output %q", buf.Bytes()) + } + } +} + func TestAppendHuffmanString(t *testing.T) { tests := []struct { in, want string diff --git a/hpack/huffman.go b/hpack/huffman.go index 8850e394..87ec2aaa 100644 --- a/hpack/huffman.go +++ b/hpack/huffman.go @@ -106,7 +106,7 @@ func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error { type node struct { // children is non-nil for internal nodes - children []*node + children *[256]*node // The following are only valid if children is nil: codeLen uint8 // number of bits that led to the output of sym @@ -114,7 +114,7 @@ type node struct { } func newInternalNode() *node { - return &node{children: make([]*node, 256)} + return &node{children: new([256]*node)} } var rootHuffmanNode = newInternalNode()