11 6
snappy试玩

背景

今天在看nsq源码的时候,发现client_v2.go里面有一段代码引起了我的注意

func (c *clientV2) UpgradeSnappy() error {

    c.Reader = bufio.NewReaderSize(snappystream.NewReader(conn, snappystream.SkipVerifyChecksum), defaultBufferSize)
    c.Writer = bufio.NewWriterSize(snappystream.NewWriter(conn), c.OutputBufferSize)

    ...
}

我大概知道它是用于对我的数据在传输前进行压缩的,我之前了解过LZ4, 但,snappy我了解甚少,于是我顺着snappystream,找到了一个库 go-snappystream

里面是这样定义的:

This package wraps snappy-go and supplies a Reader and Writer for the snappy framed stream format.

这个库是nsq的作者mreiferson基于google的snappy进行封装的工具。

Snappy 介绍

Snappy(以前称Zippy)是Google基于LZ77 的思路用C++语言编写的快速数据压缩与解压程序库,并在2011年开源。它的目标并非最大压缩率或与其他压缩程序库的兼容性, 而是非常高的速度和合理的压缩率。使用一个运行在64位模式下的酷睿i7处理器的单个核心, 压缩速度250 MB/s,解压速度500 MB/s。压缩率比gzip低20-100%

Snappy广泛应用在Google的项目,例如BigTable、MapReduce和Google内部RPC系统的压缩数据。 它可在开源项目中使用,例如Cassandra、Hadoop、LevelDB、MongoDB、RocksDB和Lucene。 解压缩时会检测压缩流中是否存在错误。Snappy不使用内联汇编并且可移植。

Snappy是一个C++的库,你可以在产品中使用,不过也有一些其他语言的版本,例如Haskell、Java、Perl、Python和Ruby。

golang版snappy可以查看这里

使用

先看第一个例子,我们采用snappy+gob来处理我们的文件

package main

import (
    "encoding/gob"
    "fmt"
    "github.com/mreiferson/go-snappystream"
    "os"
)

type FileCompress struct {
    Classifier interface{}
}

func (t *FileCompress) SaveFile(filename string) error {
    fi, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer fi.Close()

    fs := snappystream.NewBufferedWriter(fi)
    encoder := gob.NewEncoder(fs)
    err = encoder.Encode(t.Classifier)
    if err != nil {
        return err
    }
    err = fs.Close()
    if err != nil {
        return err
    }

    return nil
}

func main() {
    obj := FileCompress{Classifier: "it works"}
    err := obj.SaveFile("test.sz")
    if err != nil {
        fmt.Println(err)
    }
}

第二个例子 ,我们写一个命令交互的工具 snappy2.go,它介绍我们的输入并进行snappy处理

package main

import snap "github.com/mreiferson/go-snappystream"
import "os"
import "io"
import "flag"

type Options struct {
    Encode bool
    Decode bool
}

func check(err error) {
    if err != nil {
        panic(err)
    }
}

var en = flag.Bool("encode", false, "encode stdin (the default)")
var de = flag.Bool("decode", false, "decode stdin")

func main() {
    flag.Parse()
    opts := &Options{
        Decode: *en,
        Encode: *de,
    }

    if opts.Decode {
        decode()
    } else {
        encode()
    }
}

func encode() {
    _, err := io.Copy(snap.NewWriter(os.Stdout), os.Stdin)
    check(err)
}

func decode() {
    _, err := io.Copy(os.Stdout, snap.NewReader(os.Stdin, snap.VerifyChecksum))
    check(err)
}

我们可以再终端输入命令进行使用

go run snappy2.go < test.txt | go run snappy2.go --decode

总的来说,snappy对比其它压缩算法的工具,具备以下的特定:

  • 稳定
  • 鲁棒性强
  • 开源

下面附加一份我机器上用go-snappy的一个基准测试报告,仅供参考

BenchmarkWordsDecode1e1-4       50000000            25.6 ns/op     390.89 MB/s
BenchmarkWordsDecode1e2-4       20000000            96.5 ns/op    1036.03 MB/s
BenchmarkWordsDecode1e3-4        2000000           822 ns/op    1216.36 MB/s
BenchmarkWordsDecode1e4-4         200000          9008 ns/op    1110.06 MB/s
BenchmarkWordsDecode1e5-4          10000        205258 ns/op     487.19 MB/s
BenchmarkWordsDecode1e6-4           1000       1644589 ns/op     608.05 MB/s
BenchmarkWordsEncode1e1-4       100000000            21.5 ns/op     465.78 MB/s
BenchmarkWordsEncode1e2-4       10000000           217 ns/op     459.67 MB/s
BenchmarkWordsEncode1e3-4        1000000          2038 ns/op     490.50 MB/s
BenchmarkWordsEncode1e4-4          50000         29909 ns/op     334.35 MB/s
BenchmarkWordsEncode1e5-4           5000        355186 ns/op     281.54 MB/s
BenchmarkWordsEncode1e6-4            500       3387786 ns/op     295.18 MB/s
BenchmarkRandomEncode-4            10000        107681 ns/op    9737.75 MB/s
BenchmarkExtendMatch-4            200000         10816 ns/op
PASS