Browse Source

perf improvements: removed default unused debug args evaluations; faster writer for short writes

Pierre Curto 7 years ago
parent
commit
ea731401f5
4 changed files with 84 additions and 26 deletions
  1. 2 0
      debug.go
  2. 2 0
      debug_stub.go
  3. 34 12
      reader.go
  4. 46 14
      writer.go

+ 2 - 0
debug.go

@@ -9,6 +9,8 @@ import (
 	"runtime"
 )
 
+const debugFlag = true
+
 func debug(args ...interface{}) {
 	_, file, line, _ := runtime.Caller(1)
 	file = filepath.Base(file)

+ 2 - 0
debug_stub.go

@@ -2,4 +2,6 @@
 
 package lz4
 
+const debugFlag = false
+
 func debug(args ...interface{}) {}

+ 34 - 12
reader.go

@@ -94,7 +94,9 @@ func (z *Reader) readHeader(first bool) error {
 	if n := 2 * bSize; cap(z.zdata) < n {
 		z.zdata = make([]byte, n, n)
 	}
-	debug("header block max size id=%d size=%d", bmsID, bSize)
+	if debugFlag {
+		debug("header block max size id=%d size=%d", bmsID, bSize)
+	}
 	z.zdata = z.zdata[:bSize]
 	z.data = z.zdata[:cap(z.zdata)][bSize:]
 	z.idx = len(z.data)
@@ -121,7 +123,9 @@ func (z *Reader) readHeader(first bool) error {
 	}
 
 	z.Header.done = true
-	debug("header read: %v", z.Header)
+	if debugFlag {
+		debug("header read: %v", z.Header)
+	}
 
 	return nil
 }
@@ -132,13 +136,17 @@ func (z *Reader) readHeader(first bool) error {
 // change between calls to Read(). If that is the case, no data is actually read from
 // the underlying io.Reader, to allow for potential input buffer resizing.
 func (z *Reader) Read(buf []byte) (int, error) {
-	debug("Read buf len=%d", len(buf))
+	if debugFlag {
+		debug("Read buf len=%d", len(buf))
+	}
 	if !z.Header.done {
 		if err := z.readHeader(true); err != nil {
 			return 0, err
 		}
-		debug("header read OK compressed buffer %d / %d uncompressed buffer %d : %d index=%d",
-			len(z.zdata), cap(z.zdata), len(z.data), cap(z.data), z.idx)
+		if debugFlag {
+			debug("header read OK compressed buffer %d / %d uncompressed buffer %d : %d index=%d",
+				len(z.zdata), cap(z.zdata), len(z.data), cap(z.data), z.idx)
+		}
 	}
 
 	if len(buf) == 0 {
@@ -147,7 +155,9 @@ func (z *Reader) Read(buf []byte) (int, error) {
 
 	if z.idx == len(z.data) {
 		// No data ready for reading, process the next block.
-		debug("reading block from writer")
+		if debugFlag {
+			debug("reading block from writer")
+		}
 		// Block length: 0 = end of frame, highest bit set: uncompressed.
 		bLen, err := z.readUint32()
 		if err != nil {
@@ -163,7 +173,9 @@ func (z *Reader) Read(buf []byte) (int, error) {
 				if err != nil {
 					return 0, err
 				}
-				debug("frame checksum got=%x / want=%x", z.checksum.Sum32(), checksum)
+				if debugFlag {
+					debug("frame checksum got=%x / want=%x", z.checksum.Sum32(), checksum)
+				}
 				z.pos += 4
 				if h := z.checksum.Sum32(); checksum != h {
 					return 0, fmt.Errorf("lz4: invalid frame checksum: got %x; expected %x", h, checksum)
@@ -179,11 +191,15 @@ func (z *Reader) Read(buf []byte) (int, error) {
 			return 0, z.readHeader(false)
 		}
 
-		debug("raw block size %d", bLen)
+		if debugFlag {
+			debug("raw block size %d", bLen)
+		}
 		if bLen&compressedBlockFlag > 0 {
 			// Uncompressed block.
 			bLen &= compressedBlockMask
-			debug("uncompressed block size %d", bLen)
+			if debugFlag {
+				debug("uncompressed block size %d", bLen)
+			}
 			if int(bLen) > cap(z.data) {
 				return 0, fmt.Errorf("lz4: invalid block size: %d", bLen)
 			}
@@ -207,7 +223,9 @@ func (z *Reader) Read(buf []byte) (int, error) {
 
 		} else {
 			// Compressed block.
-			debug("compressed block size %d", bLen)
+			if debugFlag {
+				debug("compressed block size %d", bLen)
+			}
 			if int(bLen) > cap(z.data) {
 				return 0, fmt.Errorf("lz4: invalid block size: %d", bLen)
 			}
@@ -238,14 +256,18 @@ func (z *Reader) Read(buf []byte) (int, error) {
 
 		if !z.NoChecksum {
 			z.checksum.Write(z.data)
-			debug("current frame checksum %x", z.checksum.Sum32())
+			if debugFlag {
+				debug("current frame checksum %x", z.checksum.Sum32())
+			}
 		}
 		z.idx = 0
 	}
 
 	n := copy(buf, z.data[z.idx:])
 	z.idx += n
-	debug("copied %d bytes to input", n)
+	if debugFlag {
+		debug("copied %d bytes to input", n)
+	}
 
 	return n, nil
 }

+ 46 - 14
writer.go

@@ -86,7 +86,9 @@ func (z *Writer) writeHeader() error {
 		return err
 	}
 	z.Header.done = true
-	debug("wrote header %v", z.Header)
+	if debugFlag {
+		debug("wrote header %v", z.Header)
+	}
 
 	return nil
 }
@@ -99,24 +101,36 @@ func (z *Writer) Write(buf []byte) (int, error) {
 			return 0, err
 		}
 	}
-
-	if !z.NoChecksum {
-		z.checksum.Write(buf)
+	if debugFlag {
+		debug("input buffer len=%d index=%d", len(buf), z.idx)
 	}
-	debug("input buffer len=%d index=%d", len(buf), z.idx)
 
+	zn := len(z.data)
 	var n int
 	for len(buf) > 0 {
+		if z.idx == 0 && len(buf) >= zn {
+			// Avoid a copy as there is enough data for a block.
+			if err := z.compressBlock(buf[:zn]); err != nil {
+				return n, err
+			}
+			n += zn
+			buf = buf[zn:]
+			continue
+		}
 		// Accumulate the data to be compressed.
 		m := copy(z.data[z.idx:], buf)
 		n += m
 		z.idx += m
 		buf = buf[m:]
-		debug("%d bytes copied to buf, current index %d", n, z.idx)
+		if debugFlag {
+			debug("%d bytes copied to buf, current index %d", n, z.idx)
+		}
 
 		if z.idx < len(z.data) {
 			// Buffer not filled.
-			debug("need more data for compression")
+			if debugFlag {
+				debug("need more data for compression")
+			}
 			return n, nil
 		}
 
@@ -132,6 +146,10 @@ func (z *Writer) Write(buf []byte) (int, error) {
 
 // compressBlock compresses a block.
 func (z *Writer) compressBlock(data []byte) error {
+	if !z.NoChecksum {
+		z.checksum.Write(data)
+	}
+
 	// The compressed block size cannot exceed the input's.
 	var zn int
 	var err error
@@ -144,7 +162,9 @@ func (z *Writer) compressBlock(data []byte) error {
 
 	var zdata []byte
 	var bLen uint32
-	debug("block compression %d => %d", len(data), zn)
+	if debugFlag {
+		debug("block compression %d => %d", len(data), zn)
+	}
 	if err == nil && zn > 0 && zn < len(data) {
 		// Compressible and compressed size smaller than uncompressed: ok!
 		bLen = uint32(zn)
@@ -154,7 +174,9 @@ func (z *Writer) compressBlock(data []byte) error {
 		bLen = uint32(len(data)) | compressedBlockFlag
 		zdata = data
 	}
-	debug("block compression to be written len=%d data len=%d", bLen, len(zdata))
+	if debugFlag {
+		debug("block compression to be written len=%d data len=%d", bLen, len(zdata))
+	}
 
 	// Write the block.
 	if err := z.writeUint32(bLen); err != nil {
@@ -166,12 +188,16 @@ func (z *Writer) compressBlock(data []byte) error {
 
 	if z.BlockChecksum {
 		checksum := xxh32.ChecksumZero(zdata)
-		debug("block checksum %x", checksum)
+		if debugFlag {
+			debug("block checksum %x", checksum)
+		}
 		if err := z.writeUint32(checksum); err != nil {
 			return err
 		}
 	}
-	debug("current frame checksum %x", z.checksum.Sum32())
+	if debugFlag {
+		debug("current frame checksum %x", z.checksum.Sum32())
+	}
 
 	return nil
 }
@@ -180,7 +206,9 @@ func (z *Writer) compressBlock(data []byte) error {
 // Flush does not return until the data has been written.
 // If the underlying writer returns an error, Flush returns that error.
 func (z *Writer) Flush() error {
-	debug("flush with index %d", z.idx)
+	if debugFlag {
+		debug("flush with index %d", z.idx)
+	}
 	if z.idx == 0 {
 		return nil
 	}
@@ -200,13 +228,17 @@ func (z *Writer) Close() error {
 		return err
 	}
 
-	debug("writing last empty block")
+	if debugFlag {
+		debug("writing last empty block")
+	}
 	if err := z.writeUint32(0); err != nil {
 		return err
 	}
 	if !z.NoChecksum {
 		checksum := z.checksum.Sum32()
-		debug("stream checksum %x", checksum)
+		if debugFlag {
+			debug("stream checksum %x", checksum)
+		}
 		if err := z.writeUint32(checksum); err != nil {
 			return err
 		}