Skip to content

Commit

Permalink
flate: Faster hashing for level 7-9 (#590)
Browse files Browse the repository at this point in the history
Remove stateful hash and need for AND operation.

Benchmarks show no change.

Fixes #589
  • Loading branch information
klauspost committed May 23, 2022
1 parent e77bf31 commit 4703195
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 23 deletions.
36 changes: 14 additions & 22 deletions flate/deflate.go
Expand Up @@ -84,24 +84,23 @@ type advancedState struct {
length int
offset int
maxInsertIndex int
chainHead int
hashOffset int

// Input hash chains
// hashHead[hashValue] contains the largest inputIndex with the specified hash value
// If hashHead[hashValue] is within the current window, then
// hashPrev[hashHead[hashValue] & windowMask] contains the previous index
// with the same hash value.
chainHead int
hashHead [hashSize]uint32
hashPrev [windowSize]uint32
hashOffset int
ii uint16 // position of last match, intended to overflow to reset.

// input window: unprocessed data is window[index:windowEnd]
index int
estBitsPerByte int
hashMatch [maxMatchLength + minMatchLength]uint32

hash uint32
ii uint16 // position of last match, intended to overflow to reset.
// Input hash chains
// hashHead[hashValue] contains the largest inputIndex with the specified hash value
// If hashHead[hashValue] is within the current window, then
// hashPrev[hashHead[hashValue] & windowMask] contains the previous index
// with the same hash value.
hashHead [hashSize]uint32
hashPrev [windowSize]uint32
}

type compressor struct {
Expand Down Expand Up @@ -259,7 +258,6 @@ func (d *compressor) fillWindow(b []byte) {
// Set the head of the hash chain to us.
s.hashHead[newH] = uint32(di + s.hashOffset)
}
s.hash = newH
}
// Update window information.
d.windowEnd += n
Expand Down Expand Up @@ -403,7 +401,6 @@ func (d *compressor) initDeflate() {
s.hashOffset = 1
s.length = minMatchLength - 1
s.offset = 0
s.hash = 0
s.chainHead = -1
}

Expand Down Expand Up @@ -432,9 +429,6 @@ func (d *compressor) deflateLazy() {
}

s.maxInsertIndex = d.windowEnd - (minMatchLength - 1)
if s.index < s.maxInsertIndex {
s.hash = hash4(d.window[s.index:])
}

for {
if sanity && s.index > d.windowEnd {
Expand Down Expand Up @@ -466,11 +460,11 @@ func (d *compressor) deflateLazy() {
}
if s.index < s.maxInsertIndex {
// Update the hash
s.hash = hash4(d.window[s.index:])
ch := s.hashHead[s.hash&hashMask]
hash := hash4(d.window[s.index:])
ch := s.hashHead[hash]
s.chainHead = int(ch)
s.hashPrev[s.index&windowMask] = ch
s.hashHead[s.hash&hashMask] = uint32(s.index + s.hashOffset)
s.hashHead[hash] = uint32(s.index + s.hashOffset)
}
prevLength := s.length
prevOffset := s.offset
Expand Down Expand Up @@ -503,7 +497,7 @@ func (d *compressor) deflateLazy() {
end += prevIndex
idx := prevIndex + prevLength - (4 - checkOff)
h := hash4(d.window[idx:])
ch2 := int(s.hashHead[h&hashMask]) - s.hashOffset - prevLength + (4 - checkOff)
ch2 := int(s.hashHead[h]) - s.hashOffset - prevLength + (4 - checkOff)
if ch2 > minIndex {
length := matchLen(d.window[prevIndex:end], d.window[ch2:])
// It seems like a pure length metric is best.
Expand Down Expand Up @@ -547,7 +541,6 @@ func (d *compressor) deflateLazy() {
// Set the head of the hash chain to us.
s.hashHead[newH] = uint32(di + s.hashOffset)
}
s.hash = newH
}

s.index = newIndex
Expand Down Expand Up @@ -793,7 +786,6 @@ func (d *compressor) reset(w io.Writer) {
d.tokens.Reset()
s.length = minMatchLength - 1
s.offset = 0
s.hash = 0
s.ii = 0
s.maxInsertIndex = 0
}
Expand Down
2 changes: 1 addition & 1 deletion flate/fast_encoder.go
Expand Up @@ -117,7 +117,7 @@ func (e *fastGen) addBlock(src []byte) int32 {
// hash4 returns the hash of u to fit in a hash table with h bits.
// Preferably h should be a constant and should always be <32.
func hash4u(u uint32, h uint8) uint32 {
return (u * prime4bytes) >> ((32 - h) & reg8SizeMask32)
return (u * prime4bytes) >> (32 - h)
}

type tableEntryPrev struct {
Expand Down

0 comments on commit 4703195

Please sign in to comment.