[インデックス 17470] ファイルの概要
このコミットは、Go言語の標準ライブラリである compress/flate
パッケージのテストファイル src/pkg/compress/flate/reader_test.go
に変更を加えています。具体的には、ベンチマークテストにおいて bytes.NewBuffer
の代わりに bytes.NewReader
を使用するように修正し、さらにベンチマーク結果にメモリ割り当て(アロケーション)の情報を報告するように変更しています。
コミット
commit fca660892db526f86b9d113e58e292c27583b6a7
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Sep 4 15:31:46 2013 -0700
compress/flate: use bytes.NewReader instead of NewBuffer in test
Also, report allocations in benchmark.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/13410044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fca660892db526f86b9d113e58e292c27583b6a7
元コミット内容
compress/flate: use bytes.NewReader instead of NewBuffer in test
Also, report allocations in benchmark.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/13410044
変更の背景
このコミットの背景には、Go言語のベンチマークテストの精度と有用性を向上させる目的があります。
-
bytes.NewBuffer
からbytes.NewReader
への変更:bytes.Buffer
は可変長のバイトスライスを扱うための型で、主に書き込み操作(Write
メソッドなど)によって内部バッファが拡張される可能性があります。一方、bytes.Reader
は固定長のバイトスライスを読み込むための型で、内部バッファの拡張は発生しません。ベンチマークテストにおいて、入力データが固定であるにもかかわらずbytes.NewBuffer
を使用すると、内部で不要なメモリ割り当てやコピーが発生し、ベンチマーク結果にノイズが混入する可能性があります。特に、io.Copy
のような操作でbytes.Buffer
をソースとして使用する場合、bytes.Buffer
はio.Reader
インターフェースを満たしますが、その実装がbytes.Reader
よりも効率的でない場合があります。この変更は、ベンチマークの対象となるcompress/flate
パッケージ自体の性能をより正確に測定するために、テストハーネス(テストコード)が引き起こすオーバーヘッドを最小限に抑えることを目的としています。 -
ベンチマークでのメモリ割り当て報告 (
b.ReportAllocs()
): Goのベンチマークは通常、実行時間(ops/sec)を測定しますが、b.ReportAllocs()
を呼び出すことで、ベンチマーク実行中に発生したメモリ割り当ての回数と合計バイト数も報告されるようになります。これは、特にガベージコレクション(GC)の影響を受けやすいGo言語において、パフォーマンスチューニングの重要な指標となります。実行時間だけでなく、メモリ割り当ての状況を把握することで、より効率的なコードパスを見つけ出し、GCの負荷を軽減する改善を行うための手がかりを得ることができます。この変更は、compress/flate
パッケージのデコード処理がどれだけメモリを効率的に使用しているかを評価するために追加されました。
要するに、このコミットは、compress/flate
パッケージのベンチマークテストをより正確かつ包括的にすることで、将来的な性能改善のための基盤を強化することを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と標準ライブラリに関する知識が必要です。
-
compress/flate
パッケージ:compress/flate
パッケージは、DEFLATEアルゴリズム(RFC 1951で定義)を実装したGo言語の標準ライブラリです。DEFLATEは、Zlibやgzipなどの圧縮形式の基盤となるデータ圧縮アルゴリズムです。このパッケージは、データの圧縮(エンコード)と解凍(デコード)機能を提供します。このコミットでは、主にデコード(解凍)のベンチマークテストが対象となっています。 -
bytes
パッケージ:bytes
パッケージは、バイトスライスを操作するためのユーティリティ関数と型を提供します。bytes.Buffer
: 可変長のバイトバッファを実装する型です。io.Reader
およびio.Writer
インターフェースを実装しており、バイトスライスへの書き込みやバイトスライスからの読み込みが可能です。内部的には必要に応じてバッファが拡張されます。bytes.Reader
: 固定長のバイトスライスから読み込むための型です。io.Reader
,io.ReaderAt
,io.Seeker
,io.ByteReader
,io.RuneReader
インターフェースを実装しています。内部バッファは固定であり、拡張されることはありません。
-
io
パッケージとio.Copy
:io
パッケージは、I/Oプリミティブ(Reader、Writerなど)を定義するGo言語の基本的なパッケージです。io.Reader
インターフェース:Read(p []byte) (n int, err error)
メソッドを持つインターフェースで、データソースからの読み込みを抽象化します。io.Writer
インターフェース:Write(p []byte) (n int, err error)
メソッドを持つインターフェースで、データシンクへの書き込みを抽象化します。io.Copy(dst io.Writer, src io.Reader) (written int64, err error)
:src
からデータを読み込み、dst
に書き込むユーティリティ関数です。src
がio.WriterTo
インターフェースを実装している場合、そのWriteTo
メソッドが使用され、dst
がio.ReaderFrom
インターフェースを実装している場合、そのReadFrom
メソッドが使用されます。これにより、効率的なデータ転送が可能です。
-
testing
パッケージとベンチマーク:testing
パッケージは、Go言語のテストとベンチマークをサポートするための機能を提供します。*testing.B
: ベンチマーク関数に渡される型で、ベンチマークの実行を制御し、結果を報告するためのメソッドを提供します。b.ReportAllocs()
: ベンチマーク実行中に発生したメモリ割り当ての回数と合計バイト数を報告するように設定します。このメソッドは、ベンチマーク関数の開始時に一度だけ呼び出す必要があります。b.StopTimer()
/b.StartTimer()
: ベンチマークの計測時間を一時停止/再開します。セットアップやクリーンアップなど、ベンチマーク対象外の処理の時間を計測から除外するために使用されます。b.SetBytes(n int64)
: 1回の操作で処理されるバイト数を設定します。これにより、ベンチマーク結果が「操作/秒」だけでなく「バイト/秒」としても報告されるようになります。b.N
: ベンチマーク関数が実行されるイテレーション回数です。testing
パッケージが自動的に調整し、統計的に有意な結果が得られるようにします。
-
runtime.GC()
:runtime
パッケージは、Goランタイムとの相互作用を可能にする関数を提供します。runtime.GC()
は、ガベージコレクタを強制的に実行します。ベンチマークにおいて、GCの影響を均一にするためや、特定の処理の直後にメモリをクリーンアップするために使用されることがあります。
技術的詳細
このコミットの技術的な核心は、bytes.NewBuffer
と bytes.NewReader
の使い分け、およびベンチマークにおけるメモリ割り当ての重要性にあります。
bytes.NewBuffer
と bytes.NewReader
の違いとベンチマークへの影響
-
bytes.NewBuffer(buf []byte)
: この関数は、与えられたバイトスライスbuf
を初期内容とするbytes.Buffer
を作成します。bytes.Buffer
は内部に可変長のバイトスライスを持っており、書き込み操作(例:io.Copy
のsrc
がbytes.Buffer
の場合、bytes.Buffer
のRead
メソッドが呼ばれる)によって、必要に応じて内部バッファが拡張される可能性があります。 ベンチマークの文脈では、bytes.NewBuffer
を使用すると、たとえ読み込み専用の操作であっても、bytes.Buffer
の内部実装がbytes.Reader
よりも複雑であるため、わずかながらオーバーヘッドが発生する可能性があります。特に、io.Copy
のような操作では、bytes.Buffer
がio.Reader
インターフェースを満たすために、内部で追加のチェックや処理が行われることが考えられます。 -
bytes.NewReader(buf []byte)
: この関数は、与えられたバイトスライスbuf
を読み込み専用のデータソースとするbytes.Reader
を作成します。bytes.Reader
は内部バッファを拡張することなく、常に固定のバイトスライスから読み込みを行います。 ベンチマークの文脈では、bytes.NewReader
は読み込み専用の操作に特化しているため、bytes.NewBuffer
よりも効率的です。不要なメモリ割り当てやバッファの拡張ロジックがないため、より純粋な読み込み性能を測定できます。io.Copy
のsrc
としてbytes.NewReader
を使用する場合、bytes.Reader
はio.Reader
インターフェースを効率的に実装しており、特にio.WriterTo
インターフェースも実装しているため、io.Copy
が内部で最適化されたパス(WriteTo
メソッドの利用)を選択する可能性が高く、これによりコピー操作全体の効率が向上します。
この変更は、compress/flate
のデコード処理のベンチマークにおいて、入力データの準備段階でのオーバーヘッドを削減し、より正確なデコード性能を測定することを目的としています。
b.ReportAllocs()
の重要性
Go言語では、メモリ割り当て(アロケーション)はガベージコレクション(GC)のトリガーとなり、GCはプログラムの実行を一時停止させる可能性があります。したがって、メモリ割り当ての回数と量が少ないほど、GCの頻度と時間が減少し、全体的なパフォーマンスが向上する傾向があります。
b.ReportAllocs()
をベンチマークに追加することで、以下の情報が得られます。
allocs/op
: 1回の操作あたりのメモリ割り当て回数。B/op
: 1回の操作あたりのメモリ割り当てバイト数。
これらの指標は、コードがどれだけメモリ効率が良いかを示します。例えば、あるベンチマークで allocs/op
が高い場合、それはループ内でオブジェクトが頻繁に作成されていることを示唆しており、オブジェクトプールの利用や、スライス/マップの事前割り当てなどによって改善できる可能性があります。
このコミットでは、compress/flate
のデコード処理がどれだけメモリを効率的に使用しているかを評価するために b.ReportAllocs()
が追加されました。これにより、将来的にデコード処理のメモリ効率を改善するための具体的な目標設定や、改善効果の検証が可能になります。
コアとなるコードの変更箇所
--- a/src/pkg/compress/flate/reader_test.go
+++ b/src/pkg/compress/flate/reader_test.go
@@ -37,6 +37,7 @@ var testfiles = []string{
}
func benchmarkDecode(b *testing.B, testfile, level, n int) {
+ b.ReportAllocs()
b.StopTimer()
b.SetBytes(int64(n))
buf0, err := ioutil.ReadFile(testfiles[testfile])
@@ -55,7 +56,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) {
if len(buf0) > n-i {
buf0 = buf0[:n-i]
}
- io.Copy(w, bytes.NewBuffer(buf0))
+ io.Copy(w, bytes.NewReader(buf0))
}
w.Close()
buf1 := compressed.Bytes()
@@ -63,7 +64,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) {
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
- io.Copy(ioutil.Discard, NewReader(bytes.NewBuffer(buf1)))
+ io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1)))
}
}
コアとなるコードの解説
このコミットでは、src/pkg/compress/flate/reader_test.go
ファイル内の benchmarkDecode
関数に3つの変更が加えられています。
-
b.ReportAllocs()
の追加:func benchmarkDecode(b *testing.B, testfile, level, n int) { + b.ReportAllocs() b.StopTimer() // ... }
benchmarkDecode
関数の冒頭にb.ReportAllocs()
が追加されました。これにより、このベンチマークを実行する際に、Goのテストフレームワークが実行時間だけでなく、ベンチマーク中に発生したメモリ割り当ての回数と合計バイト数も計測し、結果として報告するようになります。これは、compress/flate
のデコード処理のメモリ効率を評価するための重要な指標となります。 -
io.Copy
のソースをbytes.NewBuffer
からbytes.NewReader
へ変更 (1回目):- io.Copy(w, bytes.NewBuffer(buf0)) + io.Copy(w, bytes.NewReader(buf0))
これは、圧縮前のデータ
buf0
をflate.Writer
(w
) にコピーする部分の変更です。以前はbytes.NewBuffer(buf0)
を使用していましたが、これをbytes.NewReader(buf0)
に変更しました。buf0
はioutil.ReadFile
で読み込まれた固定長のバイトスライスであり、読み込み専用のデータソースとして使用されます。このような場合、bytes.NewReader
を使用する方がbytes.NewBuffer
よりも効率的です。bytes.NewReader
は内部でバッファの拡張を考慮する必要がなく、読み込みに特化しているため、ベンチマークのオーバーヘッドを削減し、より正確な圧縮処理の性能を測定できます。 -
io.Copy
のソースをbytes.NewBuffer
からbytes.NewReader
へ変更 (2回目):- io.Copy(ioutil.Discard, NewReader(bytes.NewBuffer(buf1))) + io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1)))
これは、圧縮されたデータ
buf1
をflate.Reader
(NewReader(bytes.NewBuffer(buf1))
) を通してioutil.Discard
(書き込まれたデータを破棄するio.Writer
) にコピーする部分の変更です。ここでも、bytes.NewBuffer(buf1)
がbytes.NewReader(buf1)
に変更されました。buf1
もまた、圧縮された固定長のバイトスライスであり、デコード処理の入力として使用されます。ここでも、読み込み専用のデータソースとしてbytes.NewReader
を使用することで、デコード処理のベンチマークにおける入力準備のオーバーヘッドを最小限に抑え、より純粋なデコード性能を測定することが可能になります。
これらの変更は、compress/flate
パッケージのベンチマークテストの精度と有用性を向上させることを目的としています。特に、メモリ割り当ての報告は、将来的な性能最適化のための重要な情報を提供します。
関連リンク
- Go CL 13410044: https://golang.org/cl/13410044
参考にした情報源リンク
- Go言語の
bytes
パッケージドキュメント: https://pkg.go.dev/bytes - Go言語の
io
パッケージドキュメント: https://pkg.go.dev/io - Go言語の
testing
パッケージドキュメント: https://pkg.go.dev/testing - Go言語の
compress/flate
パッケージドキュメント: https://pkg.go.dev/compress/flate - Go言語のベンチマークに関する公式ドキュメントやブログ記事 (一般的な知識として参照)
- The Go Programming Language Blog: Benchmarking Go Programs: https://go.dev/blog/benchmarking
- Go Wiki: Benchmarking: https://go.dev/wiki/Benchmarking
- RFC 1951 - DEFLATE Compressed Data Format Specification version 1.3: https://www.rfc-editor.org/rfc/rfc1951
- Go言語のガベージコレクションに関する情報 (一般的な知識として参照)
- Go言語のメモリ管理とGCの仕組みに関する記事など# [インデックス 17470] ファイルの概要
このコミットは、Go言語の標準ライブラリである compress/flate
パッケージのテストファイル src/pkg/compress/flate/reader_test.go
に変更を加えています。具体的には、ベンチマークテストにおいて bytes.NewBuffer
の代わりに bytes.NewReader
を使用するように修正し、さらにベンチマーク結果にメモリ割り当て(アロケーション)の情報を報告するように変更しています。
コミット
commit fca660892db526f86b9d113e58e292c27583b6a7
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Sep 4 15:31:46 2013 -0700
compress/flate: use bytes.NewReader instead of NewBuffer in test
Also, report allocations in benchmark.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/13410044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fca660892db526f86b9d113e58e292c27583b6a7
元コミット内容
compress/flate: use bytes.NewReader instead of NewBuffer in test
Also, report allocations in benchmark.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/13410044
変更の背景
このコミットの背景には、Go言語のベンチマークテストの精度と有用性を向上させる目的があります。
-
bytes.NewBuffer
からbytes.NewReader
への変更:bytes.Buffer
は可変長のバイトスライスを扱うための型で、主に書き込み操作(Write
メソッドなど)によって内部バッファが拡張される可能性があります。一方、bytes.Reader
は固定長のバイトスライスを読み込むための型で、内部バッファの拡張は発生しません。ベンチマークテストにおいて、入力データが固定であるにもかかわらずbytes.NewBuffer
を使用すると、内部で不要なメモリ割り当てやコピーが発生し、ベンチマーク結果にノイズが混入する可能性があります。特に、io.Copy
のような操作でbytes.Buffer
をソースとして使用する場合、bytes.Buffer
はio.Reader
インターフェースを満たしますが、その実装がbytes.Reader
よりも効率的でない場合があります。この変更は、ベンチマークの対象となるcompress/flate
パッケージ自体の性能をより正確に測定するために、テストハーネス(テストコード)が引き起こすオーバーヘッドを最小限に抑えることを目的としています。 -
ベンチマークでのメモリ割り当て報告 (
b.ReportAllocs()
): Goのベンチマークは通常、実行時間(ops/sec)を測定しますが、b.ReportAllocs()
を呼び出すことで、ベンチマーク実行中に発生したメモリ割り当ての回数と合計バイト数も報告されるようになります。これは、特にガベージコレクション(GC)の影響を受けやすいGo言語において、パフォーマンスチューニングの重要な指標となります。実行時間だけでなく、メモリ割り当ての状況を把握することで、より効率的なコードパスを見つけ出し、GCの負荷を軽減する改善を行うための手がかりを得ることができます。この変更は、compress/flate
パッケージのデコード処理がどれだけメモリを効率的に使用しているかを評価するために追加されました。
要するに、このコミットは、compress/flate
パッケージのベンチマークテストをより正確かつ包括的にすることで、将来的な性能改善のための基盤を強化することを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と標準ライブラリに関する知識が必要です。
-
compress/flate
パッケージ:compress/flate
パッケージは、DEFLATEアルゴリズム(RFC 1951で定義)を実装したGo言語の標準ライブラリです。DEFLATEは、Zlibやgzipなどの圧縮形式の基盤となるデータ圧縮アルゴリズムです。このパッケージは、データの圧縮(エンコード)と解凍(デコード)機能を提供します。このコミットでは、主にデコード(解凍)のベンチマークテストが対象となっています。 -
bytes
パッケージ:bytes
パッケージは、バイトスライスを操作するためのユーティリティ関数と型を提供します。bytes.Buffer
: 可変長のバイトバッファを実装する型です。io.Reader
およびio.Writer
インターフェースを実装しており、バイトスライスへの書き込みやバイトスライスからの読み込みが可能です。内部的には必要に応じてバッファが拡張されます。bytes.Reader
: 固定長のバイトスライスから読み込むための型です。io.Reader
,io.ReaderAt
,io.Seeker
,io.ByteReader
,io.RuneReader
インターフェースを実装しています。内部バッファは固定であり、拡張されることはありません。
-
io
パッケージとio.Copy
:io
パッケージは、I/Oプリミティブ(Reader、Writerなど)を定義するGo言語の基本的なパッケージです。io.Reader
インターフェース:Read(p []byte) (n int, err error)
メソッドを持つインターフェースで、データソースからの読み込みを抽象化します。io.Writer
インターフェース:Write(p []byte) (n int, err error)
メソッドを持つインターフェースで、データシンクへの書き込みを抽象化します。io.Copy(dst io.Writer, src io.Reader) (written int64, err error)
:src
からデータを読み込み、dst
に書き込むユーティリティ関数です。src
がio.WriterTo
インターフェースを実装している場合、そのWriteTo
メソッドが使用され、dst
がio.ReaderFrom
インターフェースを実装している場合、そのReadFrom
メソッドが使用されます。これにより、効率的なデータ転送が可能です。
-
testing
パッケージとベンチマーク:testing
パッケージは、Go言語のテストとベンチマークをサポートするための機能を提供します。*testing.B
: ベンチマーク関数に渡される型で、ベンチマークの実行を制御し、結果を報告するためのメソッドを提供します。b.ReportAllocs()
: ベンチマーク実行中に発生したメモリ割り当ての回数と合計バイト数を報告するように設定します。このメソッドは、ベンチマーク関数の開始時に一度だけ呼び出す必要があります。b.StopTimer()
/b.StartTimer()
: ベンチマークの計測時間を一時停止/再開します。セットアップやクリーンアップなど、ベンチマーク対象外の処理の時間を計測から除外するために使用されます。b.SetBytes(n int64)
: 1回の操作で処理されるバイト数を設定します。これにより、ベンチマーク結果が「操作/秒」だけでなく「バイト/秒」としても報告されるようになります。b.N
: ベンチマーク関数が実行されるイテレーション回数です。testing
パッケージが自動的に調整し、統計的に有意な結果が得られるようにします。
-
runtime.GC()
:runtime
パッケージは、Goランタイムとの相互作用を可能にする関数を提供します。runtime.GC()
は、ガベージコレクタを強制的に実行します。ベンチマークにおいて、GCの影響を均一にするためや、特定の処理の直後にメモリをクリーンアップするために使用されることがあります。
技術的詳細
このコミットの技術的な核心は、bytes.NewBuffer
と bytes.NewReader
の使い分け、およびベンチマークにおけるメモリ割り当ての重要性にあります。
bytes.NewBuffer
と bytes.NewReader
の違いとベンチマークへの影響
-
bytes.NewBuffer(buf []byte)
: この関数は、与えられたバイトスライスbuf
を初期内容とするbytes.Buffer
を作成します。bytes.Buffer
は内部に可変長のバイトスライスを持っており、書き込み操作(例:io.Copy
のsrc
がbytes.Buffer
の場合、bytes.Buffer
のRead
メソッドが呼ばれる)によって、必要に応じて内部バッファが拡張される可能性があります。 ベンチマークの文脈では、bytes.NewBuffer
を使用すると、たとえ読み込み専用の操作であっても、bytes.Buffer
の内部実装がbytes.Reader
よりも複雑であるため、わずかながらオーバーヘッドが発生する可能性があります。特に、io.Copy
のような操作では、bytes.Buffer
がio.Reader
インターフェースを満たすために、内部で追加のチェックや処理が行われることが考えられます。 -
bytes.NewReader(buf []byte)
: この関数は、与えられたバイトスライスbuf
を読み込み専用のデータソースとするbytes.Reader
を作成します。bytes.Reader
は内部バッファを拡張することなく、常に固定のバイトスライスから読み込みを行います。 ベンチマークの文脈では、bytes.NewReader
は読み込み専用の操作に特化しているため、bytes.NewBuffer
よりも効率的です。不要なメモリ割り当てやバッファの拡張ロジックがないため、より純粋な読み込み性能を測定できます。io.Copy
のsrc
としてbytes.NewReader
を使用する場合、bytes.Reader
はio.Reader
インターフェースを効率的に実装しており、特にio.WriterTo
インターフェースも実装しているため、io.Copy
が内部で最適化されたパス(WriteTo
メソッドの利用)を選択する可能性が高く、これによりコピー操作全体の効率が向上します。
この変更は、compress/flate
のデコード処理のベンチマークにおいて、入力データの準備段階でのオーバーヘッドを削減し、より正確なデコード性能を測定することを目的としています。
b.ReportAllocs()
の重要性
Go言語では、メモリ割り当て(アロケーション)はガベージコレクション(GC)のトリガーとなり、GCはプログラムの実行を一時停止させる可能性があります。したがって、メモリ割り当ての回数と量が少ないほど、GCの頻度と時間が減少し、全体的なパフォーマンスが向上する傾向があります。
b.ReportAllocs()
をベンチマークに追加することで、以下の情報が得られます。
allocs/op
: 1回の操作あたりのメモリ割り当て回数。B/op
: 1回の操作あたりのメモリ割り当てバイト数。
これらの指標は、コードがどれだけメモリ効率が良いかを示します。例えば、あるベンチマークで allocs/op
が高い場合、それはループ内でオブジェクトが頻繁に作成されていることを示唆しており、オブジェクトプールの利用や、スライス/マップの事前割り当てなどによって改善できる可能性があります。
このコミットでは、compress/flate
のデコード処理がどれだけメモリを効率的に使用しているかを評価するために b.ReportAllocs()
が追加されました。これにより、将来的にデコード処理のメモリ効率を改善するための具体的な目標設定や、改善効果の検証が可能になります。
コアとなるコードの変更箇所
--- a/src/pkg/compress/flate/reader_test.go
+++ b/src/pkg/compress/flate/reader_test.go
@@ -37,6 +37,7 @@ var testfiles = []string{
}
func benchmarkDecode(b *testing.B, testfile, level, n int) {
+ b.ReportAllocs()
b.StopTimer()
b.SetBytes(int64(n))
buf0, err := ioutil.ReadFile(testfiles[testfile])
@@ -55,7 +56,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) {
if len(buf0) > n-i {
buf0 = buf0[:n-i]
}
- io.Copy(w, bytes.NewBuffer(buf0))
+ io.Copy(w, bytes.NewReader(buf0))
}
w.Close()
buf1 := compressed.Bytes()
@@ -63,7 +64,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) {
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
- io.Copy(ioutil.Discard, NewReader(bytes.NewBuffer(buf1)))
+ io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1)))
}
}
コアとなるコードの解説
このコミットでは、src/pkg/compress/flate/reader_test.go
ファイル内の benchmarkDecode
関数に3つの変更が加えられています。
-
b.ReportAllocs()
の追加:func benchmarkDecode(b *testing.B, testfile, level, n int) { + b.ReportAllocs() b.StopTimer() // ... }
benchmarkDecode
関数の冒頭にb.ReportAllocs()
が追加されました。これにより、このベンチマークを実行する際に、Goのテストフレームワークが実行時間だけでなく、ベンチマーク中に発生したメモリ割り当ての回数と合計バイト数も計測し、結果として報告するようになります。これは、compress/flate
のデコード処理のメモリ効率を評価するための重要な指標となります。 -
io.Copy
のソースをbytes.NewBuffer
からbytes.NewReader
へ変更 (1回目):- io.Copy(w, bytes.NewBuffer(buf0)) + io.Copy(w, bytes.NewReader(buf0))
これは、圧縮前のデータ
buf0
をflate.Writer
(w
) にコピーする部分の変更です。以前はbytes.NewBuffer(buf0)
を使用していましたが、これをbytes.NewReader(buf0)
に変更しました。buf0
はioutil.ReadFile
で読み込まれた固定長のバイトスライスであり、読み込み専用のデータソースとして使用されます。このような場合、bytes.NewReader
を使用する方がbytes.NewBuffer
よりも効率的です。bytes.NewReader
は内部でバッファの拡張を考慮する必要がなく、読み込みに特化しているため、ベンチマークのオーバーヘッドを削減し、より正確な圧縮処理の性能を測定できます。 -
io.Copy
のソースをbytes.NewBuffer
からbytes.NewReader
へ変更 (2回目):- io.Copy(ioutil.Discard, NewReader(bytes.NewBuffer(buf1))) + io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1)))
これは、圧縮されたデータ
buf1
をflate.Reader
(NewReader(bytes.NewBuffer(buf1))
) を通してioutil.Discard
(書き込まれたデータを破棄するio.Writer
) にコピーする部分の変更です。ここでも、bytes.NewBuffer(buf1)
がbytes.NewReader(buf1)
に変更されました。buf1
もまた、圧縮された固定長のバイトスライスであり、デコード処理の入力として使用されます。ここでも、読み込み専用のデータソースとしてbytes.NewReader
を使用することで、デコード処理のベンチマークにおける入力準備のオーバーヘッドを最小限に抑え、より純粋なデコード性能を測定することが可能になります。
これらの変更は、compress/flate
パッケージのベンチマークテストの精度と有用性を向上させることを目的としています。特に、メモリ割り当ての報告は、将来的な性能最適化のための重要な情報を提供します。
関連リンク
- Go CL 13410044: https://golang.org/cl/13410044
参考にした情報源リンク
- Go言語の
bytes
パッケージドキュメント: https://pkg.go.dev/bytes - Go言語の
io
パッケージドキュメント: https://pkg.go.dev/io - Go言語の
testing
パッケージドキュメント: https://pkg.go.dev/testing - Go言語の
compress/flate
パッケージドキュメント: https://pkg.go.dev/compress/flate - Go言語のベンチマークに関する公式ドキュメントやブログ記事 (一般的な知識として参照)
- The Go Programming Language Blog: Benchmarking Go Programs: https://go.dev/blog/benchmarking
- Go Wiki: Benchmarking: https://go.dev/wiki/Benchmarking
- RFC 1951 - DEFLATE Compressed Data Format Specification version 1.3: https://www.rfc-editor.org/rfc/rfc1951
- Go言語のガベージコレクションに関する情報 (一般的な知識として参照)
- Go言語のメモリ管理とGCの仕組みに関する記事など