Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18435] ファイルの概要

このコミットは、Go言語の標準ライブラリarchive/zipパッケージに、Deflate圧縮を使用したZIP書き込みのベンチマークを追加するものです。具体的には、writer_test.goファイルにBenchmarkCompressedZipGarbageという新しいベンチマーク関数が導入されました。

コミット

commit 730f51ab58cebb05eb06eea5113e4c2bcb1aff65
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Sun Feb 9 13:56:47 2014 -0800

    archive/zip: add flate writing benchmark
    
    LGTM=adg
    R=adg
    CC=golang-codereviews
    https://golang.org/cl/60530049

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/730f51ab58cebb05eb06eea5113e4c2bcb1aff65

元コミット内容

archive/zip: add flate writing benchmark

このコミットは、archive/zipパッケージにflate(Deflate)圧縮を用いたZIPファイル書き込みのベンチマークを追加します。

変更の背景

このコミットの背景には、Go言語の標準ライブラリであるarchive/zipパッケージの性能評価と最適化への継続的な取り組みがあります。特に、ZIPファイルの書き込み処理、中でもDeflate圧縮を使用した場合の性能は、アプリケーションのI/O性能に直結するため重要です。

ベンチマークを追加する主な目的は以下の通りと考えられます。

  1. 性能の測定と監視: Deflate圧縮は計算コストが高いため、その書き込み性能を正確に測定し、将来の変更が性能に与える影響を監視するための基準(ベースライン)を確立すること。
  2. 最適化の機会の特定: ベンチマークを実行することで、性能のボトルネックとなっている箇所を特定し、さらなる最適化の機会を見つけること。
  3. 回帰の防止: 将来のコード変更によって性能が意図せず低下する(性能回帰)ことを防ぐためのテストとして機能させること。

このコミットは、Goの標準ライブラリが常に高性能で効率的であることを保証するための、一般的な開発プラクティスの一環として行われました。

前提知識の解説

1. ZIPファイルフォーマット

ZIPは、データ圧縮とアーカイブのためのファイルフォーマットです。複数のファイルを一つのアーカイブにまとめることができ、各ファイルは異なる圧縮方式で保存できます。Go言語のarchive/zipパッケージは、このZIPフォーマットの読み書きをサポートします。

2. Deflate圧縮 (Flate)

Deflateは、LZ77アルゴリズムとハフマン符号化を組み合わせた可逆データ圧縮アルゴリズムです。ZIPファイルで最も一般的に使用される圧縮方式であり、PNG画像やgzipファイルなど、他の多くのフォーマットでも利用されています。Go言語では、compress/flateパッケージがDeflate圧縮の実装を提供しており、archive/zipパッケージはこのcompress/flateを利用してDeflate圧縮を行います。

3. Go言語のベンチマーク

Go言語には、標準でベンチマークテストを記述・実行するためのフレームワークが組み込まれています。testingパッケージの一部として提供されており、go test -bench=.コマンドで実行できます。

  • func BenchmarkXxx(b *testing.B): ベンチマーク関数はBenchmarkで始まり、*testing.B型の引数を取ります。
  • b.N: ベンチマーク関数内のループはb.N回実行されます。b.Nの値は、ベンチマークの実行時間に基づいてGoのテストフレームワークが自動的に調整します。
  • b.ReportAllocs(): このメソッドを呼び出すと、ベンチマーク実行中に発生したメモリ割り当ての回数とバイト数がレポートに含まれるようになります。性能最適化において、不要なメモリ割り当てを減らすことは非常に重要です。
  • b.ResetTimer(): ベンチマークの計測を開始する前に、セットアップコードの時間を計測から除外するために使用します。このコミットのベンチマークでは使用されていませんが、一般的なベンチマークのプラクティスです。
  • bytes.Buffer: 可変長のバイトシーケンスを扱うためのバッファです。このベンチマークでは、ZIPファイルの出力先として使用されています。
  • bytes.Repeat([]byte("a"), 1<<20): 指定されたバイトスライスを繰り返し結合して新しいバイトスライスを作成します。ここでは、1MB(1<<20バイト)の「a」の繰り返しからなる大きなバッファを作成し、これを圧縮対象のデータとして使用しています。これは、ある程度のサイズのデータを扱う際の性能を評価するためによく用いられる手法です。

4. archive/zipパッケージの主要な構造体とメソッド

  • zip.NewWriter(w io.Writer): io.Writerを引数に取り、ZIPアーカイブを書き込むためのzip.Writerを返します。
  • zip.Writer.CreateHeader(fh *FileHeader): 新しいファイルをZIPアーカイブに追加し、そのファイルに書き込むためのio.Writerを返します。FileHeaderにはファイル名や圧縮方式などのメタデータが含まれます。
  • zip.FileHeader: ZIPアーカイブ内の個々のファイルのエントリに関するメタデータを定義する構造体です。
    • Name: ファイル名。
    • Method: 圧縮方式。DeflateはDeflate圧縮を意味します。
  • zip.Writer.Close(): ZIPアーカイブの書き込みを完了し、基になるio.Writerを閉じます。このメソッドを呼び出さないと、ZIPアーカイブが破損する可能性があります。

技術的詳細

このコミットで追加されたBenchmarkCompressedZipGarbageベンチマーク関数は、archive/zipパッケージを使用してDeflate圧縮されたZIPファイルをメモリ上で作成する際の性能を測定します。

ベンチマークのロジックは以下の通りです。

  1. b.ReportAllocs(): メモリ割り当ての統計をレポートに含めるように設定します。
  2. var buf bytes.Buffer: ZIPファイルの出力先となるbytes.Bufferを宣言します。これはメモリ上にZIPファイルを作成するための一時的なバッファとして機能します。
  3. bigBuf := bytes.Repeat([]byte("a"), 1<<20): 1MBサイズのダミーデータ(すべて'a')を作成します。このデータがZIPファイル内で圧縮されます。
  4. for i := 0; i < b.N; i++: ベンチマークのメインループです。b.NはGoのテストフレームワークによって動的に調整される実行回数です。
  5. buf.Reset(): 各イテレーションの開始時に、bytes.Bufferをリセットし、前のイテレーションで書き込まれたデータをクリアします。これにより、各ベンチマーク実行が独立して行われ、前の実行の影響を受けないようにします。
  6. zw := NewWriter(&buf): 新しいzip.Writerを作成し、bufを書き込み先として指定します。
  7. for j := 0; j < 3; j++: 各ZIPアーカイブ内に3つのファイルエントリを作成します。
  8. w, _ := zw.CreateHeader(&FileHeader{Name: "foo", Method: Deflate,}): fooという名前でDeflate圧縮方式を使用する新しいファイルエントリを作成し、そのエントリにデータを書き込むためのio.Writerを取得します。
  9. w.Write(bigBuf): 作成した1MBのダミーデータをファイルエントリに書き込みます。この際、Deflate圧縮が適用されます。
  10. zw.Close(): ZIPアーカイブの書き込みを完了します。これにより、ZIPヘッダやセントラルディレクトリなどが適切に書き込まれ、ZIPファイルが有効な状態になります。

このベンチマークは、特に大量のデータをDeflate圧縮でZIPファイルに書き込む際のCPU使用率、メモリ割り当て、および全体的な処理時間を評価するのに役立ちます。bytes.Repeat([]byte("a"), 1<<20)で生成されるデータは、非常に圧縮率が高い(すべて同じ文字なので)ため、Deflate圧縮アルゴリズムの最良ケースに近い性能を測定することになります。これにより、圧縮アルゴリズム自体の効率や、その実装におけるオーバーヘッドを評価するのに適しています。

コアとなるコードの変更箇所

src/pkg/archive/zip/writer_test.goファイルに以下のコードが追加されました。

--- a/src/pkg/archive/zip/writer_test.go
+++ b/src/pkg/archive/zip/writer_test.go
@@ -125,3 +125,21 @@ func testReadFile(t *testing.T, f *File, wt *WriteTest) {\n 		t.Errorf("File contents %q, want %q", b, wt.Data)\n 	}\n }\n+\n+func BenchmarkCompressedZipGarbage(b *testing.B) {\n+\tb.ReportAllocs()\n+\tvar buf bytes.Buffer\n+\tbigBuf := bytes.Repeat([]byte("a"), 1<<20)\n+\tfor i := 0; i < b.N; i++ {\n+\t\tbuf.Reset()\n+\t\tzw := NewWriter(&buf)\n+\t\tfor j := 0; j < 3; j++ {\n+\t\t\tw, _ := zw.CreateHeader(&FileHeader{\n+\t\t\t\tName:   "foo",\n+\t\t\t\tMethod: Deflate,\n+\t\t\t})\n+\t\t\tw.Write(bigBuf)\n+\t\t}\n+\t\tzw.Close()\n+\t}\n+}\n```

## コアとなるコードの解説

追加された`BenchmarkCompressedZipGarbage`関数は、`archive/zip`パッケージの`Writer`がDeflate圧縮(`Method: Deflate`)を使用してデータをZIPファイルに書き込む際の性能を測定します。

*   **`b.ReportAllocs()`**: この行は、ベンチマークの実行中に発生したメモリ割り当ての回数とバイト数をGoのテストフレームワークに報告させるためのものです。これにより、メモリ効率の観点からも性能を評価できます。
*   **`var buf bytes.Buffer`**: ZIPファイルの出力先として使用されるインメモリバッファです。これにより、ディスクI/Oのオーバーヘッドを排除し、純粋な圧縮・書き込みロジックの性能を測定できます。
*   **`bigBuf := bytes.Repeat([]byte("a"), 1<<20)`**: 1MBの「a」の繰り返しからなるバイトスライスを作成します。これは、圧縮されるデータとして使用されます。このデータは非常に圧縮率が高いため、Deflate圧縮の効率を最大限に引き出すシナリオをシミュレートします。
*   **`for i := 0; i < b.N; i++`**: Goのベンチマークの標準的なループ構造です。`b.N`はベンチマークシステムによって動的に調整され、統計的に有意な結果が得られるように十分な回数実行されます。
*   **`buf.Reset()`**: 各ベンチマークイテレーションの開始時に、`bytes.Buffer`をクリアします。これにより、各イテレーションが独立した状態から開始され、前のイテレーションの結果が次のイテレーションに影響を与えないようにします。
*   **`zw := NewWriter(&buf)`**: 新しい`zip.Writer`インスタンスを作成します。このライターは、`buf`にZIPデータを書き込みます。
*   **`for j := 0; j < 3; j++`**: 各ZIPアーカイブ内に3つのファイルエントリを作成します。これにより、複数のファイルを扱う際のオーバーヘッドも考慮に入れたベンチマークとなります。
*   **`w, _ := zw.CreateHeader(&FileHeader{Name: "foo", Method: Deflate,})`**: `foo`という名前で、Deflate圧縮方式を使用する新しいファイルエントリをZIPアーカイブ内に作成します。`CreateHeader`は、そのファイルエントリにデータを書き込むための`io.Writer`を返します。
*   **`w.Write(bigBuf)`**: 1MBのダミーデータを、Deflate圧縮を適用しながらZIPファイルエントリに書き込みます。
*   **`zw.Close()`**: `zip.Writer`を閉じます。これは非常に重要で、ZIPアーカイブのフッター(セントラルディレクトリなど)が正しく書き込まれ、ZIPファイルが有効な形式で閉じられることを保証します。この呼び出しを忘れると、生成されたZIPファイルは破損する可能性があります。

このベンチマークは、`archive/zip`パッケージがDeflate圧縮を効率的に処理できるか、特に大量の繰り返しデータに対してどの程度の性能を発揮するかを評価するためのものです。

## 関連リンク

*   Go言語 `archive/zip` パッケージのドキュメント: [https://pkg.go.dev/archive/zip](https://pkg.go.dev/archive/zip)
*   Go言語 `compress/flate` パッケージのドキュメント: [https://pkg.go.dev/compress/flate](https://pkg.go.dev/compress/flate)
*   Go言語 `testing` パッケージのドキュメント (ベンチマークに関する記述を含む): [https://pkg.go.dev/testing](https://pkg.go.dev/testing)
*   Go言語のベンチマークに関する公式ブログ記事 (古いですが概念は共通): [https://go.dev/blog/benchmarking](https://go.dev/blog/benchmarking)

## 参考にした情報源リンク

*   GitHub: golang/go commit 730f51ab58cebb05eb06eea5113e4c2bcb1aff65: [https://github.com/golang/go/commit/730f51ab58cebb05eb06eea5113e4c2bcb1aff65](https://github.com/golang/go/commit/730f51ab58cebb05eb06eea5113e4c2bcb1aff65)
*   Gerrit Code Review for Go (CL 60530049): [https://go-review.googlesource.com/c/go/+/60530049](https://go-review.googlesource.com/c/go/+/60530049) (このコミットの元のコードレビューページ)
*   Deflate (Wikipedia): [https://ja.wikipedia.org/wiki/Deflate](https://ja.wikipedia.org/wiki/Deflate)
*   ZIP (ファイルフォーマット) (Wikipedia): [https://ja.wikipedia.org/wiki/ZIP_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)](https://ja.wikipedia.org/wiki/ZIP_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88))
*   Go言語のベンチマークに関する一般的な情報源 (Go公式ドキュメント、ブログ、チュートリアルなど)