Browse Source

Make the hashTable argument to CompressBlock optional

Performance is largely unchanged, except that CompressBlock may have to
do an allocation when first run.

Benchmark results on amd64, Go 1.14:

name               old time/op    new time/op    delta
Compress-8           3.15ms ± 1%    3.14ms ± 2%    ~     (p=0.549 n=9+10)
CompressRandom-8     2.95µs ± 1%    2.86µs ± 1%  -3.13%  (p=0.000 n=9+10)
SkipBytesPg1661-8     302µs ± 3%     299µs ± 1%    ~     (p=0.065 n=9+10)
SkipBytesDigits-8    43.5µs ± 3%    47.3µs ± 1%  +8.77%  (p=0.000 n=10+10)
SkipBytesTwain-8      197µs ± 1%     197µs ± 0%    ~     (p=0.211 n=9+10)
SkipBytesRand-8      3.09µs ± 1%    3.09µs ± 1%    ~     (p=0.735 n=9+10)
CompressPg1661-8     26.8µs ± 2%    26.9µs ± 1%    ~     (p=0.143 n=10+10)
CompressDigits-8     3.64µs ± 1%    3.59µs ± 1%  -1.63%  (p=0.000 n=10+10)
CompressTwain-8      17.1µs ± 1%    17.4µs ± 1%  +1.42%  (p=0.003 n=10+9)
CompressRand-8        623ns ± 1%     626ns ± 3%    ~     (p=0.698 n=10+10)

name               old alloc/op   new alloc/op   delta
Compress-8            0.00B       1398.11B ± 3%   +Inf%  (p=0.000 n=10+9)
CompressRandom-8      0.00B          1.00B ± 0%   +Inf%  (p=0.000 n=10+10)
SkipBytesPg1661-8     0.00B          0.00B         ~     (all equal)
SkipBytesDigits-8     0.00B          0.00B         ~     (all equal)
SkipBytesTwain-8      0.00B          0.00B         ~     (all equal)
SkipBytesRand-8       0.00B          0.00B         ~     (all equal)
CompressPg1661-8      46.3B ± 2%     46.6B ± 1%    ~     (p=0.370 n=10+10)
CompressDigits-8      52.6B ± 3%     52.2B ± 2%    ~     (p=0.424 n=8+9)
CompressTwain-8       51.4B ± 1%     52.0B ± 0%  +1.17%  (p=0.011 n=10+10)
CompressRand-8        57.8B ±15%     55.2B ±11%    ~     (p=0.220 n=10+9)

name               old allocs/op  new allocs/op  delta
Compress-8             0.00           0.00         ~     (all equal)
CompressRandom-8       0.00           0.00         ~     (all equal)
SkipBytesPg1661-8      0.00           0.00         ~     (all equal)
SkipBytesDigits-8      0.00           0.00         ~     (all equal)
SkipBytesTwain-8       0.00           0.00         ~     (all equal)
SkipBytesRand-8        0.00           0.00         ~     (all equal)
CompressPg1661-8       1.00 ± 0%      1.00 ± 0%    ~     (all equal)
CompressDigits-8       1.00 ± 0%      1.00 ± 0%    ~     (all equal)
CompressTwain-8        1.00 ± 0%      1.00 ± 0%    ~     (all equal)
CompressRand-8         1.00 ± 0%      1.00 ± 0%    ~     (all equal)

name               old speed      new speed      delta
CompressRandom-8   5.55GB/s ± 1%  5.73GB/s ± 1%  +3.24%  (p=0.000 n=9+10)
SkipBytesPg1661-8  1.97GB/s ± 3%  1.99GB/s ± 1%    ~     (p=0.065 n=9+10)
SkipBytesDigits-8  2.30GB/s ± 3%  2.11GB/s ± 1%  -8.08%  (p=0.000 n=10+10)
SkipBytesTwain-8   1.97GB/s ± 1%  1.97GB/s ± 0%    ~     (p=0.211 n=9+10)
SkipBytesRand-8    5.30GB/s ± 1%  5.29GB/s ± 1%    ~     (p=0.720 n=9+10)
CompressPg1661-8   22.2GB/s ± 2%  22.1GB/s ± 1%    ~     (p=0.143 n=10+10)
CompressDigits-8   27.4GB/s ± 1%  27.9GB/s ± 1%  +1.66%  (p=0.000 n=10+10)
CompressTwain-8    22.6GB/s ± 1%  22.3GB/s ± 1%  -1.40%  (p=0.003 n=10+9)
CompressRand-8     26.3GB/s ± 1%  26.2GB/s ± 3%    ~     (p=0.684 n=10+10)
greatroar 5 years ago
parent
commit
2df7315854
2 changed files with 20 additions and 9 deletions
  1. 2 4
      bench_test.go
  2. 18 5
      block.go

+ 2 - 4
bench_test.go

@@ -10,19 +10,17 @@ import (
 )
 
 func BenchmarkCompress(b *testing.B) {
-	var hashTable [htSize]int
 	buf := make([]byte, len(pg1661))
 
 	b.ReportAllocs()
 	b.ResetTimer()
 
 	for i := 0; i < b.N; i++ {
-		_, _ = lz4.CompressBlock(pg1661, buf, hashTable[:])
+		_, _ = lz4.CompressBlock(pg1661, buf, nil)
 	}
 }
 
 func BenchmarkCompressRandom(b *testing.B) {
-	var hashTable [htSize]int
 	buf := make([]byte, len(randomLZ4))
 
 	b.ReportAllocs()
@@ -30,7 +28,7 @@ func BenchmarkCompressRandom(b *testing.B) {
 	b.ResetTimer()
 
 	for i := 0; i < b.N; i++ {
-		_, _ = lz4.CompressBlock(random, buf, hashTable[:])
+		_, _ = lz4.CompressBlock(random, buf, nil)
 	}
 }
 

+ 18 - 5
block.go

@@ -2,8 +2,8 @@ package lz4
 
 import (
 	"encoding/binary"
-	"fmt"
 	"math/bits"
+	"sync"
 )
 
 // blockHash hashes the lower 6 bytes into a value < htSize.
@@ -35,15 +35,15 @@ func UncompressBlock(src, dst []byte) (int, error) {
 
 // CompressBlock compresses the source buffer into the destination one.
 // This is the fast version of LZ4 compression and also the default one.
-// The size of hashTable must be at least 1<<16.
+//
+// The argument hashTable is scratch space for a hash table used by the
+// compressor. It provided, it should have length at least 1<<16. If it is
+// shorter (or nil), CompressBlock allocates its own hash table.
 //
 // The size of the compressed data is returned. If it is 0 and no error, then the data is incompressible.
 //
 // An error is returned if the destination buffer is too small.
 func CompressBlock(src, dst []byte, hashTable []int) (_ int, err error) {
-	if len(hashTable) < htSize {
-		return 0, fmt.Errorf("hash table too small, should be at least %d in size", htSize)
-	}
 	defer recoverBlock(&err)
 
 	// adaptSkipLog sets how quickly the compressor begins skipping blocks when data is incompressible.
@@ -54,6 +54,12 @@ func CompressBlock(src, dst []byte, hashTable []int) (_ int, err error) {
 	if sn <= 0 || dn == 0 {
 		return 0, nil
 	}
+
+	if len(hashTable) < htSize {
+		htIface := htPool.Get()
+		defer htPool.Put(htIface)
+		hashTable = (*(htIface).(*[htSize]int))[:]
+	}
 	// Prove to the compiler the table has at least htSize elements.
 	// The compiler can see that "uint32() >> hashShift" cannot be out of bounds.
 	hashTable = hashTable[:htSize]
@@ -213,6 +219,13 @@ func CompressBlock(src, dst []byte, hashTable []int) (_ int, err error) {
 	return di, nil
 }
 
+// Pool of hash tables for CompressBlock.
+var htPool = sync.Pool{
+	New: func() interface{} {
+		return new([htSize]int)
+	},
+}
+
 // blockHash hashes 4 bytes into a value < winSize.
 func blockHashHC(x uint32) uint32 {
 	const hasher uint32 = 2654435761 // Knuth multiplicative hash.