Explorar o código

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 %!s(int64=6) %!d(string=hai) anos
pai
achega
2df7315854
Modificáronse 2 ficheiros con 20 adicións e 9 borrados
  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) {
 func BenchmarkCompress(b *testing.B) {
-	var hashTable [htSize]int
 	buf := make([]byte, len(pg1661))
 	buf := make([]byte, len(pg1661))
 
 
 	b.ReportAllocs()
 	b.ReportAllocs()
 	b.ResetTimer()
 	b.ResetTimer()
 
 
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {
-		_, _ = lz4.CompressBlock(pg1661, buf, hashTable[:])
+		_, _ = lz4.CompressBlock(pg1661, buf, nil)
 	}
 	}
 }
 }
 
 
 func BenchmarkCompressRandom(b *testing.B) {
 func BenchmarkCompressRandom(b *testing.B) {
-	var hashTable [htSize]int
 	buf := make([]byte, len(randomLZ4))
 	buf := make([]byte, len(randomLZ4))
 
 
 	b.ReportAllocs()
 	b.ReportAllocs()
@@ -30,7 +28,7 @@ func BenchmarkCompressRandom(b *testing.B) {
 	b.ResetTimer()
 	b.ResetTimer()
 
 
 	for i := 0; i < b.N; i++ {
 	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 (
 import (
 	"encoding/binary"
 	"encoding/binary"
-	"fmt"
 	"math/bits"
 	"math/bits"
+	"sync"
 )
 )
 
 
 // blockHash hashes the lower 6 bytes into a value < htSize.
 // 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.
 // CompressBlock compresses the source buffer into the destination one.
 // This is the fast version of LZ4 compression and also the default 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.
 // 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.
 // An error is returned if the destination buffer is too small.
 func CompressBlock(src, dst []byte, hashTable []int) (_ int, err error) {
 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)
 	defer recoverBlock(&err)
 
 
 	// adaptSkipLog sets how quickly the compressor begins skipping blocks when data is incompressible.
 	// 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 {
 	if sn <= 0 || dn == 0 {
 		return 0, nil
 		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.
 	// Prove to the compiler the table has at least htSize elements.
 	// The compiler can see that "uint32() >> hashShift" cannot be out of bounds.
 	// The compiler can see that "uint32() >> hashShift" cannot be out of bounds.
 	hashTable = hashTable[:htSize]
 	hashTable = hashTable[:htSize]
@@ -213,6 +219,13 @@ func CompressBlock(src, dst []byte, hashTable []int) (_ int, err error) {
 	return di, nil
 	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.
 // blockHash hashes 4 bytes into a value < winSize.
 func blockHashHC(x uint32) uint32 {
 func blockHashHC(x uint32) uint32 {
 	const hasher uint32 = 2654435761 // Knuth multiplicative hash.
 	const hasher uint32 = 2654435761 // Knuth multiplicative hash.