[インデックス 10048] Go言語fmtパッケージの内部状態最適化
コミット
コミットハッシュ: 811d334a65fe7a8fce9680ed5e41878027d00bbc
作成者: Rob Pike r@golang.org
日付: 2011年10月18日 16:23:07 (UTC-7)
コミットメッセージ:
fmt: clean up after reflect.Interface change.
Store the reflect.Value in the internal print state. Code is simpler, cleaner,
and a little faster - back to what it was before the change.
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/811d334a65fe7a8fce9680ed5e41878027d00bbc
元コミット内容
このコミットは、Go言語の fmt
パッケージにおける reflect.Interface
の変更後のクリーンアップを行う重要な最適化です。具体的には、pp
構造体(fmt パッケージの内部プリント状態)に reflect.Value
フィールドを追加し、複数の書式設定関数のシグネチャを簡素化しました。
変更されたファイル:
src/pkg/fmt/print.go
: 128行の変更(68行追加、60行削除)
変更の背景
2011年当時、Go言語の reflect
パッケージは大幅な変更を受けていました。特に reflect.Interface
メソッドの変更により、fmt パッケージの内部実装が複雑化していました。この変更は、以下の問題を解決するために行われました:
- 冗長な引数の削減: 多くの書式設定関数が
reflect.Value
を個別に渡していた - 性能向上: 不要な
reflect.Value
の生成と受け渡しを削減 - コードの簡素化: 複雑な関数シグネチャを単純化
前提知識の解説
Go言語の fmt パッケージ
Go言語の fmt
パッケージは、C言語の printf
ファミリーに類似した書式設定機能を提供します。内部的には以下の構造を持ちます:
- pp構造体: プリント処理の状態を保持する内部構造体
- reflect.Value: 実行時の型情報と値を保持するGo言語の反射システム
- format verbの処理:
%d
、%s
、%v
などの書式指定子の処理
reflect.Value とは
reflect.Value
は Go言語の反射システムの中核となる型で、以下の機能を提供します:
- 値の型情報の取得
- 値の実際の内容へのアクセス
- メソッドの呼び出し
- フィールドへのアクセス
2011年のreflect.Interface変更
2011年頃、Go言語の reflect
パッケージは大幅な設計変更を受けました。特に reflect.Interface
メソッドの変更により、既存のコードが影響を受けました。
技術的詳細
1. pp構造体の変更
type pp struct {
n int
panicking bool
buf bytes.Buffer
- runeBuf [utf8.UTFMax]byte
- fmt fmt
+ // value holds the current item, as a reflect.Value, and will be
+ // the zero Value if the item has not been reflected.
+ value reflect.Value
+ runeBuf [utf8.UTFMax]byte
+ fmt fmt
}
この変更により、現在処理中の値を reflect.Value
として保持できるようになりました。
2. 関数シグネチャの簡素化
変更前:
func (p *pp) fmtBool(v bool, verb int, value interface{}, value1 reflect.Value)
func (p *pp) fmtInt64(v int64, verb int, value interface{}, value1 reflect.Value)
func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{}, value1 reflect.Value)
変更後:
func (p *pp) fmtBool(v bool, verb int, value interface{})
func (p *pp) fmtInt64(v int64, verb int, value interface{})
func (p *pp) fmtString(v string, verb int, goSyntax bool, value interface{})
3. badVerbメソッドの改善
func (p *pp) badVerb(verb int, val interface{}) {
p.add('%')
p.add('!')
p.add(verb)
p.add('(')
switch {
case val != nil:
p.buf.WriteString(reflect.TypeOf(val).String())
p.add('=')
p.printField(val, 'v', false, false, 0)
- case val1.IsValid():
- p.buf.WriteString(val1.Type().String())
+ case p.value.IsValid():
+ p.buf.WriteString(p.value.Type().String())
p.add('=')
- p.printValue(val1, 'v', false, false, 0)
+ p.printValue(p.value, 'v', false, false, 0)
default:
p.buf.Write(nilAngleBytes)
}
p.add(')')
}
4. printReflectValueでの状態管理
func (p *pp) printReflectValue(value reflect.Value, verb int, plus, goSyntax bool, depth int) (wasString bool) {
+ oldValue := p.value
+ p.value = value
BigSwitch:
switch f := value; f.Kind() {
// ... 処理 ...
}
+ p.value = oldValue
+ return wasString
}
コアとなるコードの変更箇所
1. src/pkg/fmt/print.go:74-80 (pp構造体の定義)
type pp struct {
n int
panicking bool
buf bytes.Buffer
+ // value holds the current item, as a reflect.Value, and will be
+ // the zero Value if the item has not been reflected.
+ value reflect.Value
runeBuf [utf8.UTFMax]byte
fmt fmt
}
2. src/pkg/fmt/print.go:132-139 (free関数の更新)
func (p *pp) free() {
if cap(p.buf.Bytes()) > 1<<16 {
return
}
p.buf.Reset()
+ p.value = reflect.Value{}
ppFree.put(p)
}
3. src/pkg/fmt/print.go:331-334 (printReflectValue)
func (p *pp) printReflectValue(value reflect.Value, verb int, plus, goSyntax bool, depth int) (wasString bool) {
+ oldValue := p.value
+ p.value = value
BigSwitch:
// ... 処理 ...
+ p.value = oldValue
+ return wasString
}
コアとなるコードの解説
1. 内部状態の一元化
最大の変更点は、pp
構造体に value
フィールドを追加したことです。これにより、現在処理中の reflect.Value
を構造体レベルで管理できるようになりました。
2. 関数シグネチャの簡素化
従来は各書式設定関数が reflect.Value
を個別に引数として受け取っていましたが、構造体フィールドとして保持することで、関数シグネチャが大幅に簡素化されました。
3. 性能向上
- メモリ効率の向上: 不要な
reflect.Value
の複製を削減 - CPU効率の向上: 引数の受け渡しオーバーヘッドを削減
- キャッシュ効率の向上: 構造体のレイアウトを最適化
4. 状態管理の改善
printReflectValue
で新旧の値を適切に管理することで、再帰的な処理でも正しい状態を維持できるようになりました。