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

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

このコミットは、Go言語の標準ライブラリであるbytesパッケージに、ARMアーキテクチャ向けのアセンブリ最適化されたEqual関数の実装を追加するものです。これにより、特にARMプロセッサ上でのバイトスライス比較のパフォーマンスが大幅に向上しています。

コミット

commit d472d3faf17490e1c9b1c38d78ebe65baead30fa
Author: Dave Cheney <dave@cheney.net>
Date:   Wed May 2 12:10:24 2012 +1000

    bytes: add assembly version of Equal for ARM
    
    BenchmarkEqual32                       662          159  -75.98%
    BenchmarkEqual4K                     76545        13719  -82.08%
    BenchmarkEqual4M                  90136700     23588870  -73.83%
    BenchmarkEqual64M               2147483647   1419616000  -42.63%
    
    BenchmarkEqual32                     48.32       201.15    4.16x
    BenchmarkEqual4K                     53.51       298.56    5.58x
    BenchmarkEqual4M                     46.53       177.81    3.82x
    BenchmarkEqual64M                    27.12        47.27    1.74x
    
    R=golang-dev, qyzhai, minux.ma, rsc, iant, nigeltao
    CC=golang-dev
    https://golang.org/cl/6118049

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

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

元コミット内容

このコミットは、Go言語のbytesパッケージ内のEqual関数に、ARMアーキテクチャ向けのアセンブリ言語による最適化バージョンを追加するものです。コミットメッセージには、ベンチマーク結果が示されており、様々なサイズのバイトスライス(32バイト、4KB、4MB、64MB)に対するEqual関数の実行時間が大幅に改善されていることがわかります。

具体的には、以下のパフォーマンス向上が報告されています。

  • BenchmarkEqual32: 実行時間が75.98%削減され、4.16倍高速化。
  • BenchmarkEqual4K: 実行時間が82.08%削減され、5.58倍高速化。
  • BenchmarkEqual4M: 実行時間が73.83%削減され、3.82倍高速化。
  • BenchmarkEqual64M: 実行時間が42.63%削減され、1.74倍高速化。

これらの結果は、特に小規模から中規模のバイトスライスにおいて顕著な改善が見られることを示しています。

変更の背景

Go言語は、クロスプラットフォーム対応を重視しており、様々なCPUアーキテクチャで動作します。しかし、汎用的なGoコードは、特定のアーキテクチャの特性を最大限に活かせない場合があります。特に、頻繁に実行される低レベルの操作(例: バイトスライスの比較、メモリコピーなど)では、アセンブリ言語による最適化がパフォーマンスに大きな影響を与えることがあります。

bytes.Equal関数は、2つのバイトスライスが等しいかどうかを比較する基本的な操作であり、多くのGoプログラムで内部的に利用されます。例えば、ネットワークプロトコルの処理、ファイルI/O、ハッシュ計算など、バイトスライスの比較がボトルネックとなるシナリオは多岐にわたります。

このコミットの背景には、ARMアーキテクチャ上でのGoプログラムの実行性能を向上させるという明確な目的があります。当時、ARMプロセッサはモバイルデバイスや組み込みシステムで広く採用され始めており、Go言語がこれらのプラットフォームでより効率的に動作することが求められていました。アセンブリ言語を使用することで、CPUのレジスタを直接操作し、ループの最適化や分岐予測の改善など、Goコンパイラが生成するコードでは実現が難しい低レベルの最適化が可能になります。

前提知識の解説

Go言語のbytesパッケージ

bytesパッケージは、バイトスライス([]byte)を操作するためのユーティリティ関数を提供します。Equal関数は、このパッケージの基本的な機能の一つで、2つのバイトスライスの内容が完全に一致するかどうかを効率的に比較します。

アセンブリ言語とGo

Go言語は通常、Goコンパイラによって機械語にコンパイルされますが、パフォーマンスがクリティカルな部分では、特定のアセンブリ言語で書かれたコードをGoプログラムに組み込むことができます。Goのアセンブリは、Plan 9アセンブラの文法に基づいており、一般的なx86やARMのアセンブリとは異なる独自の記法を持ちます。

  • TEXT: 関数の開始を宣言します。例: TEXT ·Equal(SB),7,$0 は、Equalという名前の関数を定義し、スタックフレームサイズや引数のオフセットなどを指定します。
  • SB (Static Base): グローバルシンボルや外部シンボルを参照するための擬似レジスタです。
  • FP (Frame Pointer): 現在のスタックフレームの引数やローカル変数を参照するための擬似レジスタです。
  • レジスタ: ARMプロセッサには、R0からR15までの汎用レジスタがあります。Goのアセンブリでは、これらのレジスタを直接使用してデータを操作します。
  • 命令:
    • MOVW: ワード(4バイト)を移動します。
    • CMP: 2つのオペランドを比較し、フラグレジスタを設定します。
    • B.NE (Branch if Not Equal): 比較結果が等しくない場合に分岐します。
    • B.EQ (Branch if Equal): 比較結果が等しい場合に分岐します。
    • ADD: 加算を行います。
    • MOVBU.P: バイトをロードし、アドレスをインクリメントします(Post-indexed addressing)。これは、ループ内で配列やスライスを効率的に走査する際に使用されます。
    • RET: 関数から戻ります。

ベンチマーク

Go言語には、標準でベンチマーク機能が組み込まれています。go test -bench=.コマンドを実行することで、Benchmarkプレフィックスを持つ関数が実行され、そのパフォーマンスが測定されます。コミットメッセージに記載されているベンチマーク結果は、この機能によって得られたものです。

技術的詳細

このコミットで追加されたアセンブリコードは、bytes.Equal関数のARMアーキテクチャ向けの実装です。Goのbytes.Equalは、通常、2つのバイトスライスの長さが同じであるかを確認し、その後、各要素を順に比較していきます。アセンブリ実装では、この比較処理をCPUのレジスタと命令を直接使って最適化します。

アセンブリコードの主な最適化ポイントは以下の通りです。

  1. 長さの比較: まず、2つのバイトスライスの長さが等しいかを確認します。長さが異なる場合、それらは等しくないため、すぐに結果を返します。これはGoの汎用実装でも行われますが、アセンブリではより直接的にレジスタ操作で行われます。
  2. バイトごとの比較ループ: 長さが同じ場合、アセンブリコードはバイトごとに比較するループに入ります。
    • MOVBU.P命令を使用することで、メモリからバイトをロードすると同時に、ポインタを次のバイトに自動的に進めることができます。これにより、ループ内の命令数を減らし、効率を高めます。
    • 比較結果が等しくない場合、すぐにループを終了し、falseを返します。
    • すべてのバイトが比較され、すべてが等しい場合、ループを終了し、trueを返します。
  3. レジスタの活用: 頻繁にアクセスされるデータ(スライスのアドレス、長さ、現在のポインタなど)をCPUレジスタに保持することで、メモリへのアクセス回数を減らし、処理速度を向上させます。

このアセンブリ実装は、Goコンパイラが生成する汎用的なループよりも、ARMプロセッサのパイプライン処理やキャッシュ効率をより考慮した設計になっています。特に、MOVBU.Pのようなポストインデックスアドレッシングモードは、連続したメモリ領域へのアクセスを最適化する上で非常に有効です。

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

変更はsrc/pkg/bytes/asm_arm.sファイルに対して行われています。

--- a/src/pkg/bytes/asm_arm.s
+++ b/src/pkg/bytes/asm_arm.s
@@ -27,4 +27,30 @@ _notfound:
 	RET
 
 TEXT ·Equal(SB),7,$0
-	B	·equalPortable(SB)
+	MOVW	alen+4(FP), R1
+	MOVW	blen+16(FP), R3
+	
+	CMP	R1, R3		// unequal lengths are not equal
+	B.NE	_notequal
+
+	MOVW	aptr+0(FP), R0
+	MOVW	bptr+12(FP), R2
+	ADD	R0, R1		// end
+
+_next:
+	CMP	R0, R1
+	B.EQ	_equal		// reached the end
+	MOVBU.P	1(R0), R4
+	MOVBU.P	1(R2), R5
+	CMP	R4, R5
+	B.EQ	_next
+
+_notequal:
+	MOVW	$0, R0
+	MOVW	R0, equal+24(FP)
+	RET
+
+_equal:
+	MOVW	$1, R0
+	MOVW	R0, equal+24(FP)
+	RET

この差分は、Equal関数の実装が、以前のB ·equalPortable(SB)(Goで書かれたポータブルな実装への分岐)から、ARMアセンブリによる直接的な実装に置き換えられたことを示しています。

コアとなるコードの解説

追加されたARMアセンブリコードのEqual関数は以下のロジックで動作します。

TEXT ·Equal(SB),7,$0
	MOVW	alen+4(FP), R1  // スライスaの長さをR1にロード
	MOVW	blen+16(FP), R3 // スライスbの長さをR3にロード
	
	CMP	R1, R3		// R1 (aの長さ) と R3 (bの長さ) を比較
	B.NE	_notequal	// 長さが異なる場合、_notequalへ分岐(falseを返す)

	MOVW	aptr+0(FP), R0  // スライスaの先頭アドレスをR0にロード
	MOVW	bptr+12(FP), R2 // スライスbの先頭アドレスをR2にロード
	ADD	R0, R1		// R0 (aの先頭アドレス) に R1 (aの長さ) を加算し、R1に格納。これはスライスaの終端アドレスとなる。

_next:
	CMP	R0, R1		// R0 (現在のaのポインタ) と R1 (aの終端アドレス) を比較
	B.EQ	_equal		// ポインタが終端に達した場合、_equalへ分岐(trueを返す)
	MOVBU.P	1(R0), R4	// R0が指すアドレスから1バイトをR4にロードし、R0を1バイト進める
	MOVBU.P	1(R2), R5	// R2が指すアドレスから1バイトをR5にロードし、R2を1バイト進める
	CMP	R4, R5		// R4 (aのバイト) と R5 (bのバイト) を比較
	B.EQ	_next		// バイトが等しい場合、_nextへ分岐して次のバイトを比較

_notequal:
	MOVW	$0, R0		// R0に0をロード(falseを表す)
	MOVW	R0, equal+24(FP) // 戻り値の場所にR0の値を格納
	RET			// 関数から戻る

_equal:
	MOVW	$1, R0		// R0に1をロード(trueを表す)
	MOVW	R0, equal+24(FP) // 戻り値の場所にR0の値を格納
	RET			// 関数から戻る

詳細な解説:

  1. 引数のロード:

    • alen+4(FP)blen+16(FP)は、スタックフレーム上の引数(スライスの長さ)へのオフセットを示します。Goのスライスは、ポインタ、長さ、容量の3つの要素で構成されるため、それぞれの引数(ab)の長さフィールドにアクセスするためにオフセットが使用されます。
    • aptr+0(FP)bptr+12(FP)は、同様にスライスの先頭ポインタへのオフセットです。
  2. 長さの比較:

    • CMP R1, R3で2つのスライスの長さを比較します。
    • B.NE _notequalは、長さが等しくない(Not Equal)場合に、_notequalラベルにジャンプし、falseを返します。
  3. ループの初期化:

    • ADD R0, R1は、スライスaの先頭アドレス(R0)に長さ(R1)を加算し、その結果をR1に格納します。これにより、R1はスライスaの終端アドレス(比較を終了するアドレス)として機能します。
  4. バイトごとの比較ループ (_next):

    • CMP R0, R1: 現在のポインタR0が終端アドレスR1に達したかどうかをチェックします。
    • B.EQ _equal: ポインタが終端に達した場合(すべてのバイトが比較され、等しかった場合)、_equalラベルにジャンプし、trueを返します。
    • MOVBU.P 1(R0), R4: R0が指すメモリ位置から符号なしバイトをR4レジスタにロードし、同時にR0レジスタの値を1だけインクリメントします。これは「ポストインデックスアドレッシング」と呼ばれるもので、ループ内でポインタを自動的に進めるため、効率的です。
    • MOVBU.P 1(R2), R5: 同様に、R2が指すメモリ位置からバイトをR5にロードし、R2を1インクリメントします。
    • CMP R4, R5: ロードした2つのバイトを比較します。
    • B.EQ _next: 2つのバイトが等しい場合、_nextラベルにジャンプしてループを続行します。
  5. 結果の返却:

    • _notequal: 長さが異なるか、途中で異なるバイトが見つかった場合に到達します。R0に0(false)を設定し、戻り値の場所に格納して関数を終了します。
    • _equal: すべてのバイトが等しかった場合に到達します。R0に1(true)を設定し、戻り値の場所に格納して関数を終了します。

このアセンブリコードは、Goコンパイラが生成する一般的なループよりも、ARMプロセッサの命令セットとレジスタをより効率的に利用することで、バイトスライス比較のパフォーマンスを最大化しています。特に、MOVBU.P命令は、メモリからの連続的なデータロードとポインタの更新を単一の命令で行えるため、ループのオーバーヘッドを削減し、高速化に貢献しています。

関連リンク

  • Go CL 6118049: https://golang.org/cl/6118049 (このコミットに対応するGoのコードレビューシステム上のチェンジリスト)

参考にした情報源リンク

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

このコミットは、Go言語の標準ライブラリであるbytesパッケージに、ARMアーキテクチャ向けのアセンブリ最適化されたEqual関数の実装を追加するものです。これにより、特にARMプロセッサ上でのバイトスライス比較のパフォーマンスが大幅に向上しています。

コミット

commit d472d3faf17490e1c9b1c38d78ebe65baead30fa
Author: Dave Cheney <dave@cheney.net>
Date:   Wed May 2 12:10:24 2012 +1000

    bytes: add assembly version of Equal for ARM
    
    BenchmarkEqual32                       662          159  -75.98%
    BenchmarkEqual4K                     76545        13719  -82.08%
    BenchmarkEqual4M                  90136700     23588870  -73.83%
    BenchmarkEqual64M               2147483647   1419616000  -42.63%
    
    BenchmarkEqual32                     48.32       201.15    4.16x
    BenchmarkEqual4K                     53.51       298.56    5.58x
    BenchmarkEqual4M                     46.53       177.81    3.82x
    BenchmarkEqual64M                    27.12        47.27    1.74x
    
    R=golang-dev, qyzhai, minux.ma, rsc, iant, nigeltao
    CC=golang-dev
    https://golang.org/cl/6118049

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

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

元コミット内容

このコミットは、Go言語のbytesパッケージ内のEqual関数に、ARMアーキテクチャ向けのアセンブリ言語による最適化バージョンを追加するものです。コミットメッセージには、ベンチマーク結果が示されており、様々なサイズのバイトスライス(32バイト、4KB、4MB、64MB)に対するEqual関数の実行時間が大幅に改善されていることがわかります。

具体的には、以下のパフォーマンス向上が報告されています。

  • BenchmarkEqual32: 実行時間が75.98%削減され、4.16倍高速化。
  • BenchmarkEqual4K: 実行時間が82.08%削減され、5.58倍高速化。
  • BenchmarkEqual4M: 実行時間が73.83%削減され、3.82倍高速化。
  • BenchmarkEqual64M: 実行時間が42.63%削減され、1.74倍高速化。

これらの結果は、特に小規模から中規模のバイトスライスにおいて顕著な改善が見られることを示しています。

変更の背景

Go言語は、クロスプラットフォーム対応を重視しており、様々なCPUアーキテクチャで動作します。しかし、汎用的なGoコードは、特定のアーキテクチャの特性を最大限に活かせない場合があります。特に、頻繁に実行される低レベルの操作(例: バイトスライスの比較、メモリコピーなど)では、アセンブリ言語による最適化がパフォーマンスに大きな影響を与えることがあります。

bytes.Equal関数は、2つのバイトスライスが等しいかどうかを比較する基本的な操作であり、多くのGoプログラムで内部的に利用されます。例えば、ネットワークプロトコルの処理、ファイルI/O、ハッシュ計算など、バイトスライスの比較がボトルネックとなるシナリオは多岐にわたります。

このコミットの背景には、ARMアーキテクチャ上でのGoプログラムの実行性能を向上させるという明確な目的があります。当時、ARMプロセッサはモバイルデバイスや組み込みシステムで広く採用され始めており、Go言語がこれらのプラットフォームでより効率的に動作することが求められていました。アセンブリ言語を使用することで、CPUのレジスタを直接操作し、ループの最適化や分岐予測の改善など、Goコンパイラが生成するコードでは実現が難しい低レベルの最適化が可能になります。

前提知識の解説

Go言語のbytesパッケージ

bytesパッケージは、バイトスライス([]byte)を操作するためのユーティリティ関数を提供します。Equal関数は、このパッケージの基本的な機能の一つで、2つのバイトスライスの内容が完全に一致するかどうかを効率的に比較します。

アセンブリ言語とGo

Go言語は通常、Goコンパイラによって機械語にコンパイルされますが、パフォーマンスがクリティカルな部分では、特定のアセンブリ言語で書かれたコードをGoプログラムに組み込むことができます。Goのアセンブリは、Plan 9アセンブラの文法に基づいており、一般的なx86やARMのアセンブリとは異なる独自の記法を持ちます。

  • TEXT: 関数の開始を宣言します。例: TEXT ·Equal(SB),7,$0 は、Equalという名前の関数を定義し、スタックフレームサイズや引数のオフセットなどを指定します。
  • SB (Static Base): グローバルシンボルや外部シンボルを参照するための擬似レジスタです。
  • FP (Frame Pointer): 現在のスタックフレームの引数やローカル変数を参照するための擬似レジスタです。
  • レジスタ: ARMプロセッサには、R0からR15までの汎用レジスタがあります。Goのアセンブリでは、これらのレジスタを直接使用してデータを操作します。
  • 命令:
    • MOVW: ワード(4バイト)を移動します。
    • CMP: 2つのオペランドを比較し、フラグレジスタを設定します。
    • B.NE (Branch if Not Equal): 比較結果が等しくない場合に分岐します。
    • B.EQ (Branch if Equal): 比較結果が等しい場合に分岐します。
    • ADD: 加算を行います。
    • MOVBU.P: バイトをロードし、アドレスをインクリメントします(Post-indexed addressing)。これは、ループ内で配列やスライスを効率的に走査する際に使用されます。
    • RET: 関数から戻ります。

ベンチマーク

Go言語には、標準でベンチマーク機能が組み込まれています。go test -bench=.コマンドを実行することで、Benchmarkプレフィックスを持つ関数が実行され、そのパフォーマンスが測定されます。コミットメッセージに記載されているベンチマーク結果は、この機能によって得られたものです。

技術的詳細

このコミットで追加されたアセンブリコードは、bytes.Equal関数のARMアーキテクチャ向けの実装です。Goのbytes.Equalは、通常、2つのバイトスライスの長さが同じであるかを確認し、その後、各要素を順に比較していきます。アセンブリ実装では、この比較処理をCPUのレジスタと命令を直接使って最適化します。

アセンブリコードの主な最適化ポイントは以下の通りです。

  1. 長さの比較: まず、2つのバイトスライスの長さが等しいかを確認します。長さが異なる場合、それらは等しくないため、すぐに結果を返します。これはGoの汎用実装でも行われますが、アセンブリではより直接的にレジスタ操作で行われます。
  2. バイトごとの比較ループ: 長さが同じ場合、アセンブリコードはバイトごとに比較するループに入ります。
    • MOVBU.P命令を使用することで、メモリからバイトをロードすると同時に、ポインタを次のバイトに自動的に進めることができます。これにより、ループ内の命令数を減らし、効率を高めます。
    • 比較結果が等しくない場合、すぐにループを終了し、falseを返します。
    • すべてのバイトが比較され、すべてが等しい場合、ループを終了し、trueを返します。
  3. レジスタの活用: 頻繁にアクセスされるデータ(スライスのアドレス、長さ、現在のポインタなど)をCPUレジスタに保持することで、メモリへのアクセス回数を減らし、処理速度を向上させます。

このアセンブリ実装は、Goコンパイラが生成する汎用的なループよりも、ARMプロセッサのパイプライン処理やキャッシュ効率をより考慮した設計になっています。特に、MOVBU.Pのようなポストインデックスアドレッシングモードは、連続したメモリ領域へのアクセスを最適化する上で非常に有効です。

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

変更はsrc/pkg/bytes/asm_arm.sファイルに対して行われています。

--- a/src/pkg/bytes/asm_arm.s
+++ b/src/pkg/bytes/asm_arm.s
@@ -27,4 +27,30 @@ _notfound:
 	RET
 
 TEXT ·Equal(SB),7,$0
-	B	·equalPortable(SB)
+	MOVW	alen+4(FP), R1
+	MOVW	blen+16(FP), R3
+	
+	CMP	R1, R3		// unequal lengths are not equal
+	B.NE	_notequal
+
+	MOVW	aptr+0(FP), R0
+	MOVW	bptr+12(FP), R2
+	ADD	R0, R1		// end
+
+_next:
+	CMP	R0, R1
+	B.EQ	_equal		// reached the end
+	MOVBU.P	1(R0), R4
+	MOVBU.P	1(R2), R5
+	CMP	R4, R5
+	B.EQ	_next
+
+_notequal:
+	MOVW	$0, R0
+	MOVW	R0, equal+24(FP)
+	RET
+
+_equal:
+	MOVW	$1, R0
+	MOVW	R0, equal+24(FP)
+	RET

この差分は、Equal関数の実装が、以前のB ·equalPortable(SB)(Goで書かれたポータブルな実装への分岐)から、ARMアセンブリによる直接的な実装に置き換えられたことを示しています。

コアとなるコードの解説

追加されたARMアセンブリコードのEqual関数は以下のロジックで動作します。

TEXT ·Equal(SB),7,$0
	MOVW	alen+4(FP), R1  // スライスaの長さをR1にロード
	MOVW	blen+16(FP), R3 // スライスbの長さをR3にロード
	
	CMP	R1, R3		// R1 (aの長さ) と R3 (bの長さ) を比較
	B.NE	_notequal	// 長さが異なる場合、_notequalへ分岐(falseを返す)

	MOVW	aptr+0(FP), R0  // スライスaの先頭アドレスをR0にロード
	MOVW	bptr+12(FP), R2 // スライスbの先頭アドレスをR2にロード
	ADD	R0, R1		// R0 (aの先頭アドレス) に R1 (aの長さ) を加算し、R1に格納。これはスライスaの終端アドレスとなる。

_next:
	CMP	R0, R1		// R0 (現在のaのポインタ) と R1 (aの終端アドレス) を比較
	B.EQ	_equal		// ポインタが終端に達した場合、_equalへ分岐(trueを返す)
	MOVBU.P	1(R0), R4	// R0が指すアドレスから1バイトをR4にロードし、R0を1バイト進める
	MOVBU.P	1(R2), R5	// R2が指すアドレスから1バイトをR5にロードし、R2を1バイト進める
	CMP	R4, R5		// R4 (aのバイト) と R5 (bのバイト) を比較
	B.EQ	_next		// バイトが等しい場合、_nextへ分岐して次のバイトを比較

_notequal:
	MOVW	$0, R0		// R0に0をロード(falseを表す)
	MOVW	R0, equal+24(FP) // 戻り値の場所にR0の値を格納
	RET			// 関数から戻る

_equal:
	MOVW	$1, R0		// R0に1をロード(trueを表す)
	MOVW	R0, equal+24(FP) // 戻り値の場所にR0の値を格納
	RET			// 関数から戻る

詳細な解説:

  1. 引数のロード:

    • alen+4(FP)blen+16(FP)は、スタックフレーム上の引数(スライスの長さ)へのオフセットを示します。Goのスライスは、ポインタ、長さ、容量の3つの要素で構成されるため、それぞれの引数(ab)の長さフィールドにアクセスするためにオフセットが使用されます。
    • aptr+0(FP)bptr+12(FP)は、同様にスライスの先頭ポインタへのオフセットです。
  2. 長さの比較:

    • CMP R1, R3で2つのスライスの長さを比較します。
    • B.NE _notequalは、長さが等しくない(Not Equal)場合に、_notequalラベルにジャンプし、falseを返します。
  3. ループの初期化:

    • ADD R0, R1は、スライスaの先頭アドレス(R0)に長さ(R1)を加算し、その結果をR1に格納します。これにより、R1はスライスaの終端アドレス(比較を終了するアドレス)として機能します。
  4. バイトごとの比較ループ (_next):

    • CMP R0, R1: 現在のポインタR0が終端アドレスR1に達したかどうかをチェックします。
    • B.EQ _equal: ポインタが終端に達した場合(すべてのバイトが比較され、等しかった場合)、_equalラベルにジャンプし、trueを返します。
    • MOVBU.P 1(R0), R4: R0が指すメモリ位置から符号なしバイトをR4レジスタにロードし、同時にR0レジスタの値を1だけインクリメントします。これは「ポストインデックスアドレッシング」と呼ばれるもので、ループ内でポインタを自動的に進めるため、効率的です。
    • MOVBU.P 1(R2), R5: 同様に、R2が指すメモリ位置からバイトをR5にロードし、R2を1インクリメントします。
    • CMP R4, R5: ロードした2つのバイトを比較します。
    • B.EQ _next: 2つのバイトが等しい場合、_nextラベルにジャンプしてループを続行します。
  5. 結果の返却:

    • _notequal: 長さが異なるか、途中で異なるバイトが見つかった場合に到達します。R0に0(false)を設定し、戻り値の場所に格納して関数を終了します。
    • _equal: すべてのバイトが等しかった場合に到達します。R0に1(true)を設定し、戻り値の場所に格納して関数を終了します。

このアセンブリコードは、Goコンパイラが生成する一般的なループよりも、ARMプロセッサの命令セットとレジスタをより効率的に利用することで、バイトスライス比較のパフォーマンスを最大化しています。特に、MOVBU.P命令は、メモリからの連続的なデータロードとポインタの更新を単一の命令で行えるため、ループのオーバーヘッドを削減し、高速化に貢献しています。

関連リンク

  • Go CL 6118049: https://golang.org/cl/6118049 (このコミットに対応するGoのコードレビューシステム上のチェンジリスト)

参考にした情報源リンク