Skip to content

Commit 05bf781

Browse files
author
teivah
committed
Improve spatial locality by handling the huffman code as a struct of slices instead of a slice of structs.
1 parent 85afa2e commit 05bf781

File tree

2 files changed

+34
-32
lines changed

2 files changed

+34
-32
lines changed

src/compress/flate/huffman_bit_writer.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,12 @@ func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int, litE
209209
// Copy the concatenated code sizes to codegen. Put a marker at the end.
210210
cgnl := codegen[:numLiterals]
211211
for i := range cgnl {
212-
cgnl[i] = uint8(litEnc.codes[i].len)
212+
cgnl[i] = uint8(litEnc.codes.len[i])
213213
}
214214

215215
cgnl = codegen[numLiterals : numLiterals+numOffsets]
216216
for i := range cgnl {
217-
cgnl[i] = uint8(offEnc.codes[i].len)
217+
cgnl[i] = uint8(offEnc.codes.len[i])
218218
}
219219
codegen[numLiterals+numOffsets] = badCode
220220

@@ -324,12 +324,12 @@ func (w *huffmanBitWriter) storedSize(in []byte) (int, bool) {
324324
return 0, false
325325
}
326326

327-
func (w *huffmanBitWriter) writeCode(c hcode) {
327+
func (w *huffmanBitWriter) writeCode(codes, lens []uint16, index uint32) {
328328
if w.err != nil {
329329
return
330330
}
331-
w.bits |= uint64(c.code) << w.nbits
332-
w.nbits += uint(c.len)
331+
w.bits |= uint64(codes[index]) << w.nbits
332+
w.nbits += uint(lens[index])
333333
if w.nbits >= 48 {
334334
bits := w.bits
335335
w.bits >>= 48
@@ -370,7 +370,7 @@ func (w *huffmanBitWriter) writeDynamicHeader(numLiterals int, numOffsets int, n
370370
w.writeBits(int32(numCodegens-4), 4)
371371

372372
for i := 0; i < numCodegens; i++ {
373-
value := uint(w.codegenEncoding.codes[codegenOrder[i]].len)
373+
value := uint(w.codegenEncoding.codes.len[codegenOrder[i]])
374374
w.writeBits(int32(value), 3)
375375
}
376376

@@ -381,7 +381,7 @@ func (w *huffmanBitWriter) writeDynamicHeader(numLiterals int, numOffsets int, n
381381
if codeWord == badCode {
382382
break
383383
}
384-
w.writeCode(w.codegenEncoding.codes[uint32(codeWord)])
384+
w.writeCode(w.codegenEncoding.codes.code, w.codegenEncoding.codes.len, uint32(codeWord))
385385

386386
switch codeWord {
387387
case 16:
@@ -574,19 +574,19 @@ func (w *huffmanBitWriter) indexTokens(tokens []token) (numLiterals, numOffsets
574574

575575
// writeTokens writes a slice of tokens to the output.
576576
// codes for literal and offset encoding must be supplied.
577-
func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode) {
577+
func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes hcode) {
578578
if w.err != nil {
579579
return
580580
}
581581
for _, t := range tokens {
582582
if t < matchType {
583-
w.writeCode(leCodes[t.literal()])
583+
w.writeCode(leCodes.code, leCodes.len, t.literal())
584584
continue
585585
}
586586
// Write the length
587587
length := t.length()
588588
lengthCode := lengthCode(length)
589-
w.writeCode(leCodes[lengthCode+lengthCodesStart])
589+
w.writeCode(leCodes.code, leCodes.len, lengthCode+lengthCodesStart)
590590
extraLengthBits := uint(lengthExtraBits[lengthCode])
591591
if extraLengthBits > 0 {
592592
extraLength := int32(length - lengthBase[lengthCode])
@@ -595,7 +595,7 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
595595
// Write the offset
596596
offset := t.offset()
597597
offsetCode := offsetCode(offset)
598-
w.writeCode(oeCodes[offsetCode])
598+
w.writeCode(oeCodes.code, oeCodes.len, offsetCode)
599599
extraOffsetBits := uint(offsetExtraBits[offsetCode])
600600
if extraOffsetBits > 0 {
601601
extraOffset := int32(offset - offsetBase[offsetCode])
@@ -658,13 +658,13 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
658658

659659
// Huffman.
660660
w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof)
661-
encoding := w.literalEncoding.codes[:257]
661+
encodingCode := w.literalEncoding.codes.code[:257]
662+
encodingLen := w.literalEncoding.codes.len[:257]
662663
n := w.nbytes
663664
for _, t := range input {
664665
// Bitwriting inlined, ~30% speedup
665-
c := encoding[t]
666-
w.bits |= uint64(c.code) << w.nbits
667-
w.nbits += uint(c.len)
666+
w.bits |= uint64(encodingCode[t]) << w.nbits
667+
w.nbits += uint(encodingLen[t])
668668
if w.nbits < 48 {
669669
continue
670670
}
@@ -690,7 +690,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
690690
n = 0
691691
}
692692
w.nbytes = n
693-
w.writeCode(encoding[endBlockMarker])
693+
w.writeCode(encodingCode, encodingLen, endBlockMarker)
694694
}
695695

696696
// histogram accumulates a histogram of b in h.

src/compress/flate/huffman_code.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ import (
1212

1313
// hcode is a huffman code with a bit code and bit length.
1414
type hcode struct {
15-
code, len uint16
15+
code []uint16
16+
len []uint16
1617
}
1718

1819
type huffmanEncoder struct {
19-
codes []hcode
20+
codes hcode
2021
freqcache []literalNode
2122
bitCount [17]int32
2223
lns byLiteral // stored to avoid repeated allocation in generate
@@ -48,16 +49,13 @@ type levelInfo struct {
4849
needed int32
4950
}
5051

51-
// set sets the code and length of an hcode.
52-
func (h *hcode) set(code uint16, length uint16) {
53-
h.len = length
54-
h.code = code
55-
}
56-
5752
func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxInt32} }
5853

5954
func newHuffmanEncoder(size int) *huffmanEncoder {
60-
return &huffmanEncoder{codes: make([]hcode, size)}
55+
return &huffmanEncoder{codes: hcode{
56+
code: make([]uint16, size),
57+
len: make([]uint16, size),
58+
}}
6159
}
6260

6361
// Generates a HuffmanCode corresponding to the fixed literal table
@@ -89,16 +87,18 @@ func generateFixedLiteralEncoding() *huffmanEncoder {
8987
bits = ch + 192 - 280
9088
size = 8
9189
}
92-
codes[ch] = hcode{code: reverseBits(bits, byte(size)), len: size}
90+
codes.code[ch] = reverseBits(bits, byte(size))
91+
codes.len[ch] = size
9392
}
9493
return h
9594
}
9695

9796
func generateFixedOffsetEncoding() *huffmanEncoder {
9897
h := newHuffmanEncoder(30)
9998
codes := h.codes
100-
for ch := range codes {
101-
codes[ch] = hcode{code: reverseBits(uint16(ch), 5), len: 5}
99+
for i := 0; i < len(codes.code); i++ {
100+
codes.code[i] = reverseBits(uint16(i), 5)
101+
codes.len[i] = 5
102102
}
103103
return h
104104
}
@@ -110,7 +110,7 @@ func (h *huffmanEncoder) bitLength(freq []int32) int {
110110
var total int
111111
for i, f := range freq {
112112
if f != 0 {
113-
total += int(f) * int(h.codes[i].len)
113+
total += int(f) * int(h.codes.len[i])
114114
}
115115
}
116116
return total
@@ -260,7 +260,8 @@ func (h *huffmanEncoder) assignEncodingAndSize(bitCount []int32, list []literalN
260260

261261
h.lns.sort(chunk)
262262
for _, node := range chunk {
263-
h.codes[node.literal] = hcode{code: reverseBits(code, uint8(n)), len: uint16(n)}
263+
h.codes.code[node.literal] = reverseBits(code, uint8(n))
264+
h.codes.len[node.literal] = uint16(n)
264265
code++
265266
}
266267
list = list[0 : len(list)-int(bits)]
@@ -288,7 +289,7 @@ func (h *huffmanEncoder) generate(freq []int32, maxBits int32) {
288289
count++
289290
} else {
290291
list[count] = literalNode{}
291-
h.codes[i].len = 0
292+
h.codes.len[i] = 0
292293
}
293294
}
294295
list[len(freq)] = literalNode{}
@@ -299,7 +300,8 @@ func (h *huffmanEncoder) generate(freq []int32, maxBits int32) {
299300
// two or fewer literals, everything has bit length 1.
300301
for i, node := range list {
301302
// "list" is in order of increasing literal value.
302-
h.codes[node.literal].set(uint16(i), 1)
303+
h.codes.code[node.literal] = uint16(i)
304+
h.codes.len[node.literal] = 1
303305
}
304306
return
305307
}

0 commit comments

Comments
 (0)