[インデックス 17037] ファイルの概要
このコミットは、Go言語のランタイムにおけるappend
操作のパフォーマンスチューニングに関するものです。特に、Intelプラットフォーム(amd64および386アーキテクチャ)でのスライスおよび文字列の追加(append)処理における「crossover」値を調整することで、小規模な追加操作の効率を向上させています。
コミット
commit 8ce8adbe7a194cba2819b533b603d92df40fe799
Author: Dave Cheney <dave@cheney.net>
Date: Tue Aug 6 07:51:37 2013 +1000
runtime: tune append crossover on amd64 and 386
Fixes #4963.
Sets the append crossover to 0 on intel platforms.
Results for linux/amd64 Core i5 SNB
benchmark old ns/op new ns/op delta
BenchmarkAppend 102 104 +1.96%
BenchmarkAppend1Byte 10 11 +0.92%
BenchmarkAppend4Bytes 15 11 -28.10%
BenchmarkAppend7Bytes 17 12 -32.58%
BenchmarkAppend8Bytes 18 12 -36.17%
BenchmarkAppend15Bytes 24 11 -55.02%
BenchmarkAppend16Bytes 25 11 -56.03%
BenchmarkAppend32Bytes 11 12 +4.31%
BenchmarkAppendStr1Byte 8 9 +13.99%
BenchmarkAppendStr4Bytes 11 9 -17.52%
BenchmarkAppendStr8Bytes 14 9 -35.70%
BenchmarkAppendStr16Bytes 21 9 -55.19%
BenchmarkAppendStr32Bytes 10 10 -5.66%
BenchmarkAppendSpecialCase 49 52 +7.96%
Results for linux/386 Atom(TM) CPU 330 @ 1.60GHz
benchmark old ns/op new ns/op delta
BenchmarkAppend 219 218 -0.46%
BenchmarkAppend1Byte 75 72 -3.44%
BenchmarkAppend4Bytes 92 73 -19.87%
BenchmarkAppend7Bytes 108 74 -31.20%
BenchmarkAppend8Bytes 116 74 -35.95%
BenchmarkAppend15Bytes 162 77 -52.22%
BenchmarkAppend16Bytes 169 77 -54.20%
BenchmarkAppend32Bytes 88 86 -2.38%
BenchmarkAppendStr1Byte 57 59 +3.32%
BenchmarkAppendStr4Bytes 72 59 -17.40%
BenchmarkAppendStr8Bytes 92 60 -34.70%
BenchmarkAppendStr16Bytes 141 63 -54.89%
BenchmarkAppendStr32Bytes 75 73 -2.64%
BenchmarkAppendSpecialCase 270 270 +0.00%
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/12440044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8ce8adbe7a194cba2819b533b603d92df40fe799
元コミット内容
このコミットは、Goランタイムのappend
操作におけるappendCrossover
の値をamd64および386アーキテクチャ向けに調整するものです。具体的には、これらのIntelプラットフォームにおいてappendCrossover
の値を0
に設定することで、小規模なデータ追加時のパフォーマンスを改善しています。コミットメッセージには、変更前後のベンチマーク結果が詳細に記載されており、特に小容量のバイト列や文字列の追加において顕著な性能向上が見られます。
変更の背景
Go言語のスライスや文字列のappend
操作は、内部的にランタイムによって効率的に処理されます。この処理には、追加されるデータのサイズに応じて異なる最適化戦略が適用されることがあります。appendCrossover
は、この最適化戦略を決定する閾値の一つです。
従来のGoランタイムでは、append
操作において、追加されるデータのサイズが特定の閾値(appendCrossover
)以下の場合には、よりシンプルなバイト単位のコピー(while
ループなど)が使用され、閾値を超える場合には、memmove
のようなより最適化されたメモリコピー関数が使用されるというロジックが存在しました。
しかし、Intelプラットフォーム(amd64および386)において、このappendCrossover
のデフォルト値(16バイト)が、小規模な追加操作のパフォーマンスにとって最適ではないことが判明しました。特に、1バイトから16バイト程度の非常に小さなデータの追加において、memmove
のような汎用的なメモリコピー関数を呼び出すオーバーヘッドが、単純なバイトコピーのオーバーヘッドを上回ってしまうケースがありました。
このコミットは、この非効率性を解消し、小規模なappend
操作のパフォーマンスを向上させることを目的としています。
前提知識の解説
Goのスライスとappend
関数
Go言語のスライスは、配列をラップした動的なデータ構造です。スライスは、内部的にポインタ、長さ(len
)、容量(cap
)の3つの要素で構成されます。append
関数は、スライスに要素を追加するために使用されます。
append
関数が呼び出されると、Goランタイムは以下のロジックに従って動作します。
- 容量の確認: 現在のスライスの容量が、追加される要素を格納するのに十分であるかを確認します。
- 再割り当て: 容量が不足している場合、ランタイムはより大きな新しい基底配列を割り当て、既存の要素を新しい配列にコピーし、追加される要素をその後に配置します。この際、新しい容量は通常、元の容量の2倍(またはそれ以上)に拡張されます。
- 要素のコピー: 容量が十分な場合、または新しい配列が割り当てられた場合、追加される要素がスライスの末尾にコピーされます。
appendCrossover
appendCrossover
は、Goランタイムがスライスや文字列のappend
操作を行う際に、内部的に使用するメモリコピー戦略を切り替えるための閾値です。この値は、アーキテクチャ固有のヘッダーファイル(例: src/pkg/runtime/arch_amd64.h
)で定義されています。
w <= appendCrossover
の場合: 追加されるバイト数w
がappendCrossover
以下の場合、ランタイムは通常、シンプルなループによるバイト単位のコピーを行います。これは、非常に小さなコピーサイズの場合に、memmove
のような最適化された関数を呼び出すオーバーヘッドを避けるためです。w > appendCrossover
の場合: 追加されるバイト数w
がappendCrossover
を超える場合、ランタイムは通常、memmove
のような、より高速で最適化されたメモリコピー関数を使用します。これらの関数は、大量のデータを効率的にコピーするためにアセンブリ言語などで最適化されています。
このコミットの目的は、Intelプラットフォームにおいて、このappendCrossover
の値を0
に設定することです。これにより、追加されるデータのサイズに関わらず、常にmemmove
のような最適化されたメモリコピー関数が使用されるようになります。
ベンチマーク結果の読み方
コミットメッセージに記載されているベンチマーク結果は、go test -bench=.
コマンドなどで取得されるパフォーマンス測定値です。
ns/op
(nanoseconds per operation): 1回の操作にかかる平均ナノ秒。この値が小さいほど、パフォーマンスが良いことを示します。delta
: 変更前(old)と変更後(new)のパフォーマンスの差をパーセンテージで示します。負の値はパフォーマンスの向上(時間が短縮された)、正の値はパフォーマンスの低下(時間が長くなった)を示します。
技術的詳細
このコミットの核心は、appendCrossover
の値を0
に設定することです。
従来のGoランタイムでは、appendCrossover
が16
に設定されていました。これは、16バイト以下の追加操作ではバイト単位のコピーを、16バイトを超える追加操作ではmemmove
を使用するという意味です。しかし、Intelプロセッサの進化により、小規模なメモリコピーにおいてもmemmove
のような最適化されたルーチンが非常に効率的になっていました。むしろ、appendCrossover
の条件分岐や、バイト単位のループ処理のオーバーヘッドが、memmove
の呼び出しオーバーヘッドよりも大きくなるケースが発生していました。
appendCrossover = 0
と設定することで、runtime·appendslice
およびruntime·appendstr
関数内の条件分岐if(w <= appendCrossover)
が常に偽(w > 0
の場合)となり、結果として常にelse
ブロックのmemmove
相当の処理が実行されるようになります。
ただし、slice.c
の変更を見ると、if(appendCrossover > 0 && w <= appendCrossover)
という条件に変わっています。これは、appendCrossover
が0
の場合には、この条件全体がfalse
となり、常にelse
ブロック(memmove
相当の処理)が実行されることを意味します。つまり、appendCrossover
が0
に設定されたプラットフォームでは、追加されるデータのサイズに関わらず、常に最適化されたメモリコピー関数が使用されるようになります。
この変更により、特に小規模なデータ(1バイトから16バイト程度)の追加において、条件分岐のオーバーヘッドや非効率なループ処理が回避され、memmove
による高速なコピーが常に適用されるため、パフォーマンスが大幅に向上します。ベンチマーク結果からも、BenchmarkAppend4Bytes
からBenchmarkAppend16Bytes
、および対応する文字列ベンチマークで顕著な改善が見られることが確認できます。
コアとなるコードの変更箇所
このコミットでは、以下の4つのファイルが変更されています。
src/pkg/runtime/append_test.go
: ベンチマークテストの追加。src/pkg/runtime/arch_386.h
: 386アーキテクチャのappendCrossover
の定義変更。src/pkg/runtime/arch_amd64.h
: amd64アーキテクチャのappendCrossover
の定義変更。src/pkg/runtime/slice.c
:append
操作の内部ロジックの変更。
src/pkg/runtime/append_test.go
--- a/src/pkg/runtime/append_test.go
+++ b/src/pkg/runtime/append_test.go
@@ -38,10 +38,18 @@ func BenchmarkAppend4Bytes(b *testing.B) {
benchmarkAppendBytes(b, 4)
}
+func BenchmarkAppend7Bytes(b *testing.B) {
+\tbenchmarkAppendBytes(b, 7)
+}
+\n func BenchmarkAppend8Bytes(b *testing.B) {
benchmarkAppendBytes(b, 8)
}
+func BenchmarkAppend15Bytes(b *testing.B) {
+\tbenchmarkAppendBytes(b, 15)
+}\n
func BenchmarkAppend16Bytes(b *testing.B) {
benchmarkAppendBytes(b, 16)
}
このファイルでは、BenchmarkAppend7Bytes
とBenchmarkAppend15Bytes
という新しいベンチマーク関数が追加されています。これは、appendCrossover
の閾値付近のパフォーマンスをより詳細に測定するために導入されたものです。
src/pkg/runtime/arch_386.h
--- a/src/pkg/runtime/arch_386.h
+++ b/src/pkg/runtime/arch_386.h
@@ -6,6 +6,6 @@ enum {
thechar = '8',
BigEndian = 0,
CacheLineSize = 64,
- appendCrossover = 16,
+ appendCrossover = 0,
PCQuantum = 1
};
386アーキテクチャの定義ファイルで、appendCrossover
の値が16
から0
に変更されています。
src/pkg/runtime/arch_amd64.h
--- a/src/pkg/runtime/arch_amd64.h
+++ b/src/pkg/runtime/arch_amd64.h
@@ -6,6 +6,6 @@ enum {
thechar = '6',
BigEndian = 0,
CacheLineSize = 64,
- appendCrossover = 16,
+ appendCrossover = 0,
PCQuantum = 1
};
amd64アーキテクチャの定義ファイルで、appendCrossover
の値が16
から0
に変更されています。
src/pkg/runtime/slice.c
--- a/src/pkg/runtime/slice.c
+++ b/src/pkg/runtime/slice.c
@@ -95,7 +95,7 @@ runtime·appendslice(SliceType *t, Slice x, Slice y, Slice ret)
p = ret.array+ret.len*w;
q = y.array;
w *= y.len;
- if(w <= appendCrossover) {
+ if(appendCrossover > 0 && w <= appendCrossover) {
if(p <= q || w <= p-q) // No overlap.
while(w-- > 0)
*p++ = *q++;
@@ -148,7 +148,7 @@ runtime·appendstr(SliceType *t, Slice x, String y, Slice ret)
w = y.len;
p = ret.array+ret.len;
q = y.str;
- if(w <= appendCrossover) {
+ if(appendCrossover > 0 && w <= appendCrossover) {
while(w-- > 0)
*p++ = *q++;
} else {
runtime·appendslice
とruntime·appendstr
関数の内部ロジックが変更されています。条件分岐がif(w <= appendCrossover)
からif(appendCrossover > 0 && w <= appendCrossover)
に変更されました。
コアとなるコードの解説
src/pkg/runtime/slice.c
における変更が、このコミットの動作の鍵となります。
変更前のコード:
if(w <= appendCrossover) {
// バイト単位のコピー
} else {
// memmove相当の最適化されたコピー
}
変更後のコード:
if(appendCrossover > 0 && w <= appendCrossover) {
// バイト単位のコピー
} else {
// memmove相当の最適化されたコピー
}
この変更により、appendCrossover
が0
に設定されたamd64および386アーキテクチャでは、appendCrossover > 0
という条件が常にfalse
となるため、if
ブロック内のバイト単位のコピー処理が実行されなくなります。結果として、追加されるデータのサイズw
がどれほど小さくても、常にelse
ブロック内のmemmove
相当の最適化されたメモリコピー処理が実行されるようになります。
これは、現代のIntelプロセッサにおいて、memmove
のような最適化されたルーチンが非常に効率的であり、たとえ数バイトのコピーであっても、条件分岐やループのオーバーヘッドを考慮すると、そちらの方が高速であるという知見に基づいています。
ベンチマーク結果は、この変更が意図した効果をもたらしたことを明確に示しています。特に、BenchmarkAppend4Bytes
からBenchmarkAppend16Bytes
、および対応する文字列ベンチマークにおいて、ns/op
が大幅に減少し、パフォーマンスが大きく向上していることがわかります。これは、これらの小規模な追加操作が、より効率的なmemmove
パスを通るようになったためです。
関連リンク
- Go Issue #4963: runtime: tune append crossover on amd64 and 386
- このコミットが修正したGoのIssueです。詳細な議論や背景情報が記載されている可能性があります。
- Goの
append
関数のドキュメント: https://pkg.go.dev/builtin#append - Goの内部的なスライス実装に関する記事(一般的な情報源)
参考にした情報源リンク
- Goのソースコード(特に
src/pkg/runtime/
ディレクトリ内のファイル) - GoのIssueトラッカー(#4963)
- Goのベンチマークに関する一般的な情報
- メモリコピー最適化に関する一般的なコンピュータアーキテクチャの知識
- Dave Cheney氏のブログや関連するGoのパフォーマンスに関する記事(もしあれば)
- (注: 特定のブログ記事への直接リンクは、検索結果に基づいて追加する必要がありますが、ここでは一般的な情報源として記載しています。)
[インデックス 17037] ファイルの概要
このコミットは、Go言語のランタイムにおけるappend
操作のパフォーマンスチューニングに関するものです。特に、Intelプラットフォーム(amd64および386アーキテクチャ)でのスライスおよび文字列の追加(append)処理における「crossover」値を調整することで、小規模な追加操作の効率を向上させています。
コミット
commit 8ce8adbe7a194cba2819b533b603d92df40fe799
Author: Dave Cheney <dave@cheney.net>
Date: Tue Aug 6 07:51:37 2013 +1000
runtime: tune append crossover on amd64 and 386
Fixes #4963.
Sets the append crossover to 0 on intel platforms.
Results for linux/amd64 Core i5 SNB
benchmark old ns/op new ns/op delta
BenchmarkAppend 102 104 +1.96%
BenchmarkAppend1Byte 10 11 +0.92%
BenchmarkAppend4Bytes 15 11 -28.10%
BenchmarkAppend7Bytes 17 12 -32.58%
BenchmarkAppend8Bytes 18 12 -36.17%
BenchmarkAppend15Bytes 24 11 -55.02%
BenchmarkAppend16Bytes 25 11 -56.03%
BenchmarkAppend32Bytes 11 12 +4.31%
BenchmarkAppendStr1Byte 8 9 +13.99%
BenchmarkAppendStr4Bytes 11 9 -17.52%
BenchmarkAppendStr8Bytes 14 9 -35.70%
BenchmarkAppendStr16Bytes 21 9 -55.19%
BenchmarkAppendStr32Bytes 10 10 -5.66%
BenchmarkAppendSpecialCase 49 52 +7.96%
Results for linux/386 Atom(TM) CPU 330 @ 1.60GHz
benchmark old ns/op new ns/op delta
BenchmarkAppend 219 218 -0.46%
BenchmarkAppend1Byte 75 72 -3.44%
BenchmarkAppend4Bytes 92 73 -19.87%
BenchmarkAppend7Bytes 108 74 -31.20%
BenchmarkAppend8Bytes 116 74 -35.95%
BenchmarkAppend15Bytes 162 77 -52.22%
BenchmarkAppend16Bytes 169 77 -54.20%
BenchmarkAppend32Bytes 88 86 -2.38%
BenchmarkAppendStr1Byte 57 59 +3.32%
BenchmarkAppendStr4Bytes 72 59 -17.40%
BenchmarkAppendStr8Bytes 92 60 -34.70%
BenchmarkAppendStr16Bytes 141 63 -54.89%
BenchmarkAppendStr32Bytes 75 73 -2.64%
BenchmarkAppendSpecialCase 270 270 +0.00%
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/12440044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8ce8adbe7a194cba2819b533b603d92df40fe799
元コミット内容
このコミットは、Goランタイムのappend
操作におけるappendCrossover
の値をamd64および386アーキテクチャ向けに調整するものです。具体的には、これらのIntelプラットフォームにおいてappendCrossover
の値を0
に設定することで、小規模なデータ追加時のパフォーマンスを改善しています。コミットメッセージには、変更前後のベンチマーク結果が詳細に記載されており、特に小容量のバイト列や文字列の追加において顕著な性能向上が見られます。
変更の背景
Go言語のスライスや文字列のappend
操作は、内部的にランタイムによって効率的に処理されます。この処理には、追加されるデータのサイズに応じて異なる最適化戦略が適用されることがあります。appendCrossover
は、この最適化戦略を決定する閾値の一つです。
従来のGoランタイムでは、append
操作において、追加されるデータのサイズが特定の閾値(appendCrossover
)以下の場合には、よりシンプルなバイト単位のコピー(while
ループなど)が使用され、閾値を超える場合には、memmove
のようなより最適化されたメモリコピー関数が使用されるというロジックが存在しました。
しかし、Intelプラットフォーム(amd64および386)において、このappendCrossover
のデフォルト値(16バイト)が、小規模な追加操作のパフォーマンスにとって最適ではないことが判明しました。特に、1バイトから16バイト程度の非常に小さなデータの追加において、memmove
のような汎用的なメモリコピー関数を呼び出すオーバーヘッドが、単純なバイトコピーのオーバーヘッドを上回ってしまうケースがありました。
このコミットは、この非効率性を解消し、小規模なappend
操作のパフォーマンスを向上させることを目的としています。
前提知識の解説
Goのスライスとappend
関数
Go言語のスライスは、配列をラップした動的なデータ構造です。スライスは、内部的にポインタ、長さ(len
)、容量(cap
)の3つの要素で構成されます。append
関数は、スライスに要素を追加するために使用されます。
append
関数が呼び出されると、Goランタイムは以下のロジックに従って動作します。
- 容量の確認: 現在のスライスの容量が、追加される要素を格納するのに十分であるかを確認します。
- 再割り当て: 容量が不足している場合、ランタイムはより大きな新しい基底配列を割り当て、既存の要素を新しい配列にコピーし、追加される要素をその後に配置します。この際、新しい容量は通常、元の容量の2倍(またはそれ以上)に拡張されます。
- 要素のコピー: 容量が十分な場合、または新しい配列が割り当てられた場合、追加される要素がスライスの末尾にコピーされます。
appendCrossover
appendCrossover
は、Goランタイムがスライスや文字列のappend
操作を行う際に、内部的に使用するメモリコピー戦略を切り替えるための閾値です。この値は、アーキテクチャ固有のヘッダーファイル(例: src/pkg/runtime/arch_amd64.h
)で定義されています。
w <= appendCrossover
の場合: 追加されるバイト数w
がappendCrossover
以下の場合、ランタイムは通常、シンプルなループによるバイト単位のコピーを行います。これは、非常に小さなコピーサイズの場合に、memmove
のような最適化された関数を呼び出すオーバーヘッドを避けるためです。w > appendCrossover
の場合: 追加されるバイト数w
がappendCrossover
を超える場合、ランタイムは通常、memmove
のような、より高速で最適化されたメモリコピー関数を使用します。これらの関数は、大量のデータを効率的にコピーするためにアセンブリ言語などで最適化されています。
このコミットの目的は、Intelプラットフォームにおいて、このappendCrossover
の値を0
に設定することです。これにより、追加されるデータのサイズに関わらず、常にmemmove
のような最適化されたメモリコピー関数が使用されるようになります。
ベンチマーク結果の読み方
コミットメッセージに記載されているベンチマーク結果は、go test -bench=.
コマンドなどで取得されるパフォーマンス測定値です。
ns/op
(nanoseconds per operation): 1回の操作にかかる平均ナノ秒。この値が小さいほど、パフォーマンスが良いことを示します。delta
: 変更前(old)と変更後(new)のパフォーマンスの差をパーセンテージで示します。負の値はパフォーマンスの向上(時間が短縮された)、正の値はパフォーマンスの低下(時間が長くなった)を示します。
技術的詳細
このコミットの核心は、appendCrossover
の値を0
に設定することです。
従来のGoランタイムでは、appendCrossover
が16
に設定されていました。これは、16バイト以下の追加操作ではバイト単位のコピーを、16バイトを超える追加操作ではmemmove
を使用するという意味です。しかし、Intelプロセッサの進化により、小規模なメモリコピーにおいてもmemmove
のような最適化されたルーチンが非常に効率的になっていました。むしろ、appendCrossover
の条件分岐や、バイト単位のループ処理のオーバーヘッドが、memmove
の呼び出しオーバーヘッドよりも大きくなるケースが発生していました。
appendCrossover = 0
と設定することで、runtime·appendslice
およびruntime·appendstr
関数内の条件分岐if(w <= appendCrossover)
が常に偽(w > 0
の場合)となり、結果として常にelse
ブロックのmemmove
相当の処理が実行されるようになります。
ただし、slice.c
の変更を見ると、if(appendCrossover > 0 && w <= appendCrossover)
という条件に変わっています。これは、appendCrossover
が0
の場合には、この条件全体がfalse
となり、常にelse
ブロック(memmove
相当の処理)が実行されることを意味します。つまり、appendCrossover
が0
に設定されたプラットフォームでは、追加されるデータのサイズに関わらず、常に最適化されたメモリコピー関数が使用されるようになります。
この変更により、特に小規模なデータ(1バイトから16バイト程度)の追加において、条件分岐のオーバーヘッドや非効率なループ処理が回避され、memmove
による高速なコピーが常に適用されるため、パフォーマンスが大幅に向上します。ベンチマーク結果からも、BenchmarkAppend4Bytes
からBenchmarkAppend16Bytes
、および対応する文字列ベンチマークで顕著な改善が見られることが確認できます。
コアとなるコードの変更箇所
このコミットでは、以下の4つのファイルが変更されています。
src/pkg/runtime/append_test.go
: ベンチマークテストの追加。src/pkg/runtime/arch_386.h
: 386アーキテクチャのappendCrossover
の定義変更。src/pkg/runtime/arch_amd64.h
: amd64アーキテクチャのappendCrossover
の定義変更。src/pkg/runtime/slice.c
:append
操作の内部ロジックの変更。
src/pkg/runtime/append_test.go
--- a/src/pkg/runtime/append_test.go
+++ b/src/pkg/runtime/append_test.go
@@ -38,10 +38,18 @@ func BenchmarkAppend4Bytes(b *testing.B) {
benchmarkAppendBytes(b, 4)
}
+func BenchmarkAppend7Bytes(b *testing.B) {\n\tbenchmarkAppendBytes(b, 7)\n}\n+\n func BenchmarkAppend8Bytes(b *testing.B) {
benchmarkAppendBytes(b, 8)
}
+func BenchmarkAppend15Bytes(b *testing.B) {\n\tbenchmarkAppendBytes(b, 15)\n}\n+\n func BenchmarkAppend16Bytes(b *testing.B) {
benchmarkAppendBytes(b, 16)
}
このファイルでは、BenchmarkAppend7Bytes
とBenchmarkAppend15Bytes
という新しいベンチマーク関数が追加されています。これは、appendCrossover
の閾値付近のパフォーマンスをより詳細に測定するために導入されたものです。
src/pkg/runtime/arch_386.h
--- a/src/pkg/runtime/arch_386.h
+++ b/src/pkg/runtime/arch_386.h
@@ -6,6 +6,6 @@ enum {
thechar = '8',
BigEndian = 0,
CacheLineSize = 64,
- appendCrossover = 16,
+ appendCrossover = 0,
PCQuantum = 1
};
386アーキテクチャの定義ファイルで、appendCrossover
の値が16
から0
に変更されています。
src/pkg/runtime/arch_amd64.h
--- a/src/pkg/runtime/arch_amd64.h
+++ b/src/pkg/runtime/arch_amd64.h
@@ -6,6 +6,6 @@ enum {
thechar = '6',
BigEndian = 0,
CacheLineSize = 64,
- appendCrossover = 16,
+ appendCrossover = 0,
PCQuantum = 1
};
amd64アーキテクチャの定義ファイルで、appendCrossover
の値が16
から0
に変更されています。
src/pkg/runtime/slice.c
--- a/src/pkg/runtime/slice.c
+++ b/src/pkg/runtime/slice.c
@@ -95,7 +95,7 @@ runtime·appendslice(SliceType *t, Slice x, Slice y, Slice ret)
p = ret.array+ret.len*w;
q = y.array;
w *= y.len;
- if(w <= appendCrossover) {
+ if(appendCrossover > 0 && w <= appendCrossover) {
if(p <= q || w <= p-q) // No overlap.
while(w-- > 0)
*p++ = *q++;
@@ -148,7 +148,7 @@ runtime·appendstr(SliceType *t, Slice x, String y, Slice ret)
w = y.len;
p = ret.array+ret.len;
q = y.str;
- if(w <= appendCrossover) {
+ if(appendCrossover > 0 && w <= appendCrossover) {
while(w-- > 0)
*p++ = *q++;
} else {
runtime·appendslice
とruntime·appendstr
関数の内部ロジックが変更されています。条件分岐がif(w <= appendCrossover)
からif(appendCrossover > 0 && w <= appendCrossover)
に変更されました。
コアとなるコードの解説
src/pkg/runtime/slice.c
における変更が、このコミットの動作の鍵となります。
変更前のコード:
if(w <= appendCrossover) {
// バイト単位のコピー
} else {
// memmove相当の最適化されたコピー
}
変更後のコード:
if(appendCrossover > 0 && w <= appendCrossover) {
// バイト単位のコピー
} else {
// memmove相当の最適化されたコピー
}
この変更により、appendCrossover
が0
に設定されたamd64および386アーキテクチャでは、appendCrossover > 0
という条件が常にfalse
となるため、if
ブロック内のバイト単位のコピー処理が実行されなくなります。結果として、追加されるデータのサイズw
がどれほど小さくても、常にelse
ブロック内のmemmove
相当の最適化されたメモリコピー処理が実行されるようになります。
これは、現代のIntelプロセッサにおいて、memmove
のような最適化されたルーチンが非常に効率的であり、たとえ数バイトのコピーであっても、条件分岐やループのオーバーヘッドを考慮すると、そちらの方が高速であるという知見に基づいています。
ベンチマーク結果は、この変更が意図した効果をもたらしたことを明確に示しています。特に、BenchmarkAppend4Bytes
からBenchmarkAppend16Bytes
、および対応する文字列ベンチマークにおいて、ns/op
が大幅に減少し、パフォーマンスが大きく向上していることがわかります。これは、これらの小規模な追加操作が、より効率的なmemmove
パスを通るようになったためです。
関連リンク
- Go Issue #4963: runtime: tune append crossover on amd64 and 386
- このコミットが修正したGoのIssueです。詳細な議論や背景情報が記載されている可能性があります。
- Goの
append
関数のドキュメント: https://pkg.go.dev/builtin#append - Goの内部的なスライス実装に関する記事(一般的な情報源)
参考にした情報源リンク
- Goのソースコード(特に
src/pkg/runtime/
ディレクトリ内のファイル) - GoのIssueトラッカー(#4963)
- Goのベンチマークに関する一般的な情報
- メモリコピー最適化に関する一般的なコンピュータアーキテクチャの知識
- Dave Cheney氏のブログや関連するGoのパフォーマンスに関する記事(もしあれば)
- (注: 特定のブログ記事への直接リンクは、検索結果に基づいて追加する必要がありますが、ここでは一般的な情報源として記載しています。)