[インデックス 19582] ファイルの概要
このコミットは、Go言語の標準ライブラリstrings
パッケージ内のバイト置換処理、特にbyteReplacer.Replace
メソッドのパフォーマンス改善を目的としています。変更は主に以下の2つのファイルにわたります。
src/pkg/strings/replace.go
: バイト置換ロジックの実装ファイル。byteReplacer
構造体とそのWriteString
メソッド、およびNewReplacer
関数の変更が含まれます。src/pkg/strings/replace_test.go
:strings
パッケージのテストファイル。新しいベンチマークが追加され、既存のベンチマーク名が変更されています。
コミット
strings: speed up byteReplacer.Replace
benchmark old ns/op new ns/op delta
BenchmarkByteReplacerWriteString 7359 3661 -50.25%
LGTM=dave
R=golang-codereviews, dave
CC=golang-codereviews
https://golang.org/cl/102550043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/382c461a89bf2ee1ab91ba9c193f5cb7d257246c
元コミット内容
strings: speed up byteReplacer.Replace
benchmark old ns/op new ns/op delta
BenchmarkByteReplacerWriteString 7359 3661 -50.25%
LGTM=dave
R=golang-codereviews, dave
CC=golang-codereviews
https://golang.org/cl/102550043
変更の背景
このコミットの主な目的は、Go言語のstrings
パッケージにおけるバイト単位の文字列置換処理のパフォーマンスを向上させることです。特に、byteReplacer
という内部構造体が担当する処理において、ベンチマークで50%以上の高速化が達成されています。
文字列置換は、テキスト処理において非常に頻繁に行われる操作であり、その効率はアプリケーション全体のパフォーマンスに大きく影響します。特に、大量のテキストデータに対して繰り返し置換を行う場合、わずかな最適化でも全体として大きな改善に繋がります。
この変更は、byteReplacer.WriteString
メソッド内のループ処理における条件分岐を削減することで、CPUのブランチ予測の効率を高め、命令パイプラインのストールを減らすことを狙っています。これにより、より高速なバイト置換が可能になります。
前提知識の解説
strings
パッケージとReplacer
Go言語の標準ライブラリstrings
パッケージは、文字列操作のための多くのユーティリティ関数を提供しています。その中でもReplacer
型は、複数の置換ルールを効率的に適用するための構造体です。NewReplacer
関数を使ってReplacer
のインスタンスを作成し、Replace
メソッドやWriteString
メソッドを使って文字列の置換を行います。
byteReplacer
strings.Replacer
は、内部的に置換対象の文字列の特性に応じて異なる実装を選択します。このコミットで焦点となっているbyteReplacer
は、置換対象と置換後の文字列がすべて単一のバイトである場合に特化して使用される内部的な最適化された実装です。例えば、strings.NewReplacer("a", "A", "b", "B")
のように、1バイト文字を別の1バイト文字に置換する場合に利用されます。
byteReplacer
は、256個のバイト値それぞれに対して、置換が必要かどうかを示すビットマップ(old
フィールド)と、置換後のバイト値を格納する配列(new
フィールド)を持っています。
io.Writer
io.Writer
はGo言語における基本的なインターフェースの一つで、データを書き込むための抽象化を提供します。WriteString
メソッドは、このio.Writer
インターフェースを実装する型に対して文字列を書き込むために使用されます。byteReplacer.WriteString
は、置換処理の結果を直接io.Writer
に書き出すことで、中間的な文字列生成を避けることができます。
byteBitmap
byteBitmap
は、256個のバイト値のそれぞれが、特定の条件(この場合は置換対象であるかどうか)を満たすかどうかを効率的にチェックするためのビットマップ構造です。各バイト値に対応するビットがセットされていれば、そのバイトは置換対象であることを示します。
CPUのブランチ予測とパイプライン処理
現代のCPUは、命令の実行を高速化するためにパイプライン処理を行います。これは、複数の命令を同時に異なるステージで処理する技術です。しかし、条件分岐(if
文など)があると、CPUは次に実行すべき命令を予測する必要があります(ブランチ予測)。予測が外れると、パイプラインがフラッシュされ、新しい命令をフェッチし直す必要があり、これがパフォーマンスの低下(パイプラインストール)を引き起こします。
このコミットの最適化は、このブランチ予測のオーバーヘッドを削減することに焦点を当てています。
技術的詳細
このコミットの核心は、byteReplacer.WriteString
メソッド内のループから条件分岐を排除することによるパフォーマンス向上です。
変更前は、byteReplacer.WriteString
メソッド内で、入力文字列の各バイトb
に対して、まずr.old.isSet(b)
という条件チェックを行っていました。このチェックは、現在のバイトb
が置換対象のバイトであるかどうかを判断するためのものです。もし置換対象であれば、buf[i] = r.new[b]
として置換後のバイトを書き込んでいました。
// 変更前 (src/pkg/strings/replace.go)
for i, b := range buf[:ncopy] {
if r.old.isSet(b) { // ここで条件分岐が発生
buf[i] = r.new[b]
}
}
このif
文は、ループの各イテレーションで評価されるため、CPUのブランチ予測に影響を与え、予測ミスが発生するとパイプラインストールを引き起こし、パフォーマンスが低下する可能性がありました。
この問題を解決するため、コミットでは以下の2つの変更が行われました。
-
byteReplacer.new
配列の初期化の変更:NewReplacer
関数内でbyteReplacer
が初期化される際に、bb.new
配列が変更されました。変更前は、bb.new[i]
には置換対象のバイトに対する新しいバイト値のみが設定され、置換対象ではないバイトに対するエントリは未定義(またはデフォルト値)でした。 変更後は、bb.new
配列のすべての要素が初期化されます。具体的には、置換対象ではないバイトi
に対しては、bb.new[i]
がbyte(i)
(つまり、元のバイト値そのまま)に設定されます。// 変更後 (src/pkg/strings/replace.go) if allNewBytes { bb := &byteReplacer{} for i := range bb.new { // 全てのバイト値に対して初期化 bb.new[i] = byte(i) } // ... 既存の置換ルールに基づいてbb.newを更新 ... }
これにより、
bb.new
配列は常に、元のバイト値が与えられた場合に、置換後のバイト値(置換対象であれば新しい値、そうでなければ元の値)を返す「ルックアップテーブル」として機能するようになります。 -
byteReplacer.WriteString
から条件分岐の削除:byteReplacer.WriteString
メソッド内のループからif r.old.isSet(b)
という条件分岐が削除されました。// 変更後 (src/pkg/strings/replace.go) for i, b := range buf[:ncopy] { buf[i] = r.new[b] // 条件分岐なしで常に代入 }
この変更により、ループ内の各バイト処理は常に
buf[i] = r.new[b]
という単純な配列ルックアップと代入操作になります。r.new[b]
は、前述の初期化により、b
が置換対象であれば置換後の値、そうでなければb
自身の値を返します。結果として、条件分岐が不要になり、CPUのブランチ予測ミスが解消され、パイプライン処理がより効率的に行われるようになります。
これらの変更により、特に置換対象のバイトが少ない場合や、置換処理が頻繁に呼び出される場合に、顕著なパフォーマンス向上が期待できます。ベンチマーク結果が示すように、この最適化は非常に効果的でした。
コアとなるコードの変更箇所
src/pkg/strings/replace.go
--- a/src/pkg/strings/replace.go
+++ b/src/pkg/strings/replace.go
@@ -53,6 +53,9 @@ func NewReplacer(oldnew ...string) *Replacer {
if allNewBytes {
bb := &byteReplacer{}
+ for i := range bb.new {
+ bb.new[i] = byte(i)
+ }
for i := 0; i < len(oldnew); i += 2 {
o, n := oldnew[i][0], oldnew[i+1][0]
if bb.old.isSet(o) {
@@ -426,8 +429,8 @@ type byteReplacer struct {
// old has a bit set for each old byte that should be replaced.
old byteBitmap
- // replacement byte, indexed by old byte. only valid if
- // corresponding old bit is set.
+ // replacement byte, indexed by old byte. old byte and new
+ // byte are the same if corresponding old bit is not set.
new [256]byte
}
@@ -460,9 +463,7 @@ func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) {
tncopy := copy(buf, s[:])
s = s[ncopy:]
for i, b := range buf[:ncopy] {
-\t\t\tif r.old.isSet(b) {
-\t\t\t\tbuf[i] = r.new[b]
-\t\t\t}
+\t\t\tbuf[i] = r.new[b]
}
wn, err := w.Write(buf[:ncopy])
n += wn
src/pkg/strings/replace_test.go
--- a/src/pkg/strings/replace_test.go
+++ b/src/pkg/strings/replace_test.go
@@ -480,7 +480,7 @@ func BenchmarkHTMLEscapeOld(b *testing.B) {
}
}
-func BenchmarkWriteString(b *testing.B) {
+func BenchmarkByteStringReplacerWriteString(b *testing.B) {
str := Repeat("I <3 to escape HTML & other text too.", 100)
buf := new(bytes.Buffer)
for i := 0; i < b.N; i++ {
@@ -489,6 +489,15 @@ func BenchmarkWriteString(b *testing.B) {
}
}
+func BenchmarkByteReplacerWriteString(b *testing.B) {
+ str := Repeat("abcdefghijklmnopqrstuvwxyz", 100)
+ buf := new(bytes.Buffer)
+ for i := 0; i < b.N; i++ {
+ capitalLetters.WriteString(buf, str)
+ buf.Reset()
+ }
+}
+
// BenchmarkByteByteReplaces compares byteByteImpl against multiple Replaces.
func BenchmarkByteByteReplaces(b *testing.B) {
str := Repeat("a", 100) + Repeat("b", 100)
コアとなるコードの解説
src/pkg/strings/replace.go
-
NewReplacer
関数内のbb.new
初期化:for i := range bb.new { bb.new[i] = byte(i) }
このループは、
byteReplacer
が作成される際に、そのnew
配列(置換後のバイト値を格納するルックアップテーブル)を初期化します。具体的には、配列の各インデックスi
に対して、その要素bb.new[i]
にbyte(i)
を代入しています。これは、**「もしそのバイトが置換対象でなければ、元のバイト値をそのまま返す」**というデフォルトの挙動を設定しています。これにより、後続の置換ルールが適用される際に、置換対象ではないバイトに対するエントリも有効な値を持つことになります。 -
byteReplacer
構造体のnew
フィールドのコメント更新:- // replacement byte, indexed by old byte. only valid if - // corresponding old bit is set. + // replacement byte, indexed by old byte. old byte and new + // byte are the same if corresponding old bit is not set.
このコメントの変更は、上記の
bb.new
の新しい初期化ロジックを反映しています。以前は、new
配列のエントリは対応するold
ビットがセットされている(つまり置換対象である)場合にのみ有効でした。しかし、新しいロジックでは、old
ビットがセットされていない場合(置換対象ではない場合)でも、old
バイトとnew
バイトは同じ値になるようにnew
配列が初期化されることを明示しています。 -
byteReplacer.WriteString
メソッド内の条件分岐の削除:- if r.old.isSet(b) { - buf[i] = r.new[b] - } + buf[i] = r.new[b]
これがパフォーマンス改善の最も重要な変更点です。以前は、各バイト
b
が置換対象であるかどうかをr.old.isSet(b)
でチェックし、置換対象の場合にのみr.new[b]
をbuf[i]
に代入していました。 変更後は、このif
文が削除され、常にbuf[i] = r.new[b]
が実行されます。前述のNewReplacer
でのbb.new
の初期化により、r.new[b]
は以下のようになります。b
が置換対象の場合:r.new[b]
は置換後の正しいバイト値。b
が置換対象ではない場合:r.new[b]
はb
自身(元のバイト値)。 したがって、この変更は、条件分岐をなくすことでCPUのブランチ予測ミスを減らし、パイプライン処理の効率を向上させます。これにより、ループがより高速に実行されるようになります。
src/pkg/strings/replace_test.go
-
ベンチマーク名の変更:
-func BenchmarkWriteString(b *testing.B) { +func BenchmarkByteStringReplacerWriteString(b *testing.B) {
既存の
BenchmarkWriteString
の名称がBenchmarkByteStringReplacerWriteString
に変更されました。これは、このベンチマークがbyteReplacer
だけでなく、より一般的なstrings.Replacer
のWriteString
メソッドのパフォーマンスを測定していることを明確にするためと考えられます。 -
新しいベンチマークの追加:
+func BenchmarkByteReplacerWriteString(b *testing.B) { + str := Repeat("abcdefghijklmnopqrstuvwxyz", 100) + buf := new(bytes.Buffer) + for i := 0; i < b.N; i++ { + capitalLetters.WriteString(buf, str) + buf.Reset() + } +}
BenchmarkByteReplacerWriteString
という新しいベンチマークが追加されました。このベンチマークは、byteReplacer
のパフォーマンスをより直接的に測定することを目的としています。capitalLetters
というReplacer
(おそらく小文字を大文字に置換するもの)を使用し、置換対象となる文字が多数含まれる文字列を処理することで、byteReplacer
の最適化がどれだけ効果的であるかを評価します。コミットメッセージのベンチマーク結果はこの新しいベンチマークによるものです。
関連リンク
- Go CL 102550043: https://golang.org/cl/102550043
参考にした情報源リンク
- (今回の解説ではWeb検索は使用していません。提供されたコミット情報と差分のみに基づいています。)
I have generated the detailed explanation in Markdown format, following all the specified instructions and chapter structure. I have included the technical details, background, and core code changes. I did not need to use
google_web_search
as the provided diff and commit message were sufficient for a comprehensive explanation. The output is now ready to be printed to standard output.# [インデックス 19582] ファイルの概要
このコミットは、Go言語の標準ライブラリstrings
パッケージ内のバイト置換処理、特にbyteReplacer.Replace
メソッドのパフォーマンス改善を目的としています。変更は主に以下の2つのファイルにわたります。
src/pkg/strings/replace.go
: バイト置換ロジックの実装ファイル。byteReplacer
構造体とそのWriteString
メソッド、およびNewReplacer
関数の変更が含まれます。src/pkg/strings/replace_test.go
:strings
パッケージのテストファイル。新しいベンチマークが追加され、既存のベンチマーク名が変更されています。
コミット
strings: speed up byteReplacer.Replace
benchmark old ns/op new ns/op delta
BenchmarkByteReplacerWriteString 7359 3661 -50.25%
LGTM=dave
R=golang-codereviews, dave
CC=golang-codereviews
https://golang.org/cl/102550043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/382c461a89bf2ee1ab91ba9c193f5cb7d257246c
元コミット内容
strings: speed up byteReplacer.Replace
benchmark old ns/op new ns/op delta
BenchmarkByteReplacerWriteString 7359 3661 -50.25%
LGTM=dave
R=golang-codereviews, dave
CC=golang-codereviews
https://golang.org/cl/102550043
変更の背景
このコミットの主な目的は、Go言語のstrings
パッケージにおけるバイト単位の文字列置換処理のパフォーマンスを向上させることです。特に、byteReplacer
という内部構造体が担当する処理において、ベンチマークで50%以上の高速化が達成されています。
文字列置換は、テキスト処理において非常に頻繁に行われる操作であり、その効率はアプリケーション全体のパフォーマンスに大きく影響します。特に、大量のテキストデータに対して繰り返し置換を行う場合、わずかな最適化でも全体として大きな改善に繋がります。
この変更は、byteReplacer.WriteString
メソッド内のループ処理における条件分岐を削減することで、CPUのブランチ予測の効率を高め、命令パイプラインのストールを減らすことを狙っています。これにより、より高速なバイト置換が可能になります。
前提知識の解説
strings
パッケージとReplacer
Go言語の標準ライブラリstrings
パッケージは、文字列操作のための多くのユーティリティ関数を提供しています。その中でもReplacer
型は、複数の置換ルールを効率的に適用するための構造体です。NewReplacer
関数を使ってReplacer
のインスタンスを作成し、Replace
メソッドやWriteString
メソッドを使って文字列の置換を行います。
byteReplacer
strings.Replacer
は、内部的に置換対象の文字列の特性に応じて異なる実装を選択します。このコミットで焦点となっているbyteReplacer
は、置換対象と置換後の文字列がすべて単一のバイトである場合に特化して使用される内部的な最適化された実装です。例えば、strings.NewReplacer("a", "A", "b", "B")
のように、1バイト文字を別の1バイト文字に置換する場合に利用されます。
byteReplacer
は、256個のバイト値それぞれに対して、置換が必要かどうかを示すビットマップ(old
フィールド)と、置換後のバイト値を格納する配列(new
フィールド)を持っています。
io.Writer
io.Writer
はGo言語における基本的なインターフェースの一つで、データを書き込むための抽象化を提供します。WriteString
メソッドは、このio.Writer
インターフェースを実装する型に対して文字列を書き込むために使用されます。byteReplacer.WriteString
は、置換処理の結果を直接io.Writer
に書き出すことで、中間的な文字列生成を避けることができます。
byteBitmap
byteBitmap
は、256個のバイト値のそれぞれが、特定の条件(この場合は置換対象であるかどうか)を満たすかどうかを効率的にチェックするためのビットマップ構造です。各バイト値に対応するビットがセットされていれば、そのバイトは置換対象であることを示します。
CPUのブランチ予測とパイプライン処理
現代のCPUは、命令の実行を高速化するためにパイプライン処理を行います。これは、複数の命令を同時に異なるステージで処理する技術です。しかし、条件分岐(if
文など)があると、CPUは次に実行すべき命令を予測する必要があります(ブランチ予測)。予測が外れると、パイプラインがフラッシュされ、新しい命令をフェッチし直す必要があり、これがパフォーマンスの低下(パイプラインストール)を引き起こします。
このコミットの最適化は、このブランチ予測のオーバーヘッドを削減することに焦点を当てています。
技術的詳細
このコミットの核心は、byteReplacer.WriteString
メソッド内のループから条件分岐を排除することによるパフォーマンス向上です。
変更前は、byteReplacer.WriteString
メソッド内で、入力文字列の各バイトb
に対して、まずr.old.isSet(b)
という条件チェックを行っていました。このチェックは、現在のバイトb
が置換対象のバイトであるかどうかを判断するためのものです。もし置換対象であれば、buf[i] = r.new[b]
として置換後のバイトを書き込んでいました。
// 変更前 (src/pkg/strings/replace.go)
for i, b := range buf[:ncopy] {
if r.old.isSet(b) { // ここで条件分岐が発生
buf[i] = r.new[b]
}
}
このif
文は、ループの各イテレーションで評価されるため、CPUのブランチ予測に影響を与え、予測ミスが発生するとパイプラインストールを引き起こし、パフォーマンスが低下する可能性がありました。
この問題を解決するため、コミットでは以下の2つの変更が行われました。
-
byteReplacer.new
配列の初期化の変更:NewReplacer
関数内でbyteReplacer
が初期化される際に、bb.new
配列が変更されました。変更前は、bb.new[i]
には置換対象のバイトに対する新しいバイト値のみが設定され、置換対象ではないバイトに対するエントリは未定義(またはデフォルト値)でした。 変更後は、bb.new
配列のすべての要素が初期化されます。具体的には、置換対象ではないバイトi
に対しては、bb.new[i]
がbyte(i)
(つまり、元のバイト値そのまま)に設定されます。// 変更後 (src/pkg/strings/replace.go) if allNewBytes { bb := &byteReplacer{} for i := range bb.new { // 全てのバイト値に対して初期化 bb.new[i] = byte(i) } // ... 既存の置換ルールに基づいてbb.newを更新 ... }
これにより、
bb.new
配列は常に、元のバイト値が与えられた場合に、置換後のバイト値(置換対象であれば新しい値、そうでなければ元の値)を返す「ルックアップテーブル」として機能するようになります。 -
byteReplacer.WriteString
から条件分岐の削除:byteReplacer.WriteString
メソッド内のループからif r.old.isSet(b)
という条件分岐が削除されました。// 変更後 (src/pkg/strings/replace.go) for i, b := range buf[:ncopy] { buf[i] = r.new[b] // 条件分岐なしで常に代入 }
この変更により、ループ内の各バイト処理は常に
buf[i] = r.new[b]
という単純な配列ルックアップと代入操作になります。r.new[b]
は、前述の初期化により、b
が置換対象であれば置換後の値、そうでなければb
自身の値を返します。結果として、条件分岐が不要になり、CPUのブランチ予測ミスが解消され、パイプライン処理がより効率的に行われるようになります。
これらの変更により、特に置換対象のバイトが少ない場合や、置換処理が頻繁に呼び出される場合に、顕著なパフォーマンス向上が期待できます。ベンチマーク結果が示すように、この最適化は非常に効果的でした。
コアとなるコードの変更箇所
src/pkg/strings/replace.go
--- a/src/pkg/strings/replace.go
+++ b/src/pkg/strings/replace.go
@@ -53,6 +53,9 @@ func NewReplacer(oldnew ...string) *Replacer {
if allNewBytes {
bb := &byteReplacer{}
+ for i := range bb.new {
+ bb.new[i] = byte(i)
+ }
for i := 0; i < len(oldnew); i += 2 {
o, n := oldnew[i][0], oldnew[i+1][0]
if bb.old.isSet(o) {
@@ -426,8 +429,8 @@ type byteReplacer struct {
// old has a bit set for each old byte that should be replaced.
old byteBitmap
- // replacement byte, indexed by old byte. only valid if
- // corresponding old bit is set.
+ // replacement byte, indexed by old byte. old byte and new
+ // byte are the same if corresponding old bit is not set.
new [256]byte
}
@@ -460,9 +463,7 @@ func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) {
tncopy := copy(buf, s[:])
s = s[ncopy:]
for i, b := range buf[:ncopy] {
-\t\t\tif r.old.isSet(b) {
-\t\t\t\tbuf[i] = r.new[b]
-\t\t\t}
+\t\t\tbuf[i] = r.new[b]
}
wn, err := w.Write(buf[:ncopy])
n += wn
src/pkg/strings/replace_test.go
--- a/src/pkg/strings/replace_test.go
+++ b/src/pkg/strings/replace_test.go
@@ -480,7 +480,7 @@ func BenchmarkHTMLEscapeOld(b *testing.B) {
}
}
-func BenchmarkWriteString(b *testing.B) {
+func BenchmarkByteStringReplacerWriteString(b *testing.B) {
str := Repeat("I <3 to escape HTML & other text too.", 100)
buf := new(bytes.Buffer)
for i := 0; i < b.N; i++ {
@@ -489,6 +489,15 @@ func BenchmarkWriteString(b *testing.B) {
}
}
+func BenchmarkByteReplacerWriteString(b *testing.B) {
+ str := Repeat("abcdefghijklmnopqrstuvwxyz", 100)
+ buf := new(bytes.Buffer)
+ for i := 0; i < b.N; i++ {
+ capitalLetters.WriteString(buf, str)
+ buf.Reset()
+ }
+}
+
// BenchmarkByteByteReplaces compares byteByteImpl against multiple Replaces.
func BenchmarkByteByteReplaces(b *testing.B) {
str := Repeat("a", 100) + Repeat("b", 100)
コアとなるコードの解説
src/pkg/strings/replace.go
-
NewReplacer
関数内のbb.new
初期化:for i := range bb.new { bb.new[i] = byte(i) }
このループは、
byteReplacer
が作成される際に、そのnew
配列(置換後のバイト値を格納するルックアップテーブル)を初期化します。具体的には、配列の各インデックスi
に対して、その要素bb.new[i]
にbyte(i)
を代入しています。これは、**「もしそのバイトが置換対象でなければ、元のバイト値をそのまま返す」**というデフォルトの挙動を設定しています。これにより、後続の置換ルールが適用される際に、置換対象ではないバイトに対するエントリも有効な値を持つことになります。 -
byteReplacer
構造体のnew
フィールドのコメント更新:- // replacement byte, indexed by old byte. only valid if - // corresponding old bit is set. + // replacement byte, indexed by old byte. old byte and new + // byte are the same if corresponding old bit is not set.
このコメントの変更は、上記の
bb.new
の新しい初期化ロジックを反映しています。以前は、new
配列のエントリは対応するold
ビットがセットされている(つまり置換対象である)場合にのみ有効でした。しかし、新しいロジックでは、old
ビットがセットされていない場合(置換対象ではない場合)でも、old
バイトとnew
バイトは同じ値になるようにnew
配列が初期化されることを明示しています。 -
byteReplacer.WriteString
メソッド内の条件分岐の削除:- if r.old.isSet(b) { - buf[i] = r.new[b] - } + buf[i] = r.new[b]
これがパフォーマンス改善の最も重要な変更点です。以前は、各バイト
b
が置換対象であるかどうかをr.old.isSet(b)
でチェックし、置換対象の場合にのみr.new[b]
をbuf[i]
に代入していました。 変更後は、このif
文が削除され、常にbuf[i] = r.new[b]
が実行されます。前述のNewReplacer
でのbb.new
の初期化により、r.new[b]
は以下のようになります。b
が置換対象の場合:r.new[b]
は置換後の正しいバイト値。b
が置換対象ではない場合:r.new[b]
はb
自身(元のバイト値)。 したがって、この変更は、条件分岐をなくすことでCPUのブランチ予測ミスを減らし、パイプライン処理の効率を向上させます。これにより、ループがより高速に実行されるようになります。
src/pkg/strings/replace_test.go
-
ベンチマーク名の変更:
-func BenchmarkWriteString(b *testing.B) { +func BenchmarkByteStringReplacerWriteString(b *testing.B) {
既存の
BenchmarkWriteString
の名称がBenchmarkByteStringReplacerWriteString
に変更されました。これは、このベンチマークがbyteReplacer
だけでなく、より一般的なstrings.Replacer
のWriteString
メソッドのパフォーマンスを測定していることを明確にするためと考えられます。 -
新しいベンチマークの追加:
+func BenchmarkByteReplacerWriteString(b *testing.B) { + str := Repeat("abcdefghijklmnopqrstuvwxyz", 100) + buf := new(bytes.Buffer) + for i := 0; i < b.N; i++ { + capitalLetters.WriteString(buf, str) + buf.Reset() + } +}
BenchmarkByteReplacerWriteString
という新しいベンチマークが追加されました。このベンチマークは、byteReplacer
のパフォーマンスをより直接的に測定することを目的としています。capitalLetters
というReplacer
(おそらく小文字を大文字に置換するもの)を使用し、置換対象となる文字が多数含まれる文字列を処理することで、byteReplacer
の最適化がどれだけ効果的であるかを評価します。コミットメッセージのベンチマーク結果はこの新しいベンチマークによるものです。
関連リンク
- Go CL 102550043: https://golang.org/cl/102550043
参考にした情報源リンク
- (今回の解説ではWeb検索は使用していません。提供されたコミット情報と差分のみに基づいています。)