Convert Figma logo to code with AI

klauspost logopgzip

Go parallel gzip (de)compression

1,151
79
1,151
5

Top Related Projects

DEPRECATED. Please use mholt/archives instead.

Quick Overview

klauspost/pgzip is a Go library that provides parallel gzip compression and decompression. It aims to improve performance by utilizing multiple CPU cores, making it significantly faster than the standard library's gzip implementation for large files.

Pros

  • Significantly faster compression and decompression for large files
  • Drop-in replacement for standard library's gzip package
  • Supports both compression and decompression
  • Customizable compression level and number of concurrent goroutines

Cons

  • May not provide significant benefits for small files
  • Increased memory usage due to parallel processing
  • Potential for increased CPU usage on systems with limited cores
  • Not suitable for streaming applications with limited memory

Code Examples

  1. Compressing a file:
import "github.com/klauspost/pgzip"

func compressFile(src, dst string) error {
    in, err := os.Open(src)
    if err != nil {
        return err
    }
    defer in.Close()

    out, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer out.Close()

    gw, err := pgzip.NewWriterLevel(out, pgzip.BestCompression)
    if err != nil {
        return err
    }
    defer gw.Close()

    _, err = io.Copy(gw, in)
    return err
}
  1. Decompressing a file:
import "github.com/klauspost/pgzip"

func decompressFile(src, dst string) error {
    in, err := os.Open(src)
    if err != nil {
        return err
    }
    defer in.Close()

    out, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer out.Close()

    gr, err := pgzip.NewReader(in)
    if err != nil {
        return err
    }
    defer gr.Close()

    _, err = io.Copy(out, gr)
    return err
}
  1. Setting custom options:
import "github.com/klauspost/pgzip"

func customCompression(w io.Writer) error {
    gw, err := pgzip.NewWriterLevel(w, pgzip.BestSpeed)
    if err != nil {
        return err
    }
    gw.SetConcurrency(100000, 10) // Set block size and number of goroutines
    defer gw.Close()

    // Write data to gw
    return nil
}

Getting Started

To use klauspost/pgzip in your Go project, follow these steps:

  1. Install the package:

    go get -u github.com/klauspost/pgzip
    
  2. Import the package in your Go code:

    import "github.com/klauspost/pgzip"
    
  3. Use it as a drop-in replacement for the standard library's gzip package:

    // Instead of "compress/gzip"
    gw := pgzip.NewWriter(outputFile)
    // or
    gr, err := pgzip.NewReader(inputFile)
    

Competitor Comparisons

DEPRECATED. Please use mholt/archives instead.

Pros of archiver

  • Supports multiple archive formats (zip, tar, rar, etc.) beyond just gzip
  • Provides a high-level API for creating and extracting archives
  • Includes a command-line interface for easy use outside of Go programs

Cons of archiver

  • May have more overhead for simple gzip operations
  • Less focused on performance optimization for gzip specifically
  • Larger dependency footprint due to supporting multiple formats

Code comparison

archiver:

err := archiver.Archive([]string{"file1.txt", "file2.txt"}, "output.zip")

pgzip:

w := pgzip.NewWriter(file)
_, err := w.Write(data)
w.Close()

Key differences

pgzip is specifically designed for parallel gzip compression and decompression, offering high performance for gzip operations. It's a more focused library that can be ideal for projects primarily dealing with gzip format.

archiver, on the other hand, is a more versatile tool that handles various archive formats. It provides a unified interface for working with different compression and archive types, making it suitable for projects that need to support multiple formats or require a higher-level abstraction.

The choice between these libraries depends on the specific requirements of your project, such as the need for multiple formats, performance priorities, and the desired level of abstraction.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

pgzip

Go parallel gzip compression/decompression. This is a fully gzip compatible drop in replacement for "compress/gzip".

This will split compression into blocks that are compressed in parallel. This can be useful for compressing big amounts of data. The output is a standard gzip file.

The gzip decompression is modified so it decompresses ahead of the current reader. This means that reads will be non-blocking if the decompressor can keep ahead of your code reading from it. CRC calculation also takes place in a separate goroutine.

You should only use this if you are (de)compressing big amounts of data, say more than 1MB at the time, otherwise you will not see any benefit, and it will likely be faster to use the internal gzip library or this package.

It is important to note that this library creates and reads standard gzip files. You do not have to match the compressor/decompressor to get the described speedups, and the gzip files are fully compatible with other gzip readers/writers.

A golang variant of this is bgzf, which has the same feature, as well as seeking in the resulting file. The only drawback is a slightly bigger overhead compared to this and pure gzip. See a comparison below.

GoDoc Build Status

Installation

go get github.com/klauspost/pgzip/...

You might need to get/update the dependencies:

go get -u github.com/klauspost/compress

Usage

Godoc Doumentation

To use as a replacement for gzip, exchange

import "compress/gzip" with import gzip "github.com/klauspost/pgzip".

Changes

  • Oct 6, 2016: Fixed an issue if the destination writer returned an error.
  • Oct 6, 2016: Better buffer reuse, should now generate less garbage.
  • Oct 6, 2016: Output does not change based on write sizes.
  • Dec 8, 2015: Decoder now supports the io.WriterTo interface, giving a speedup and less GC pressure.
  • Oct 9, 2015: Reduced allocations by ~35 by using sync.Pool. ~15% overall speedup.

Changes in github.com/klauspost/compress are also carried over, so see that for more changes.

Compression

The simplest way to use this is to simply do the same as you would when using compress/gzip.

To change the block size, use the added (*pgzip.Writer).SetConcurrency(blockSize, blocks int) function. With this you can control the approximate size of your blocks, as well as how many you want to be processing in parallel. Default values for this is SetConcurrency(1MB, runtime.GOMAXPROCS(0)), meaning blocks are split at 1 MB and up to the number of CPU threads blocks can be processing at once before the writer blocks.

Example:

var b bytes.Buffer
w := gzip.NewWriter(&b)
w.SetConcurrency(100000, 10)
w.Write([]byte("hello, world\n"))
w.Close()

To get any performance gains, you should at least be compressing more than 1 megabyte of data at the time.

You should at least have a block size of 100k and at least a number of blocks that match the number of cores your would like to utilize, but about twice the number of blocks would be the best.

Another side effect of this is, that it is likely to speed up your other code, since writes to the compressor only blocks if the compressor is already compressing the number of blocks you have specified. This also means you don't have worry about buffering input to the compressor.

Decompression

Decompression works similar to compression. That means that you simply call pgzip the same way as you would call compress/gzip.

The only difference is that if you want to specify your own readahead, you have to use pgzip.NewReaderN(r io.Reader, blockSize, blocks int) to get a reader with your custom blocksizes. The blockSize is the size of each block decoded, and blocks is the maximum number of blocks that is decoded ahead.

See Example on playground

Performance

Compression

See my blog post in Benchmarks of Golang Gzip.

Compression cost is usually about 0.2% with default settings with a block size of 250k.

Example with GOMAXPROC set to 32 (16 core CPU)

Content is Matt Mahoneys 10GB corpus. Compression level 6.

CompressorMB/secspeedupsizesize overhead (lower=better)
gzip (golang)16.91MB/s (1 thread)1.0x47813293070%
gzip (klauspost)127.10MB/s (1 thread)7.52x4885366806+2.17%
pgzip (klauspost)2085.35MB/s123.34x4886132566+2.19%
pargzip (builder)334.04MB/s19.76x4786890417+0.12%

pgzip also contains a huffman only compression mode, that will allow compression at ~450MB per core per second, largely independent of the content.

See the complete sheet for different content types and compression settings.

Decompression

The decompression speedup is there because it allows you to do other work while the decompression is taking place.

In the example above, the numbers are as follows on a 4 CPU machine:

DecompressorTimeSpeedup
gzip (golang)1m28.85s0%
pgzip (klauspost)43.48s104%

But wait, since gzip decompression is inherently singlethreaded (aside from CRC calculation) how can it be more than 100% faster? Because pgzip due to its design also acts as a buffer. When using unbuffered gzip, you are also waiting for io when you are decompressing. If the gzip decoder can keep up, it will always have data ready for your reader, and you will not be waiting for input to the gzip decompressor to complete.

This is pretty much an optimal situation for pgzip, but it reflects most common usecases for CPU intensive gzip usage.

I haven't included bgzf in this comparison, since it only can decompress files created by a compatible encoder, and therefore cannot be considered a generic gzip decompressor. But if you are able to compress your files with a bgzf compatible program, you can expect it to scale beyond 100%.

License

This contains large portions of code from the go repository - see GO_LICENSE for more information. The changes are released under MIT License. See LICENSE for more information.