Go言語でgzip圧縮する場合の圧縮レベルの比較

前回のGolang CafeでGo言語でgzip圧縮する場合に圧縮レベルを指定することができることを学んだのですが、実際にどれほどの差が出るのか気になったので調べてみました。


Go言語のgzip圧縮では、こちらに定義してあるとおり、

  • NoCompression
  • BestSpeed
  • BestCompression
  • DefaultCompression

の4つのレベルが定義されています。


圧縮対象はGo言語のパッケージのソースコードにしてみました。

対象は1834ファイルで、容量は14.48MB(15,183,311 バイト)あるようです。


検証コードはこちらに置いていますが、前回のGolang Cafeで使用したgzipのサンプルコードを修正して使用しました。

func NoCompression() {
    compress("output/NoCompression.tar.gz", gzip.NoCompression)
}

func BestSpeed() {
    compress("output/BestSpeed.tar.gz", gzip.BestSpeed)
}

func BestCompression() {
    compress("output/BestCompression.tar.gz", gzip.BestCompression)
}

func DefaultCompression() {
    compress("output/DefaultCompression.tar.gz", gzip.DefaultCompression)
}

func compress(outfile string, level int) {
    var file *os.File
    var err error
    var writer *gzip.Writer
    var body []byte

    if file, err = os.Create(outfile); err != nil {
        log.Fatalln(err)
    }
    defer file.Close()

    if writer, err = gzip.NewWriterLevel(file, level); err != nil {
        log.Fatalln(err)
    }
    defer writer.Close()

    tw := tar.NewWriter(writer)
    defer tw.Close()

    root := "c:/golang/go/src/pkg"

    err = filepath.Walk(root,
        func(path string, fi os.FileInfo, er error) error {

            if fi.IsDir() {
                return nil
            }

            if body, err = ioutil.ReadFile(path); err != nil {
                log.Fatalln(err)
            }

            rel, err := filepath.Rel(root, path)

            if err != nil {
                log.Fatalln(err)
            }

            if body != nil {
                hdr := &tar.Header{
                    Name: rel,
                    Size: int64(len(body)),
                }
                if err := tw.WriteHeader(hdr); err != nil {
                    println(err)
                }
                if _, err := tw.Write(body); err != nil {
                    println(err)
                }
            }

            return nil
        })
}

変更点はファイル名と圧縮レベルを指定できるようにしたのと、filepath.WalkメソッドでGo言語のパッケージのソースファイルの一覧を取得するようにしているだけです。


このコードに対して以下にあるように単純に実行するだけのBenchmarkを書きました。

func BenchmarkNoCompression(b *testing.B) {
    for i := 0; i < b.N; i++ {
        NoCompression()
    }
}

func BenchmarkBestSpeed(b *testing.B) {
    for i := 0; i < b.N; i++ {
        BestSpeed()
    }
}

func BenchmarkBestCompression(b *testing.B) {
    for i := 0; i < b.N; i++ {
        BestCompression()
    }
}

func BenchmarkDefaultCompression(b *testing.B) {
    for i := 0; i < b.N; i++ {
        DefaultCompression()
    }
}


結果はこのようになりました。(※見やすいようにフォーマットだけしています)

$ go test -bench . -benchtime 5s
testing: warning: no tests to run
PASS
BenchmarkNoCompression        50         221192650 ns/op
BenchmarkBestSpeed            10         738142220 ns/op
BenchmarkBestCompression       2        2757157700 ns/op
BenchmarkDefaultCompression    5        1392479640 ns/op
ok      github.com/taknb2nch/golangcafe/12      36.182s
$ ls -l ./output/
total 15260
-rw-r--r--    1 taknb2nc Administ  4637547 Jan 17 18:48 BestCompression.tar.gz
-rw-r--r--    1 taknb2nc Administ  5354707 Jan 17 18:48 BestSpeed.tar.gz
-rw-r--r--    1 taknb2nc Administ  4666521 Jan 17 18:48 DefaultCompression.tar.gz
-rw-r--r--    1 taknb2nc Administ 16591117 Jan 17 18:48 NoCompression.tar.gz
圧縮レベル 時間(ns) 圧縮率(%)
NoCompression
221192650
109.2
BestSpeed
738142220
35.3
BestCompression
2757157700
30.5
DefaultCompression
1392479640
30.7

圧縮なし(NoCompression)はおいておいて、想像通りの結果となりました。
この結果を見る限りではDefaultCompressionが速度でもファイルサイズでも一番バランスがよさそうですね。BestCompressionはDefaultCompressionに比べて時間は4倍かかっていますが、圧縮率をみるとあまりかわりません。今回の検証対象がテキストファイルだけだったのでこのような結果になったのかもしれません。


単純にコマンドラインで圧縮しても同じような結果になるのでしょうけど、Go言語でも圧縮率を指定して圧縮できるという検証ができたのでとりあえずは納得です。