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

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

このコミットは、Go言語の標準ライブラリ io/ioutil パッケージ内の Discard (実体は devNull 型) に WriteString メソッドを追加するものです。これにより、文字列を Discard に書き込む際に、バイトスライスへの変換オーバーヘッドなしに効率的に破棄できるようになります。

コミット

commit ecf3274143a512153b4b85b77a06aacd620fcfa9
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Fri Aug 9 11:27:29 2013 -0700

    io/ioutil: add WriteString to Discard
    
    R=golang-dev, iant
    CC=golang-dev
    https://golang.org/cl/12580045

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

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

元コミット内容

io/ioutil: add WriteString to Discard

このコミットは、io/ioutil パッケージの Discard オブジェクトに WriteString メソッドを追加します。

変更の背景

Go言語の io パッケージには、データを書き込むための基本的なインターフェースである io.Writer が定義されています。io.WriterWrite([]byte) (n int, err error) メソッドを持ち、バイトスライスを受け取って書き込みます。

io/ioutil パッケージの Discard は、書き込まれたデータをすべて破棄する io.Writer の実装です。これは、例えば、関数の戻り値として io.Writer が期待されるが、実際には出力が不要な場合などに使用されます。

しかし、文字列を io.Writer に書き込む場合、通常は []byte(s) のように文字列をバイトスライスに変換してから Write メソッドを呼び出す必要があります。この変換は、特に頻繁に大きな文字列を書き込む場合に、メモリ割り当てとコピーのオーバーヘッドを発生させます。

このコミットの背景には、このような文字列からバイトスライスへの不要な変換を避けることで、Discard への文字列書き込みのパフォーマンスを向上させるという目的があります。Go 1.1で導入された io.StringWriter インターフェースは、この問題を解決するために設計されました。io.StringWriterWriteString(s string) (n int, err error) メソッドを定義しており、これを実装する型は文字列を直接受け取って処理できます。Discardio.StringWriter を実装することで、文字列を破棄する際に効率的なパスが提供されます。

前提知識の解説

  • io.Writer インターフェース: Go言語における基本的な出力インターフェースです。Write([]byte) (n int, err error) メソッドを定義しており、バイトスライスを受け取ってデータを書き込みます。ファイル、ネットワーク接続、標準出力など、様々な出力先にデータを書き込む際に利用されます。

  • io.StringWriter インターフェース: Go 1.1で導入されたインターフェースで、WriteString(s string) (n int, err error) メソッドを定義しています。このインターフェースを実装する型は、文字列を直接受け取って処理できることを示します。これにより、文字列をバイトスライスに変換する中間ステップを省略し、パフォーマンスを向上させることができます。例えば、fmt.Fprintio.Copy のような関数は、内部的に io.StringWriter の実装をチェックし、利用可能であれば WriteString を呼び出すことで最適化を図ります。

  • io/ioutil.Discard: io/ioutil パッケージが提供する変数で、io.Writer インターフェースを実装しています。この Writer に書き込まれたデータはすべて破棄されます。Unix系の /dev/null に似た機能を提供するため、内部的には devNull という構造体が使われています。主に、関数の戻り値として io.Writer が必要だが、実際には出力が不要な場合(例: ベンチマークで出力を無視したい場合、ログを一時的に無効にしたい場合など)に利用されます。

  • devNull 構造体: io/ioutil.Discard の実体となる、エクスポートされていない(小文字で始まる)構造体です。この構造体が io.Writer インターフェースの Write メソッドを実装しており、今回のコミットで WriteString メソッドも追加されました。

技術的詳細

このコミットは、io/ioutil パッケージの devNull 型に WriteString メソッドを追加することで、Discardio.StringWriter インターフェースを満たすようにします。

変更前は、Discard に文字列を書き込む場合、Discard.Write([]byte("some string")) のように、文字列をバイトスライスに変換する必要がありました。この変換は、文字列のコピーを伴うため、特に大きな文字列や頻繁な書き込みにおいては、不要なメモリ割り当てとCPUサイクルを消費します。

変更後は、DiscardWriteString(s string) (int, error) メソッドを実装するため、Discard.WriteString("some string") のように直接文字列を渡すことができるようになります。devNullWriteString メソッドは、受け取った文字列の長さをそのまま返し、エラーは発生させません。これは、Discard の目的(データを破棄すること)に合致しており、実際にデータを書き込む必要がないため、文字列のバイトスライス変換や実際の書き込み処理を完全にスキップできます。

これにより、fmt.Fprint(ioutil.Discard, "some string") のような操作が、内部的に io.StringWriter の最適化パスを利用するようになり、より効率的に実行されるようになります。これは、Goの標準ライブラリが提供するインターフェースの柔軟性と、それを利用したパフォーマンス最適化の一例です。

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

--- a/src/pkg/io/ioutil/ioutil.go
+++ b/src/pkg/io/ioutil/ioutil.go
@@ -132,6 +132,10 @@ func (devNull) Write(p []byte) (int, error) {
 	return len(p), nil
 }
 
+func (devNull) WriteString(s string) (int, error) {
+	return len(s), nil
+}
+
 func (devNull) ReadFrom(r io.Reader) (n int64, err error) {
 	buf := blackHole()
 	defer blackHolePut(buf)

コアとなるコードの解説

追加されたコードは以下の4行です。

func (devNull) WriteString(s string) (int, error) {
	return len(s), nil
}
  • func (devNull) WriteString(s string) (int, error): これは devNull 型に WriteString メソッドを追加するものです。s string は書き込まれる文字列、int は書き込まれたバイト数(この場合は文字列の長さ)、error は発生したエラーを返します。

  • return len(s), nil: この行がメソッドの本体です。Discard の性質上、実際に文字列をどこかに書き込む必要はありません。そのため、単に受け取った文字列 s の長さ (len(s)) を返し、エラーは発生しない (nil) ことを示しています。これにより、io.StringWriter インターフェースの要件を満たしつつ、最も効率的な方法で文字列の破棄を実現しています。

このシンプルな追加により、io/ioutil.Discardio.StringWriter インターフェースを暗黙的に実装することになり、文字列を破棄する際のパフォーマンスが向上します。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語の標準ライブラリ `io/ioutil` パッケージ内の `Discard` (実体は `devNull` 型) に `WriteString` メソッドを追加するものです。これにより、文字列を `Discard` に書き込む際に、バイトスライスへの変換オーバーヘッドなしに効率的に破棄できるようになります。

## コミット

commit ecf3274143a512153b4b85b77a06aacd620fcfa9 Author: Brad Fitzpatrick bradfitz@golang.org Date: Fri Aug 9 11:27:29 2013 -0700

io/ioutil: add WriteString to Discard

R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/12580045

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

[https://github.com/golang/go/commit/ecf3274143a512153b4b85b77a06aacd620fcfa9](https://github.com/golang/go/commit/ecf3274143a512153b4b85b77a06aacd620fcfa9)

## 元コミット内容

`io/ioutil: add WriteString to Discard`

このコミットは、`io/ioutil` パッケージの `Discard` オブジェクトに `WriteString` メソッドを追加します。

## 変更の背景

Go言語の `io` パッケージには、データを書き込むための基本的なインターフェースである `io.Writer` が定義されています。`io.Writer` は `Write([]byte) (n int, err error)` メソッドを持ち、バイトスライスを受け取って書き込みます。

`io/ioutil` パッケージの `Discard` は、書き込まれたデータをすべて破棄する `io.Writer` の実装です。これは、例えば、関数の戻り値として `io.Writer` が期待されるが、実際には出力が不要な場合などに使用されます。

しかし、文字列を `io.Writer` に書き込む場合、通常は `[]byte(s)` のように文字列をバイトスライスに変換してから `Write` メソッドを呼び出す必要があります。この変換は、特に頻繁に大きな文字列を書き込む場合に、メモリ割り当てとコピーのオーバーヘッドを発生させます。

このコミットの背景には、このような文字列からバイトスライスへの不要な変換を避けることで、`Discard` への文字列書き込みのパフォーマンスを向上させるという目的があります。Go 1.1で導入された `io.StringWriter` インターフェースは、この問題を解決するために設計されました。`io.StringWriter` は `WriteString(s string) (n int, err error)` メソッドを定義しており、これを実装する型は文字列を直接受け取って処理できます。`Discard` が `io.StringWriter` を実装することで、文字列を破棄する際に効率的なパスが提供されます。

## 前提知識の解説

*   **`io.Writer` インターフェース**:
    Go言語における基本的な出力インターフェースです。`Write([]byte) (n int, err error)` メソッドを定義しており、バイトスライスを受け取ってデータを書き込みます。ファイル、ネットワーク接続、標準出力など、様々な出力先にデータを書き込む際に利用されます。

*   **`io.StringWriter` インターフェース**:
    Go 1.1で導入されたインターフェースで、`WriteString(s string) (n int, err error)` メソッドを定義しています。このインターフェースを実装する型は、文字列を直接受け取って処理できることを示します。これにより、文字列をバイトスライスに変換する中間ステップを省略し、パフォーマンスを向上させることができます。例えば、`fmt.Fprint` や `io.Copy` のような関数は、内部的に `io.StringWriter` の実装をチェックし、利用可能であれば `WriteString` を呼び出すことで最適化を図ります。

*   **`io/ioutil.Discard`**:
    `io/ioutil` パッケージが提供する変数で、`io.Writer` インターフェースを実装しています。この `Writer` に書き込まれたデータはすべて破棄されます。Unix系の `/dev/null` に似た機能を提供するため、内部的には `devNull` という構造体が使われています。主に、関数の戻り値として `io.Writer` が必要だが、実際には出力が不要な場合(例: ベンチマークで出力を無視したい場合、ログを一時的に無効にしたい場合など)に利用されます。

*   **`devNull` 構造体**:
    `io/ioutil.Discard` の実体となる、エクスポートされていない(小文字で始まる)構造体です。この構造体が `io.Writer` インターフェースの `Write` メソッドを実装しており、今回のコミットで `WriteString` メソッドも追加されました。

## 技術的詳細

このコミットは、`io/ioutil` パッケージの `devNull` 型に `WriteString` メソッドを追加することで、`Discard` が `io.StringWriter` インターフェースを満たすようにします。

変更前は、`Discard` に文字列を書き込む場合、`Discard.Write([]byte("some string"))` のように、文字列をバイトスライスに変換する必要がありました。この変換は、文字列のコピーを伴うため、特に大きな文字列や頻繁な書き込みにおいては、不要なメモリ割り当てとCPUサイクルを消費します。

変更後は、`Discard` が `WriteString(s string) (int, error)` メソッドを実装するため、`Discard.WriteString("some string")` のように直接文字列を渡すことができるようになります。`devNull` の `WriteString` メソッドは、受け取った文字列の長さをそのまま返し、エラーは発生させません。これは、`Discard` の目的(データを破棄すること)に合致しており、実際にデータを書き込む必要がないため、文字列のバイトスライス変換や実際の書き込み処理を完全にスキップできます。

これにより、`fmt.Fprint(ioutil.Discard, "some string")` のような操作が、内部的に `io.StringWriter` の最適化パスを利用するようになり、より効率的に実行されるようになります。これは、Goの標準ライブラリが提供するインターフェースの柔軟性と、それを利用したパフォーマンス最適化の一例です。

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

```diff
--- a/src/pkg/io/ioutil/ioutil.go
+++ b/src/pkg/io/ioutil/ioutil.go
@@ -132,6 +132,10 @@ func (devNull) Write(p []byte) (int, error) {
 	return len(p), nil
 }
 
+func (devNull) WriteString(s string) (int, error) {
+	return len(s), nil
+}
+
 func (devNull) ReadFrom(r io.Reader) (n int64, err error) {
 	buf := blackHole()
 	defer blackHolePut(buf)

コアとなるコードの解説

追加されたコードは以下の4行です。

func (devNull) WriteString(s string) (int, error) {
	return len(s), nil
}
  • func (devNull) WriteString(s string) (int, error): これは devNull 型に WriteString メソッドを追加するものです。s string は書き込まれる文字列、int は書き込まれたバイト数(この場合は文字列の長さ)、error は発生したエラーを返します。

  • return len(s), nil: この行がメソッドの本体です。Discard の性質上、実際に文字列をどこかに書き込む必要はありません。そのため、単に受け取った文字列 s の長さ (len(s)) を返し、エラーは発生しない (nil) ことを示しています。これにより、io.StringWriter インターフェースの要件を満たしつつ、最も効率的な方法で文字列の破棄を実現しています。

このシンプルな追加により、io/ioutil.Discardio.StringWriter インターフェースを暗黙的に実装することになり、文字列を破棄する際のパフォーマンスが向上します。

関連リンク

参考にした情報源リンク