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

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

このコミットは、Go言語の標準ライブラリ compress/flate パッケージにおけるベンチマークの改善に関するものです。具体的には、ベンチマークの入力データとして、これまで使用されていた「円周率eの桁数」だけでなく、より現実的な「英語のテキスト(マーク・トウェインの『トム・ソーヤーの冒険』)」を追加し、圧縮・解凍性能の測定をより実用的なシナリオに近づけることを目的としています。

コミット

commit ffd0d02d099d07db2e98dc6e91323cc51a57c124
Author: Nigel Tao <nigeltao@golang.org>
Date:   Wed May 9 08:57:54 2012 +1000

    compress/flate: benchmark some English text, not just the digits of e.
    
    The testdata/e.txt input is repeated on the longer benchmarks, but the
    length of that data is less than flate's window size, so the numbers are
    essentially measuring the performance of a trivial compression. A follow-up
    CL will add more data to testdata/e.txt.
    
    Sample output on my laptop (linux, amd64):
    
    BenchmarkDecodeDigitsSpeed1e4       5000            603153 ns/op          16.58 MB/s
    BenchmarkDecodeDigitsSpeed1e5       1000           1465602 ns/op          68.23 MB/s
    BenchmarkDecodeDigitsSpeed1e6        200           8036050 ns/op         124.44 MB/s
    BenchmarkDecodeDigitsDefault1e4     5000            581796 ns/op          17.19 MB/s
    BenchmarkDecodeDigitsDefault1e5     2000            846653 ns/op         118.11 MB/s
    BenchmarkDecodeDigitsDefault1e6      500           3385782 ns/op         295.35 MB/s
    BenchmarkDecodeDigitsCompress1e4            5000            581180 ns/op          17.21 MB/s
    BenchmarkDecodeDigitsCompress1e5            2000            846209 ns/op         118.17 MB/s
    BenchmarkDecodeDigitsCompress1e6             500           3386174 ns/op         295.32 MB/s
    BenchmarkDecodeTwainSpeed1e4        5000            643563 ns/op          15.54 MB/s
    BenchmarkDecodeTwainSpeed1e5         500           5418408 ns/op          18.46 MB/s
    BenchmarkDecodeTwainSpeed1e6          50          52277520 ns/op          19.13 MB/s
    BenchmarkDecodeTwainDefault1e4      5000            583551 ns/op          17.14 MB/s
    BenchmarkDecodeTwainDefault1e5       500           4443428 ns/op          22.51 MB/s
    BenchmarkDecodeTwainDefault1e6        50          41862080 ns/op          23.89 MB/s
    BenchmarkDecodeTwainCompress1e4     5000            583490 ns/op          17.14 MB/s
    BenchmarkDecodeTwainCompress1e5      500           4426356 ns/op          22.59 MB/s
    BenchmarkDecodeTwainCompress1e6       50          41657940 ns/op          24.01 MB/s
    BenchmarkEncodeDigitsSpeed1e4       2000           1230907 ns/op           8.12 MB/s
    BenchmarkEncodeDigitsSpeed1e5       1000           2319129 ns/op          43.12 MB/s
    BenchmarkEncodeDigitsSpeed1e6        100          12378950 ns/op          80.78 MB/s
    BenchmarkEncodeDigitsDefault1e4     1000           1597865 ns/op           6.26 MB/s
    BenchmarkEncodeDigitsDefault1e5      500           3163458 ns/op          31.61 MB/s
    BenchmarkEncodeDigitsDefault1e6      100          18770240 ns/op          53.28 MB/s
    BenchmarkEncodeDigitsCompress1e4            1000           1603461 ns/op           6.24 MB/s
    BenchmarkEncodeDigitsCompress1e5             500           3168766 ns/op          31.56 MB/s
    BenchmarkEncodeDigitsCompress1e6             100          18855830 ns/op          53.03 MB/s
    BenchmarkEncodeTwainSpeed1e4        1000           1338049 ns/op           7.47 MB/s
    BenchmarkEncodeTwainSpeed1e5         500           7341622 ns/op          13.62 MB/s
    BenchmarkEncodeTwainSpeed1e6          50          67484600 ns/op          14.82 MB/s
    BenchmarkEncodeTwainDefault1e4      1000           1778399 ns/op           5.62 MB/s
    BenchmarkEncodeTwainDefault1e5       100          23261810 ns/op           4.30 MB/s
    BenchmarkEncodeTwainDefault1e6        10         243533600 ns/op           4.11 MB/s
    BenchmarkEncodeTwainCompress1e4     1000           1795469 ns/op           5.57 MB/s
    BenchmarkEncodeTwainCompress1e5       50          29447140 ns/op           3.40 MB/s
    BenchmarkEncodeTwainCompress1e6        5         321686800 ns/op           3.11 MB/s
    ok      compress/flate  89.246s
    
    R=rsc, r
    CC=golang-dev
    https://golang.org/cl/6195055

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

https://github.com/golang/go/commit/ffd0d02d099d07db2e98dc6e91323cc51a57c124

元コミット内容

compress/flate: benchmark some English text, not just the digits of e.

(日本語訳) compress/flate: eの桁数だけでなく、英語のテキストもベンチマーク対象にする。

変更の背景

Go言語の compress/flate パッケージは、DEFLATEアルゴリズム(ZIPやzlibなどで使用される圧縮アルゴリズム)の実装を提供しています。このパッケージの性能を評価するためにベンチマークが用意されていましたが、その入力データに課題がありました。

従来のベンチマークでは、主に「円周率eの桁数」を記録した testdata/e.txt というファイルが使用されていました。このファイルは10KBの長さで、長いベンチマーク(例えば100KBや1MBのデータを扱う場合)では、この10KBのデータが繰り返し使用されていました。

問題は、DEFLATEアルゴリズムが持つ「ウィンドウサイズ」にあります。DEFLATEは、過去に出現したデータのパターンを再利用することで圧縮を行います。flate パッケージのウィンドウサイズは 1<<15 (32768バイト、約32KB) です。e.txt のデータ長(10KB)は、このウィンドウサイズよりも短いため、データが繰り返されると、圧縮器は以前のデータを簡単に参照し、非常に効率的な「長さ/距離コピー操作」で圧縮できてしまいます。

これにより、ベンチマークの結果は、実際の多様なデータに対する圧縮性能を正確に反映しているとは言えず、むしろ「自明な圧縮(trivial compression)」の性能、特に forwardCopy のような内部実装の速度を測定しているに過ぎないという問題がありました。

このコミットは、より現実的なデータ(英語のテキスト)を導入することで、compress/flate パッケージの圧縮・解凍性能を、より実用的なシナリオで評価できるようにすることを目的としています。また、e.txt のデータ長を増やすという、さらなる改善の計画も示唆されています。

前提知識の解説

Go言語のベンチマーク

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

  • testing.B: ベンチマーク関数に渡される構造体で、ベンチマークの制御や測定結果の記録に使用されます。
  • b.StopTimer() / b.StartTimer(): 測定対象の処理の開始・停止を制御します。初期化処理などを測定から除外するために使用されます。
  • b.SetBytes(int64(n)): 1回の操作で処理されるバイト数を設定します。これにより、ベンチマーク結果に「MB/s」のようなスループットが表示されるようになります。
  • b.N: ベンチマーク関数が実行される回数を示します。この値は、ベンチマークシステムが自動的に調整し、統計的に有意な結果が得られるようにします。ベンチマーク関数は、for i := 0; i < b.N; i++ のループ内で測定対象の処理を実行します。

DEFLATEアルゴリズムとFlate圧縮

DEFLATEは、LZ77アルゴリズムとハフマン符号化を組み合わせた可逆データ圧縮アルゴリズムです。

  • LZ77 (Lempel-Ziv 1977): 繰り返し出現するバイト列(パターン)を、そのパターンが以前に出現した場所(距離)と長さで置き換えることで圧縮します。この「距離」と「長さ」を表現するために、圧縮器は一定の範囲(ウィンドウ)内の過去のデータを参照します。この参照可能な範囲が「ウィンドウサイズ」です。ウィンドウサイズが大きいほど、より長い距離のパターンも参照できるため、圧縮率が向上する可能性がありますが、メモリ使用量も増加します。
  • ハフマン符号化: LZ77によって生成されたシンボル(リテラルバイト、長さ、距離)を、出現頻度に基づいて可変長のビット列に符号化することで、さらに圧縮率を高めます。

Go言語の compress/flate パッケージは、このDEFLATEアルゴリズムを実装しており、NewWriter で圧縮器を、NewReader で解凍器を作成します。圧縮レベル(BestSpeed, DefaultCompression, BestCompression)を指定することで、圧縮速度と圧縮率のトレードオフを調整できます。

ioutil.ReadFilebytes.Buffer

  • ioutil.ReadFile(filename string): 指定されたファイルの内容をすべて読み込み、[]byte スライスとして返します。
  • bytes.Buffer: 可変長のバイトバッファを実装した型です。io.Writer インターフェースと io.Reader インターフェースの両方を実装しており、データの書き込みと読み出しが効率的に行えます。圧縮器の出力先や解凍器の入力元としてよく使用されます。

技術的詳細

このコミットでは、compress/flate パッケージのベンチマークにおいて、以下の主要な変更が行われています。

  1. 新しいテストデータの導入:

    • 従来の testdata/e.txt (円周率eの桁数、10KB) に加えて、testdata/Mark.Twain-Tom.Sawyer.txt (マーク・トウェインの『トム・ソーヤーの冒険』、英語のテキスト) が新しいテストデータとして追加されました。
    • reader_test.gowriter_test.go の両方に testfiles というグローバル変数(配列)が定義され、これらのテストファイルへのパスが管理されます。digitstwain という定数で、それぞれのファイルのインデックスが定義されています。
  2. ベンチマーク関数の汎用化:

    • benchmarkDecoder 関数は benchmarkDecode に、benchmarkEncoder 関数は benchmarkEncoder にそれぞれ名称が変更され、引数に testfile int が追加されました。これにより、どのテストファイルを使用するかをベンチマーク関数内で指定できるようになりました。
    • ioutil.ReadFile の呼び出しが testfiles[testfile] を参照するように変更され、動的にテストファイルを読み込むようになりました。
    • 入力データが空の場合のチェック (if len(buf0) == 0) が追加され、より堅牢になりました。
    • ベンチマーク対象のデータ長 n に対して、元のテストファイル buf0 の長さが短い場合に、buf0n-i の長さに切り詰める処理が追加されました。これにより、指定されたデータ長 n に正確に合わせるための繰り返しコピーが適切に行われるようになります。
  3. ベンチマーク関数の命名規則の変更と追加:

    • 従来の BenchmarkDecoderBestSpeed1K のような命名規則から、BenchmarkDecodeDigitsSpeed1e4BenchmarkDecodeTwainDefault1e5 のように、データソース(Digits または Twain)、圧縮レベル(Speed, Default, Compress)、データサイズ(1e4, 1e5, 1e6)を明確に示す命名規則に変更されました。
    • これにより、各ベンチマークが何を測定しているのかが一目でわかるようになりました。
    • speed, default_, compress という定数が導入され、BestSpeed, DefaultCompression, BestCompression を簡潔に表現できるようになりました。
    • 新しいテストデータ (Twain) に対応するベンチマーク関数が多数追加されました。これにより、英語テキストに対する圧縮・解凍性能が詳細に測定できるようになりました。

これらの変更により、compress/flate パッケージのベンチマークは、より現実的なデータセットで、より詳細な性能評価が可能になりました。特に、繰り返しパターンが多い数値データと、より複雑な構造を持つ自然言語テキストの両方で性能を比較できるようになり、DEFLATEアルゴリズムの特性が異なるデータに対してどのように現れるかを把握する上で非常に有用です。

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

src/pkg/compress/flate/reader_test.go

--- a/src/pkg/compress/flate/reader_test.go
+++ b/src/pkg/compress/flate/reader_test.go
@@ -12,20 +12,47 @@ import (
 	"testing"
 )
 
-func benchmarkDecoder(b *testing.B, level, n int) {
+const (
+	digits = iota
+	twain
+)
+
+var testfiles = []string{
+	// Digits is the digits of the irrational number e. Its decimal representation
+	// does not repeat, but there are only 10 posible digits, so it should be
+	// reasonably compressible.
+	//
+	// TODO(nigeltao): e.txt is only 10K long, so when benchmarking 100K or 1000K
+	// of input, the digits are just repeated from the beginning, and flate can
+	// trivially compress this as a length/distance copy operation. Thus,
+	// BenchmarkDecodeDigitsXxx1e6 is essentially just measuring the speed of the
+	// forwardCopy implementation, but isn't particularly representative of real
+	// usage. The TODO is to replace e.txt with 100K digits, not just 10K digits,
+	// since that's larger than the windowSize 1<<15 (= 32768).
+	digits: "../testdata/e.txt",
+	// Twain is Project Gutenberg's edition of Mark Twain's classic English novel.
+	twain: "../testdata/Mark.Twain-Tom.Sawyer.txt",
+}
+
+func benchmarkDecode(b *testing.B, testfile, level, n int) {
 	b.StopTimer()
 	b.SetBytes(int64(n))
-	buf0, err := ioutil.ReadFile("../testdata/e.txt")
+	buf0, err := ioutil.ReadFile(testfiles[testfile])
 	if err != nil {
 		b.Fatal(err)
 	}
-	buf0 = buf0[:10000]
+	if len(buf0) == 0 {
+		b.Fatalf("test file %q has no data", testfiles[testfile])
+	}
 	compressed := new(bytes.Buffer)
 	w, err := NewWriter(compressed, level)
 	if err != nil {
 		b.Fatal(err)
 	}
 	for i := 0; i < n; i += len(buf0) {
+		if len(buf0) > n-i {
+			buf0 = buf0[:n-i]
+		}
 		io.Copy(w, bytes.NewBuffer(buf0))
 	}
 	w.Close()
@@ -38,38 +65,29 @@ func benchmarkDecoder(b *testing.B, level, n int) {
 	}
 }
 
-func BenchmarkDecoderBestSpeed1K(b *testing.B) {
-	benchmarkDecoder(b, BestSpeed, 1e4)
-}
-
-func BenchmarkDecoderBestSpeed10K(b *testing.B) {
-	benchmarkDecoder(b, BestSpeed, 1e5)
-}
-
-func BenchmarkDecoderBestSpeed100K(b *testing.B) {
-	benchmarkDecoder(b, BestSpeed, 1e6)
-}
-
-func BenchmarkDecoderDefaultCompression1K(b *testing.B) {
-	benchmarkDecoder(b, DefaultCompression, 1e4)
-}
-
-func BenchmarkDecoderDefaultCompression10K(b *testing.B) {
-	benchmarkDecoder(b, DefaultCompression, 1e5)
-}
-
-func BenchmarkDecoderDefaultCompression100K(b *testing.B) {
-	benchmarkDecoder(b, DefaultCompression, 1e6)
-}
-
-func BenchmarkDecoderBestCompression1K(b *testing.B) {
-	benchmarkDecoder(b, BestCompression, 1e4)
-}
-
-func BenchmarkDecoderBestCompression10K(b *testing.B) {
-	benchmarkDecoder(b, BestCompression, 1e5)
-}
+// These short names are so that gofmt doesn't break the BenchmarkXxx function
+// bodies below over multiple lines.
+const (
+	speed    = BestSpeed
+	default_ = DefaultCompression
+	compress = BestCompression
+)
 
-func BenchmarkDecoderBestCompression100K(b *testing.B) {
-	benchmarkDecoder(b, BestCompression, 1e6)
-}
+func BenchmarkDecodeDigitsSpeed1e4(b *testing.B)    { benchmarkDecode(b, digits, speed, 1e4) }
+func BenchmarkDecodeDigitsSpeed1e5(b *testing.B)    { benchmarkDecode(b, digits, speed, 1e5) }
+func BenchmarkDecodeDigitsSpeed1e6(b *testing.B)    { benchmarkDecode(b, digits, speed, 1e6) }
+func BenchmarkDecodeDigitsDefault1e4(b *testing.B)  { benchmarkDecode(b, digits, default_, 1e4) }
+func BenchmarkDecodeDigitsDefault1e5(b *testing.B)  { benchmarkDecode(b, digits, default_, 1e5) }
+func BenchmarkDecodeDigitsDefault1e6(b *testing.B)  { benchmarkDecode(b, digits, default_, 1e6) }
+func BenchmarkDecodeDigitsCompress1e4(b *testing.B) { benchmarkDecode(b, digits, compress, 1e4) }
+func BenchmarkDecodeDigitsCompress1e5(b *testing.B) { benchmarkDecode(b, digits, compress, 1e5) }
+func BenchmarkDecodeDigitsCompress1e6(b *testing.B) { benchmarkDecode(b, digits, compress, 1e6) }
+func BenchmarkDecodeTwainSpeed1e4(b *testing.B)     { benchmarkDecode(b, twain, speed, 1e4) }
+func BenchmarkDecodeTwainSpeed1e5(b *testing.B)     { benchmarkDecode(b, twain, speed, 1e5) }
+func BenchmarkDecodeTwainSpeed1e6(b *testing.B)     { benchmarkDecode(b, twain, speed, 1e6) }
+func BenchmarkDecodeTwainDefault1e4(b *testing.B)   { benchmarkDecode(b, twain, default_, 1e4) }
+func BenchmarkDecodeTwainDefault1e5(b *testing.B)   { benchmarkDecode(b, twain, default_, 1e5) }
+func BenchmarkDecodeTwainDefault1e6(b *testing.B)   { benchmarkDecode(b, twain, default_, 1e6) }
+func BenchmarkDecodeTwainCompress1e4(b *testing.B)  { benchmarkDecode(b, twain, compress, 1e4) }
+func BenchmarkDecodeTwainCompress1e5(b *testing.B)  { benchmarkDecode(b, twain, compress, 1e5) }
+func BenchmarkDecodeTwainCompress1e6(b *testing.B)  { benchmarkDecode(b, twain, compress, 1e6) }

src/pkg/compress/flate/writer_test.go

--- a/src/pkg/compress/flate/writer_test.go
+++ b/src/pkg/compress/flate/writer_test.go
@@ -10,16 +10,21 @@ import (
 	"testing"
 )
 
-func benchmarkEncoder(b *testing.B, level, n int) {
+func benchmarkEncoder(b *testing.B, testfile, level, n int) {
 	b.StopTimer()
 	b.SetBytes(int64(n))
-	buf0, err := ioutil.ReadFile("../testdata/e.txt")
+	buf0, err := ioutil.ReadFile(testfiles[testfile])
 	if err != nil {
 		b.Fatal(err)
 	}
-	buf0 = buf0[:10000]
+	if len(buf0) == 0 {
+		b.Fatalf("test file %q has no data", testfiles[testfile])
+	}
 	buf1 := make([]byte, n)
 	for i := 0; i < n; i += len(buf0) {
+		if len(buf0) > n-i {
+			buf0 = buf0[:n-i]
+		}
 		copy(buf1[i:], buf0)
 	}
 	buf0 = nil
@@ -35,38 +40,21 @@ func benchmarkEncoder(b *testing.B, level, n int) {\n 	}\n }\n \n-func BenchmarkEncoderBestSpeed1K(b *testing.B) {\n-\tbenchmarkEncoder(b, BestSpeed, 1e4)\n-}\n-\n-func BenchmarkEncoderBestSpeed10K(b *testing.B) {\n-\tbenchmarkEncoder(b, BestSpeed, 1e5)\n-}\n-\n-func BenchmarkEncoderBestSpeed100K(b *testing.B) {\n-\tbenchmarkEncoder(b, BestSpeed, 1e6)\n-}\n-\n-func BenchmarkEncoderDefaultCompression1K(b *testing.B) {\n-\tbenchmarkEncoder(b, DefaultCompression, 1e4)\n-}\n-\n-func BenchmarkEncoderDefaultCompression10K(b *testing.B) {\n-\tbenchmarkEncoder(b, DefaultCompression, 1e5)\n-}\n-\n-func BenchmarkEncoderDefaultCompression100K(b *testing.B) {\n-\tbenchmarkEncoder(b, DefaultCompression, 1e6)\n-}\n-\n-func BenchmarkEncoderBestCompression1K(b *testing.B) {\n-\tbenchmarkEncoder(b, BestCompression, 1e4)\n-}\n-\n-func BenchmarkEncoderBestCompression10K(b *testing.B) {\n-\tbenchmarkEncoder(b, BestCompression, 1e5)\n-}\n-\n-func BenchmarkEncoderBestCompression100K(b *testing.B) {\n-\tbenchmarkEncoder(b, BestCompression, 1e6)\n-}\n+func BenchmarkEncodeDigitsSpeed1e4(b *testing.B)    { benchmarkEncoder(b, digits, speed, 1e4) }\n+func BenchmarkEncodeDigitsSpeed1e5(b *testing.B)    { benchmarkEncoder(b, digits, speed, 1e5) }\n+func BenchmarkEncodeDigitsSpeed1e6(b *testing.B)    { benchmarkEncoder(b, digits, speed, 1e6) }\n+func BenchmarkEncodeDigitsDefault1e4(b *testing.B)  { benchmarkEncoder(b, digits, default_, 1e4) }\n+func BenchmarkEncodeDigitsDefault1e5(b *testing.B)  { benchmarkEncoder(b, digits, default_, 1e5) }\n+func BenchmarkEncodeDigitsDefault1e6(b *testing.B)  { benchmarkEncoder(b, digits, default_, 1e6) }\n+func BenchmarkEncodeDigitsCompress1e4(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e4) }\n+func BenchmarkEncodeDigitsCompress1e5(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e5) }\n+func BenchmarkEncodeDigitsCompress1e6(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e6) }\n+func BenchmarkEncodeTwainSpeed1e4(b *testing.B)     { benchmarkEncoder(b, twain, speed, 1e4) }\n+func BenchmarkEncodeTwainSpeed1e5(b *testing.B)     { benchmarkEncoder(b, twain, speed, 1e5) }\n+func BenchmarkEncodeTwainSpeed1e6(b *testing.B)     { benchmarkEncoder(b, twain, speed, 1e6) }\n+func BenchmarkEncodeTwainDefault1e4(b *testing.B)   { benchmarkEncoder(b, twain, default_, 1e4) }\n+func BenchmarkEncodeTwainDefault1e5(b *testing.B)   { benchmarkEncoder(b, twain, default_, 1e5) }\n+func BenchmarkEncodeTwainDefault1e6(b *testing.B)   { benchmarkEncoder(b, twain, default_, 1e6) }\n+func BenchmarkEncodeTwainCompress1e4(b *testing.B)  { benchmarkEncoder(b, twain, compress, 1e4) }\n+func BenchmarkEncodeTwainCompress1e5(b *testing.B)  { benchmarkEncoder(b, twain, compress, 1e5) }\n+func BenchmarkEncodeTwainCompress1e6(b *testing.B)  { benchmarkEncoder(b, twain, compress, 1e6) }\n```

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

### `reader_test.go` および `writer_test.go` 共通の変更点

1.  **`testfiles` 変数と `digits`, `twain` 定数の追加**:
    *   `const (digits = iota; twain)`: `iota` を使用して `digits` を0、`twain` を1に自動的に割り当てています。これは、`testfiles` 配列のインデックスとして使用されます。
    *   `var testfiles = []string{...}`: ベンチマークに使用するテストファイルのパスを定義しています。
        *   `digits: "../testdata/e.txt"`: 従来のeの桁数のファイル。コメントで、このファイルが10KBと短く、flateのウィンドウサイズ(32KB)よりも小さいため、長いベンチマークでは自明な圧縮になってしまうという問題点が明記されています。将来的にこのファイルを100KBに増やす計画も示唆されています。
        *   `twain: "../testdata/Mark.Twain-Tom.Sawyer.txt"`: 新しく追加されたマーク・トウェインの小説のファイル。より現実的な英語テキストデータです。

2.  **`benchmarkDecode` / `benchmarkEncoder` 関数のシグネチャ変更**:
    *   `func benchmarkDecode(b *testing.B, testfile, level, n int)`: 従来の `benchmarkDecoder` に `testfile int` 引数が追加されました。これにより、ベンチマーク関数内でどのテストファイルを使用するかを動的に選択できるようになりました。
    *   `buf0, err := ioutil.ReadFile(testfiles[testfile])`: `testfiles` 配列と `testfile` 引数を使って、適切なテストファイルを読み込むように変更されました。
    *   `if len(buf0) == 0 { b.Fatalf("test file %q has no data", testfiles[testfile]) }`: 読み込んだテストファイルが空の場合にエラーを発生させるチェックが追加され、堅牢性が向上しました。
    *   `if len(buf0) > n-i { buf0 = buf0[:n-i] }`: ベンチマーク対象のデータ長 `n` に対して、元のテストデータ `buf0` の長さが短い場合に、`buf0` を `n-i` の長さに切り詰める処理が追加されました。これにより、`io.Copy` や `copy` でデータを繰り返す際に、正確に `n` バイトのデータが処理されるようになります。

3.  **新しいベンチマーク関数の定義**:
    *   `const (speed = BestSpeed; default_ = DefaultCompression; compress = BestCompression)`: 圧縮レベルを表す定数に短いエイリアスを定義し、ベンチマーク関数の記述を簡潔にしています。
    *   従来の `BenchmarkDecoderBestSpeed1K` のような関数は削除され、新しい命名規則に基づいた多数のベンチマーク関数が追加されました。
    *   例: `BenchmarkDecodeDigitsSpeed1e4`, `BenchmarkDecodeTwainDefault1e5`, `BenchmarkEncodeDigitsCompress1e6` など。
    *   これらの関数は、`benchmarkDecode` または `benchmarkEncoder` を呼び出し、`digits` または `twain` のテストファイル、圧縮レベル、データサイズを引数として渡します。

これらの変更により、`compress/flate` パッケージのベンチマークは、より多様なデータセットと圧縮レベルの組み合わせで、より詳細かつ実用的な性能評価が可能になりました。特に、自然言語テキストのような現実的なデータに対する性能特性を把握できるようになったことは大きな進歩です。

## 関連リンク

*   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)
*   DEFLATE (Wikipedia): [https://ja.wikipedia.org/wiki/DEFLATE](https://ja.wikipedia.org/wiki/DEFLATE)
*   LZ77 (Wikipedia): [https://ja.wikipedia.org/wiki/LZ77%E3%81%8A%E3%82%88%E3%81%B3LZ78](https://ja.wikipedia.org/wiki/LZ77%E3%81%8A%E3%82%88%E3%81%B3LZ78)
*   ハフマン符号 (Wikipedia): [https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%95%E3%83%9E%E3%83%B3%E7%AC%A6%E5%8F%B7](https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%95%E3%83%9E%E3%83%B3%E7%AC%A6%E5%8F%B7)

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

*   Project Gutenberg: [https://www.gutenberg.org/](https://www.gutenberg.org/) (マーク・トウェインの『トム・ソーヤーの冒険』のテキストデータは、このような公開されている電子書籍プロジェクトから取得された可能性があります。)
*   Go言語のベンチマークに関する公式ドキュメントやブログ記事 (一般的なGoベンチマークの知識として参照)
*   DEFLATEアルゴリズムに関する技術文書 (一般的な圧縮アルゴリズムの知識として参照)