[インデックス 13975] ファイルの概要
このコミットは、Go言語の標準ライブラリであるbufio
パッケージのReader
型にio.WriterTo
インターフェースの実装を追加するものです。これにより、bufio.Reader
から直接データをio.Writer
に効率的に書き出すことが可能になり、特にio.Copy
関数を使用した場合のパフォーマンスが大幅に向上します。
コミット
commit e289a2b913d476cf8592728e6d50767d0e29afde
Author: Michael Chaten <mchaten@gmail.com>
Date: Thu Sep 27 16:31:03 2012 +1000
bufio: Implement io.WriterTo for (*Reader)
This is part 1 of 2 for issue 4028
benchmark old ns/op new ns/op delta
BenchmarkReaderCopyOptimal 33495 9849 -70.60%
BenchmarkReaderCopyUnoptimal 70631 27041 -61.72%
BenchmarkReaderCopyOldImpl 51407 52970 +3.04%
Update #4028
R=dave, nigeltao, rsc, bradfitz, rogpeppe
CC=golang-dev
https://golang.org/cl/6548047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e289a2b913d476cf8592728e6d50767d0e29afde
元コミット内容
bufio
パッケージのReader
型にio.WriterTo
インターフェースを実装します。これは、Issue 4028の解決に向けた2つの変更のうちの1つ目です。
ベンチマーク結果は以下の通りです。
BenchmarkReaderCopyOptimal
: 33495 ns/op から 9849 ns/op へ、70.60%の改善BenchmarkReaderCopyUnoptimal
: 70631 ns/op から 27041 ns/op へ、61.72%の改善BenchmarkReaderCopyOldImpl
: 51407 ns/op から 52970 ns/op へ、3.04%の悪化(これは既存の実装のベンチマークであり、比較対象として残されている)
Issue #4028を更新します。
レビュー担当者: dave, nigeltao, rsc, bradfitz, rogpeppe CC: golang-dev 変更リスト: https://golang.org/cl/6548047
変更の背景
この変更の主な背景は、Go言語の標準ライブラリにおけるI/O操作の効率化、特にio.Copy
関数を用いたデータ転送のパフォーマンス改善です。コミットメッセージに明記されているように、この変更はIssue 4028に関連しています。
Issue 4028は、io.Copy
がio.Reader
とio.Writer
インターフェースを介してデータをコピーする際に、io.WriterTo
やio.ReaderFrom
といったより効率的なインターフェースが実装されている場合に、それらを活用できていないというパフォーマンス上の課題を指摘していました。
具体的には、io.Copy(dst, src)
が呼び出された際、Goの標準的なio.Copy
の実装は、まずsrc
がio.WriterTo
インターフェースを実装しているかを確認します。もし実装していれば、src.WriteTo(dst)
を呼び出すことで、src
自身が最も効率的な方法でdst
にデータを書き込むことができます。同様に、dst
がio.ReaderFrom
インターフェースを実装していれば、dst.ReadFrom(src)
を呼び出すことで、dst
自身がsrc
からデータを読み込むことができます。これらの最適化パスは、中間バッファリングを減らしたり、基盤となるシステムコールを最適化したりすることで、大幅なパフォーマンス向上をもたらします。
このコミットでは、bufio.Reader
がio.WriterTo
を実装することで、bufio.Reader
から他のio.Writer
へのコピー操作(特にio.Copy
経由)が、バッファリングされたデータを直接効率的に転送できるようになり、ベンチマーク結果が示すように顕著なパフォーマンス改善が達成されました。
前提知識の解説
Go言語のio
パッケージ
Go言語のio
パッケージは、プリミティブなI/O操作を提供する基本的なインターフェースを定義しています。
-
io.Reader
インターフェース:type Reader interface { Read(p []byte) (n int, err error) }
データを読み込むための基本的なインターフェースです。
Read
メソッドは、最大len(p)
バイトをp
に読み込み、読み込んだバイト数n
とエラーを返します。 -
io.Writer
インターフェース:type Writer interface { Write(p []byte) (n int, err error) }
データを書き込むための基本的なインターフェースです。
Write
メソッドは、p
からlen(p)
バイトを書き込み、書き込んだバイト数n
とエラーを返します。 -
io.WriterTo
インターフェース:type WriterTo interface { WriteTo(w Writer) (n int64, err error) }
このインターフェースは、自身が持っているデータを指定された
io.Writer
に直接書き込む能力を持つ型が実装します。io.Copy
関数は、ソースがio.WriterTo
を実装している場合、このメソッドを優先的に使用して効率的なコピーを行います。これにより、io.Copy
が内部でバッファを確保してRead
とWrite
を繰り返すよりも、実装側がより効率的な方法(例えば、内部バッファを直接転送する、または基盤となるシステムコールを最適化する)でデータを転送できるようになります。 -
io.ReaderFrom
インターフェース:type ReaderFrom interface { ReadFrom(r Reader) (n int64, err error) }
このインターフェースは、指定された
io.Reader
から直接データを読み込む能力を持つ型が実装します。io.Copy
関数は、デスティネーションがio.ReaderFrom
を実装している場合、このメソッドを優先的に使用します。
bufio
パッケージ
bufio
パッケージは、I/O操作をバッファリングすることで効率化するための機能を提供します。
bufio.Reader
:io.Reader
をラップし、内部にバッファを持つことで、少量のデータを頻繁に読み込む際に発生するシステムコールを減らし、パフォーマンスを向上させます。例えば、ファイルから1バイトずつ読み込む場合でも、bufio.Reader
は一度に大きなチャンクを読み込み、それを内部バッファに保持することで、実際のI/O操作の回数を減らします。
io.Copy
関数
io.Copy(dst Writer, src Reader) (written int64, err error)
は、src
からdst
へデータをコピーするためのGo言語の標準関数です。この関数は、内部的に以下のような最適化ロジックを持っています。
src
がio.WriterTo
を実装している場合、src.WriteTo(dst)
を呼び出します。dst
がio.ReaderFrom
を実装している場合、dst.ReadFrom(src)
を呼び出します。- 上記いずれでもない場合、内部で一時的なバッファ(通常は32KB)を確保し、
src.Read
とdst.Write
を繰り返し呼び出してデータを転送します。
このコミットは、bufio.Reader
がio.WriterTo
を実装することで、io.Copy
が上記1の最適化パスを利用できるようになることを目的としています。
技術的詳細
このコミットでは、bufio.Reader
型にWriteTo
メソッドを追加し、io.WriterTo
インターフェースを実装しています。
WriteTo
メソッドのロジックは以下の通りです。
- 内部バッファの書き出し: まず、
bufio.Reader
が現在保持している内部バッファ(b.buf[b.r:b.w]
)の内容を、ヘルパー関数writeBuf
を使って引数w
(io.Writer
)に書き出します。これにより、既に読み込まれてバッファにあるデータが効率的に転送されます。 - 基盤となるリーダーの最適化パス:
bufio.Reader
がラップしている基盤となるio.Reader
(b.rd
)が、もし自身もio.WriterTo
インターフェースを実装している場合、そのWriteTo
メソッドを直接呼び出します。これは、基盤となるリーダーが自身のデータを最も効率的にw
に書き出すことができるため、さらなる最適化を可能にします。例えば、ファイルディスクリプタを直接転送するような最適化(sendfile
システムコールなど)が可能な場合があります。 - 残りのデータの転送: 上記の最適化パスが利用できない場合、または基盤となるリーダーが
io.WriterTo
を実装していない場合、bufio.Reader
はループを使って残りのデータを転送します。b.fill()
を呼び出して、内部バッファを可能な限り埋めます。- バッファにデータがある限り(
b.r < b.w
)、writeBuf
を使ってそのデータをw
に書き出します。 - このループは、基盤となるリーダーからの読み込みが完了するか、エラーが発生するまで続きます。
- EOFの処理: 読み込み中に
io.EOF
エラーが発生した場合、それは正常な終了を示すため、b.err
をnil
にリセットします。 - エラーの返却: 最終的に、書き込んだバイト数
n
と、発生したエラー(もしあれば)を返します。
ヘルパー関数writeBuf(w io.Writer) (int64, error)
は、bufio.Reader
の現在のバッファ内容をw
に書き込み、書き込んだバイト数とエラーを返します。書き込み後、バッファの読み込みポインタb.r
を更新します。
テストコードでは、TestReaderWriteTo
で正常系の動作を確認し、TestReaderWriteToErrors
でエラーハンドリングを検証しています。
さらに、ベンチマークテストが追加されており、io.WriterTo
の実装がio.Copy
のパフォーマンスに与える影響を明確に示しています。
BenchmarkReaderCopyOptimal
: 基盤となるリーダーがio.WriterTo
を実装している場合のio.Copy
のパフォーマンスを測定します。このケースでは、bufio.Reader
のWriteTo
が基盤のWriteTo
を呼び出すため、非常に効率的です。BenchmarkReaderCopyUnoptimal
: 基盤となるリーダーがio.WriterTo
を実装していない場合のio.Copy
のパフォーマンスを測定します。この場合、bufio.Reader
は内部バッファリングとループによる転送を行います。BenchmarkReaderCopyNoWriteTo
:bufio.Reader
がio.WriterTo
を実装していない場合の、従来のio.Copy
の動作をシミュレートします。これは比較のために残されています。
ベンチマーク結果は、io.WriterTo
の実装がio.Copy
のパフォーマンスを劇的に改善することを示しており、特に最適なケースでは70%以上の高速化が達成されています。
コアとなるコードの変更箇所
src/pkg/bufio/bufio.go
ファイルに以下の変更が加えられています。
Reader
型にWriteTo
メソッドが追加されました。writeBuf
というヘルパー関数が追加されました。
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -375,6 +375,41 @@ func (b *Reader) ReadString(delim byte) (line string, err error) {
return string(bytes), e
}
+// WriteTo implements io.WriterTo.
+func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
+ n, err = b.writeBuf(w)
+ if err != nil {
+ return
+ }
+
+ if r, ok := b.rd.(io.WriterTo); ok {
+ m, err := r.WriteTo(w)
+ n += m
+ return n, err
+ }
+
+ for b.fill(); b.r < b.w; b.fill() {
+ m, err := b.writeBuf(w)
+ n += m
+ if err != nil {
+ return n, err
+ }
+ }
+
+ if b.err == io.EOF {
+ b.err = nil
+ }
+
+ return n, b.readErr()
+}
+
+// writeBuf writes the Reader's buffer to the writer.
+func (b *Reader) writeBuf(w io.Writer) (int64, error) {
+ n, err := w.Write(b.buf[b.r:b.w])
+ b.r += n
+ return int64(n), err
+}
+
// buffered output
// Writer implements buffering for an io.Writer object.
src/pkg/bufio/bufio_test.go
ファイルには、WriteTo
メソッドのテストケースとベンチマークが追加されています。
コアとなるコードの解説
func (b *Reader) WriteTo(w io.Writer) (n int64, err error)
このメソッドはio.WriterTo
インターフェースの実装です。bufio.Reader
が保持するデータを、引数で渡されたio.Writer
w
に書き込みます。
-
n, err = b.writeBuf(w)
: まず、bufio.Reader
の内部バッファに現在残っているデータをw
に書き出します。これにより、既にバッファリングされているデータが即座に転送されます。エラーが発生した場合は、そのエラーを返して処理を終了します。 -
if r, ok := b.rd.(io.WriterTo); ok
:bufio.Reader
がラップしている基盤となるio.Reader
(b.rd
)が、もしio.WriterTo
インターフェースを実装しているかどうかを型アサーションで確認します。- もし実装していれば、
r.WriteTo(w)
を直接呼び出します。これは、基盤となるリーダーが自身のデータをw
に書き出すための最も効率的な方法を知っている可能性があるため、非常に重要な最適化パスです。例えば、ファイルディスクリプタ間の直接転送(sendfile
)などが利用できる場合があります。 - 基盤の
WriteTo
が成功した場合、その結果(書き込んだバイト数m
とエラーerr
)を累積し、処理を終了します。
- もし実装していれば、
-
for b.fill(); b.r < b.w; b.fill()
: 基盤となるリーダーがio.WriterTo
を実装していない場合、またはその呼び出しが完了した後、このループが実行されます。b.fill()
:bufio.Reader
の内部バッファを、基盤となるリーダーから可能な限り多くのデータで満たします。b.r < b.w
: バッファにまだ読み込まれていないデータが残っている間、ループを続けます。m, err := b.writeBuf(w)
: バッファ内のデータをw
に書き出します。n += m
: 書き込んだバイト数を累積します。if err != nil { return n, err }
: 書き込み中にエラーが発生した場合、処理を終了します。
-
if b.err == io.EOF { b.err = nil }
: ループが終了した後、bufio.Reader
の内部エラー状態b.err
がio.EOF
であれば、それをnil
にリセットします。これは、io.EOF
が正常なストリームの終了を示すためです。 -
return n, b.readErr()
: 最終的に、これまでに書き込んだ合計バイト数n
と、bufio.Reader
が保持している読み込み関連のエラー(b.readErr()
はb.err
を返すヘルパー)を返します。
func (b *Reader) writeBuf(w io.Writer) (int64, error)
このヘルパー関数は、bufio.Reader
の内部バッファ(b.buf
)のうち、まだ読み出されていない部分(b.buf[b.r:b.w]
)を、指定されたio.Writer
w
に書き込みます。
-
n, err := w.Write(b.buf[b.r:b.w])
: バッファの有効な部分をw.Write
に渡して書き込みます。n
は実際に書き込まれたバイト数です。 -
b.r += n
: 書き込みが成功したバイト数だけ、bufio.Reader
の読み込みポインタb.r
を進めます。これにより、次にwriteBuf
が呼び出されたときに、既に書き込まれたデータが再度処理されるのを防ぎます。 -
return int64(n), err
: 書き込んだバイト数と、w.Write
から返されたエラーを返します。
この実装により、bufio.Reader
はio.Copy
などの関数と連携する際に、より効率的なデータ転送パスを提供できるようになり、特に大量のデータを扱う場合のパフォーマンスが向上します。
関連リンク
- Go Issue 4028:
io.Copy
should useWriterTo
andReaderFrom
if available https://github.com/golang/go/issues/4028 - Go CL 6548047:
bufio
: Implementio.WriterTo
for(*Reader)
https://golang.org/cl/6548047 - Go
io
package documentation: https://pkg.go.dev/io - Go
bufio
package documentation: https://pkg.go.dev/bufio
参考にした情報源リンク
- Go言語の公式ドキュメント (
io
パッケージ、bufio
パッケージ) - Go言語のIssueトラッカー (Issue 4028)
- Go言語の変更リスト (CL 6548047)
- Go言語の
io.Copy
関数の実装に関する一般的な知識 - Go言語のベンチマークテストの読み方に関する一般的な知識
- Go言語のインターフェースと型アサーションに関する一般的な知識
sendfile
システムコールに関する一般的な知識 (基盤となるio.WriterTo
の実装が利用する可能性のある最適化として)
[インデックス 13975] ファイルの概要
このコミットは、Go言語の標準ライブラリであるbufio
パッケージのReader
型にio.WriterTo
インターフェースの実装を追加するものです。これにより、bufio.Reader
から直接データをio.Writer
に効率的に書き出すことが可能になり、特にio.Copy
関数を使用した場合のパフォーマンスが大幅に向上します。
コミット
commit e289a2b913d476cf8592728e6d50767d0e29afde
Author: Michael Chaten <mchaten@gmail.com>
Date: Thu Sep 27 16:31:03 2012 +1000
bufio: Implement io.WriterTo for (*Reader)
This is part 1 of 2 for issue 4028
benchmark old ns/op new ns/op delta
BenchmarkReaderCopyOptimal 33495 9849 -70.60%
BenchmarkReaderCopyUnoptimal 70631 27041 -61.72%
BenchmarkReaderCopyOldImpl 51407 52970 +3.04%
Update #4028
R=dave, nigeltao, rsc, bradfitz, rogpeppe
CC=golang-dev
https://golang.org/cl/6548047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e289a2b913d476cf8592728e6d50767d0e29afde
元コミット内容
bufio
パッケージのReader
型にio.WriterTo
インターフェースを実装します。これは、Issue 4028の解決に向けた2つの変更のうちの1つ目です。
ベンチマーク結果は以下の通りです。
BenchmarkReaderCopyOptimal
: 33495 ns/op から 9849 ns/op へ、70.60%の改善BenchmarkReaderCopyUnoptimal
: 70631 ns/op から 27041 ns/op へ、61.72%の改善BenchmarkReaderCopyOldImpl
: 51407 ns/op から 52970 ns/op へ、3.04%の悪化(これは既存の実装のベンチマークであり、比較対象として残されている)
Issue #4028を更新します。
レビュー担当者: dave, nigeltao, rsc, bradfitz, rogpeppe CC: golang-dev 変更リスト: https://golang.org/cl/6548047
変更の背景
この変更の主な背景は、Go言語の標準ライブラリにおけるI/O操作の効率化、特にio.Copy
関数を用いたデータ転送のパフォーマンス改善です。コミットメッセージに明記されているように、この変更はIssue 4028に関連しています。
Issue 4028は、io.Copy
がio.Reader
とio.Writer
インターフェースを介してデータをコピーする際に、io.WriterTo
やio.ReaderFrom
といったより効率的なインターフェースが実装されている場合に、それらを活用できていないというパフォーマンス上の課題を指摘していました。
具体的には、io.Copy(dst, src)
が呼び出された際、Goの標準的なio.Copy
の実装は、まずsrc
がio.WriterTo
インターフェースを実装しているかを確認します。もし実装していれば、src.WriteTo(dst)
を呼び出すことで、src
自身が最も効率的な方法でdst
にデータを書き込むことができます。同様に、dst
がio.ReaderFrom
インターフェースを実装していれば、dst.ReadFrom(src)
を呼び出すことで、dst
自身がsrc
からデータを読み込むことができます。これらの最適化パスは、中間バッファリングを減らしたり、基盤となるシステムコールを最適化したりすることで、大幅なパフォーマンス向上をもたらします。
このコミットでは、bufio.Reader
がio.WriterTo
を実装することで、bufio.Reader
から他のio.Writer
へのコピー操作(特にio.Copy
経由)が、バッファリングされたデータを直接効率的に転送できるようになり、ベンチマーク結果が示すように顕著なパフォーマンス改善が達成されました。
前提知識の解説
Go言語のio
パッケージ
Go言語のio
パッケージは、プリミティブなI/O操作を提供する基本的なインターフェースを定義しています。
-
io.Reader
インターフェース:type Reader interface { Read(p []byte) (n int, err error) }
データを読み込むための基本的なインターフェースです。
Read
メソッドは、最大len(p)
バイトをp
に読み込み、読み込んだバイト数n
とエラーを返します。 -
io.Writer
インターフェース:type Writer interface { Write(p []byte) (n int, err error) }
データを書き込むための基本的なインターフェースです。
Write
メソッドは、p
からlen(p)
バイトを書き込み、書き込んだバイト数n
とエラーを返します。 -
io.WriterTo
インターフェース:type WriterTo interface { WriteTo(w Writer) (n int64, err error) }
このインターフェースは、自身が持っているデータを指定された
io.Writer
に直接書き込む能力を持つ型が実装します。io.Copy
関数は、ソースがio.WriterTo
を実装している場合、このメソッドを優先的に使用して効率的なコピーを行います。これにより、io.Copy
が内部でバッファを確保してRead
とWrite
を繰り返すよりも、実装側がより効率的な方法(例えば、内部バッファを直接転送する、または基盤となるシステムコールを最適化する)でデータを転送できるようになります。 -
io.ReaderFrom
インターフェース:type ReaderFrom interface { ReadFrom(r Reader) (n int64, err error) }
このインターフェースは、指定された
io.Reader
から直接データを読み込む能力を持つ型が実装します。io.Copy
関数は、デスティネーションがio.ReaderFrom
を実装している場合、このメソッドを優先的に使用します。
bufio
パッケージ
bufio
パッケージは、I/O操作をバッファリングすることで効率化するための機能を提供します。
bufio.Reader
:io.Reader
をラップし、内部にバッファを持つことで、少量のデータを頻繁に読み込む際に発生するシステムコールを減らし、パフォーマンスを向上させます。例えば、ファイルから1バイトずつ読み込む場合でも、bufio.Reader
は一度に大きなチャンクを読み込み、それを内部バッファに保持することで、実際のI/O操作の回数を減らします。
io.Copy
関数
io.Copy(dst Writer, src Reader) (written int64, err error)
は、src
からdst
へデータをコピーするためのGo言語の標準関数です。この関数は、内部的に以下のような最適化ロジックを持っています。
src
がio.WriterTo
を実装している場合、src.WriteTo(dst)
を呼び出します。dst
がio.ReaderFrom
を実装している場合、dst.ReadFrom(src)
を呼び出します。- 上記いずれでもない場合、内部で一時的なバッファ(通常は32KB)を確保し、
src.Read
とdst.Write
を繰り返し呼び出してデータを転送します。
このコミットは、bufio.Reader
がio.WriterTo
を実装することで、io.Copy
が上記1の最適化パスを利用できるようになることを目的としています。
技術的詳細
このコミットでは、bufio.Reader
型にWriteTo
メソッドを追加し、io.WriterTo
インターフェースを実装しています。
WriteTo
メソッドのロジックは以下の通りです。
- 内部バッファの書き出し: まず、
bufio.Reader
が現在保持している内部バッファ(b.buf[b.r:b.w]
)の内容を、ヘルパー関数writeBuf
を使って引数w
(io.Writer
)に書き出します。これにより、既に読み込まれてバッファにあるデータが効率的に転送されます。 - 基盤となるリーダーの最適化パス:
bufio.Reader
がラップしている基盤となるio.Reader
(b.rd
)が、もし自身もio.WriterTo
インターフェースを実装している場合、そのWriteTo
メソッドを直接呼び出します。これは、基盤となるリーダーが自身のデータを最も効率的にw
に書き出すことができるため、さらなる最適化を可能にします。例えば、ファイルディスクリプタを直接転送するような最適化(sendfile
システムコールなど)が可能な場合があります。 - 残りのデータの転送: 上記の最適化パスが利用できない場合、または基盤となるリーダーが
io.WriterTo
を実装していない場合、bufio.Reader
はループを使って残りのデータを転送します。b.fill()
を呼び出して、内部バッファを可能な限り埋めます。- バッファにデータがある限り(
b.r < b.w
)、writeBuf
を使ってそのデータをw
に書き出します。 - このループは、基盤となるリーダーからの読み込みが完了するか、エラーが発生するまで続きます。
- EOFの処理: 読み込み中に
io.EOF
エラーが発生した場合、それは正常な終了を示すため、b.err
をnil
にリセットします。 - エラーの返却: 最終的に、書き込んだバイト数
n
と、発生したエラー(もしあれば)を返します。
ヘルパー関数writeBuf(w io.Writer) (int64, error)
は、bufio.Reader
の現在のバッファ内容をw
に書き込み、書き込んだバイト数とエラーを返します。書き込み後、バッファの読み込みポインタb.r
を更新します。
テストコードでは、TestReaderWriteTo
で正常系の動作を確認し、TestReaderWriteToErrors
でエラーハンドリングを検証しています。
さらに、ベンチマークテストが追加されており、io.WriterTo
の実装がio.Copy
のパフォーマンスに与える影響を明確に示しています。
BenchmarkReaderCopyOptimal
: 基盤となるリーダーがio.WriterTo
を実装している場合のio.Copy
のパフォーマンスを測定します。このケースでは、bufio.Reader
のWriteTo
が基盤のWriteTo
を呼び出すため、非常に効率的です。BenchmarkReaderCopyUnoptimal
: 基盤となるリーダーがio.WriterTo
を実装していない場合のio.Copy
のパフォーマンスを測定します。この場合、bufio.Reader
は内部バッファリングとループによる転送を行います。BenchmarkReaderCopyNoWriteTo
:bufio.Reader
がio.WriterTo
を実装していない場合の、従来のio.Copy
の動作をシミュレートします。これは比較のために残されています。
ベンチマーク結果は、io.WriterTo
の実装がio.Copy
のパフォーマンスを劇的に改善することを示しており、特に最適なケースでは70%以上の高速化が達成されています。
コアとなるコードの変更箇所
src/pkg/bufio/bufio.go
ファイルに以下の変更が加えられています。
Reader
型にWriteTo
メソッドが追加されました。writeBuf
というヘルパー関数が追加されました。
--- a/src/pkg/bufio/bufio.go
+++ b/src/pkg/bufio/bufio.go
@@ -375,6 +375,41 @@ func (b *Reader) ReadString(delim byte) (line string, err error) {
return string(bytes), e
}
+// WriteTo implements io.WriterTo.
+func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
+ n, err = b.writeBuf(w)
+ if err != nil {
+ return
+ }
+
+ if r, ok := b.rd.(io.WriterTo); ok {
+ m, err := r.WriteTo(w)
+ n += m
+ return n, err
+ }
+
+ for b.fill(); b.r < b.w; b.fill() {
+ m, err := b.writeBuf(w)
+ n += m
+ if err != nil {
+ return n, err
+ }
+ }
+
+ if b.err == io.EOF {
+ b.err = nil
+ }
+
+ return n, b.readErr()
+}
+
+// writeBuf writes the Reader's buffer to the writer.
+func (b *Reader) writeBuf(w io.Writer) (int64, error) {
+ n, err := w.Write(b.buf[b.r:b.w])
+ b.r += n
+ return int64(n), err
+}
+
// buffered output
// Writer implements buffering for an io.Writer object.
src/pkg/bufio/bufio_test.go
ファイルには、WriteTo
メソッドのテストケースとベンチマークが追加されています。
コアとなるコードの解説
func (b *Reader) WriteTo(w io.Writer) (n int64, err error)
このメソッドはio.WriterTo
インターフェースの実装です。bufio.Reader
が保持するデータを、引数で渡されたio.Writer
w
に書き込みます。
-
n, err = b.writeBuf(w)
: まず、bufio.Reader
の内部バッファに現在残っているデータをw
に書き出します。これにより、既にバッファリングされているデータが即座に転送されます。エラーが発生した場合は、そのエラーを返して処理を終了します。 -
if r, ok := b.rd.(io.WriterTo); ok
:bufio.Reader
がラップしている基盤となるio.Reader
(b.rd
)が、もしio.WriterTo
インターフェースを実装しているかどうかを型アサーションで確認します。- もし実装していれば、
r.WriteTo(w)
を直接呼び出します。これは、基盤となるリーダーが自身のデータをw
に書き出すための最も効率的な方法を知っている可能性があるため、非常に重要な最適化パスです。例えば、ファイルディスクリプタ間の直接転送(sendfile
)などが利用できる場合があります。 - 基盤の
WriteTo
が成功した場合、その結果(書き込んだバイト数m
とエラーerr
)を累積し、処理を終了します。
- もし実装していれば、
-
for b.fill(); b.r < b.w; b.fill()
: 基盤となるリーダーがio.WriterTo
を実装していない場合、またはその呼び出しが完了した後、このループが実行されます。b.fill()
:bufio.Reader
の内部バッファを、基盤となるリーダーから可能な限り多くのデータで満たします。b.r < b.w
: バッファにまだ読み込まれていないデータが残っている間、ループを続けます。m, err := b.writeBuf(w)
: バッファ内のデータをw
に書き出します。n += m
: 書き込んだバイト数を累積します。if err != nil { return n, err }
: 書き込み中にエラーが発生した場合、処理を終了します。
-
if b.err == io.EOF { b.err = nil }
: ループが終了した後、bufio.Reader
の内部エラー状態b.err
がio.EOF
であれば、それをnil
にリセットします。これは、io.EOF
が正常なストリームの終了を示すためです。 -
return n, b.readErr()
: 最終的に、これまでに書き込んだ合計バイト数n
と、bufio.Reader
が保持している読み込み関連のエラー(b.readErr()
はb.err
を返すヘルパー)を返します。
func (b *Reader) writeBuf(w io.Writer) (int64, error)
このヘルパー関数は、bufio.Reader
の内部バッファ(b.buf
)のうち、まだ読み出されていない部分(b.buf[b.r:b.w]
)を、指定されたio.Writer
w
に書き込みます。
-
n, err := w.Write(b.buf[b.r:b.w])
: バッファの有効な部分をw.Write
に渡して書き込みます。n
は実際に書き込まれたバイト数です。 -
b.r += n
: 書き込みが成功したバイト数だけ、bufio.Reader
の読み込みポインタb.r
を進めます。これにより、次にwriteBuf
が呼び出されたときに、既に書き込まれたデータが再度処理されるのを防ぎます。 -
return int64(n), err
: 書き込んだバイト数と、w.Write
から返されたエラーを返します。
この実装により、bufio.Reader
はio.Copy
などの関数と連携する際に、より効率的なデータ転送パスを提供できるようになり、特に大量のデータを扱う場合のパフォーマンスが向上します。
関連リンク
- Go Issue 4028:
io.Copy
should useWriterTo
andReaderFrom
if available https://github.com/golang/go/issues/4028 - Go CL 6548047:
bufio
: Implementio.WriterTo
for(*Reader)
https://golang.org/cl/6548047 - Go
io
package documentation: https://pkg.go.dev/io - Go
bufio
package documentation: https://pkg.go.dev/bufio
参考にした情報源リンク
- Go言語の公式ドキュメント (
io
パッケージ、bufio
パッケージ) - Go言語のIssueトラッカー (Issue 4028)
- Go言語の変更リスト (CL 6548047)
- Go言語の
io.Copy
関数の実装に関する一般的な知識 - Go言語のベンチマークテストの読み方に関する一般的な知識
- Go言語のインターフェースと型アサーションに関する一般的な知識
sendfile
システムコールに関する一般的な知識 (基盤となるio.WriterTo
の実装が利用する可能性のある最適化として)