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

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

このコミットは、Go言語のコンパイラ(cmd/gc)におけるランタイムパッケージ(runtime.go)の更新に関するものです。具体的には、Goのマップ(map)型の新しい内部実装に対応するために、マップアクセス用の最適化されたヘルパー関数が追加されています。これにより、特定のキー型(32ビット整数、64ビット整数、文字列)に対するマップ操作のパフォーマンスが向上しています。また、builtin.cという自動生成されるファイルの生成スクリプトに、自動生成であることを示すコメントを追加する変更も含まれています。

コミット

commit e3b767a5a1dd4f45b04de991e29cacdbe9f0d041
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Wed Mar 27 21:51:07 2013 +0100

    cmd/gc: update runtime.go for new map implementation.
    
    R=golang-dev, bradfitz, r
    CC=golang-dev
    https://golang.org/cl/8051044

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

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

元コミット内容

cmd/gc: update runtime.go for new map implementation.

R=golang-dev, bradfitz, r
CC=golang-dev
https://golang.org/cl/8051044

変更の背景

このコミットの主な背景は、Go言語のマップ(map)の内部実装が変更されたことにあります。Go 1.1のリリースに向けて、マップのパフォーマンスとメモリ効率を改善するための大規模な改修が行われました。これには、ハッシュテーブルの構造の最適化や、特定のキー型(整数や文字列)に対するアクセスパスの高速化が含まれます。

以前のマップ実装では、キーの型に関わらず汎用的なアクセス関数が使用されていましたが、これによりオーバーヘッドが生じていました。新しい実装では、コンパイラがキーの型を認識し、より特化した高速なアクセス関数を呼び出すことで、パフォーマンスのボトルネックを解消しようとしました。このコミットは、その新しいマップ実装がランタイムに提供する新しいインターフェース(関数群)をコンパイラが認識できるようにするためのものです。

また、src/cmd/gc/builtin.cが自動生成されるファイルであるにも関わらず、その旨が明示されていなかったため、mkbuiltinスクリプトに自動生成コメントを追加することで、開発者が手動で編集しないように注意を促す目的もあります。

前提知識の解説

Go言語のコンパイラとランタイム

  • cmd/gc: Go言語の公式コンパイラの一つで、Go 1.5までは主要なコンパイラでした。Goのソースコードを機械語に変換する役割を担います。
  • ランタイム(runtimeパッケージ): Goプログラムの実行をサポートする低レベルなコード群です。ガベージコレクション、スケジューラ、メモリ管理、そしてマップやチャネルといった組み込み型の内部実装などが含まれます。Goプログラムは、コンパイル時にランタイムの関数を呼び出すことで、これらの機能を利用します。

Goのマップ(map

Goのマップは、キーと値のペアを格納するハッシュテーブルとして実装されています。キーは一意であり、値に高速にアクセスするために使用されます。マップの内部実装は複雑で、ハッシュ関数の選択、衝突解決、リサイズ戦略などがパフォーマンスに大きく影響します。

builtin.cmkbuiltin

  • builtin.c: GoコンパイラがGoのランタイムパッケージをインポートする際に使用する、ランタイムの組み込み関数に関する情報を含むC言語のファイルです。これは手動で書かれるのではなく、mkbuiltinというスクリプトによって自動生成されます。
  • mkbuiltin: src/cmd/gcディレクトリにあるシェルスクリプトで、builtin.cファイルを生成する役割を担います。Goのランタイムパッケージの関数シグネチャなどを解析し、コンパイラが利用できる形式でCコードを生成します。

Goの型システムとany(旧interface{}

Goの関数シグネチャに見られるany(Go 1.18以降で導入されたinterface{}のエイリアス)は、任意の型を受け入れることができることを示します。マップのキーや値は任意の型になりうるため、汎用的なマップ操作関数ではany型が使用されます。しかし、特定のプリミティブ型(int32, int64, stringなど)は、その特性を活かしてより効率的な処理が可能です。

技術的詳細

このコミットの核となるのは、Goのマップ実装における「高速パス(fast path)」の導入です。

従来のマップアクセスでは、キーの型が何であっても、mapaccess1mapaccess2といった汎用的なランタイム関数が呼び出されていました。これらの関数は、any型のキーを処理するために、型アサーションやリフレクションに似た内部的な処理を必要とし、これがオーバーヘッドとなっていました。

新しいマップ実装では、コンパイラがコンパイル時にキーの具体的な型(例: int32, int64, string)を特定できる場合、その型に特化したランタイム関数を直接呼び出すように変更されました。このコミットで追加されたmapaccess1_fast32, mapaccess1_fast64, mapaccess1_faststr、およびmapaccess2_fast32, mapaccess2_fast64, mapaccess2_faststrがこれらの「高速パス」を提供する関数です。

  • mapaccess1_fastX: マップから値を取得する(キーが存在しない場合はゼロ値を返す)ための高速パス。val *anyを返すのは、値のポインタを直接返すことで、値のコピーを避け、さらに効率的なアクセスや更新を可能にするためと考えられます。
  • mapaccess2_fastX: マップから値と、キーが存在するかどうかの真偽値(pres)を取得するための高速パス。

これらの関数は、特定のキー型に特化しているため、型チェックや汎用的なハッシュ計算のオーバーヘッドを削減し、より直接的なメモリアクセスやハッシュ計算ロジックを実行できます。これにより、特に頻繁にマップアクセスが行われるアプリケーションにおいて、顕著なパフォーマンス向上が期待されます。

src/cmd/gc/mkbuiltinの変更は、_builtin.cbuiltin.cの内部名)の先頭に// AUTO-GENERATED by mkbuiltin; DO NOT EDITというコメントを追加するものです。これは、このファイルがスクリプトによって自動生成されるものであり、手動で変更すべきではないことを開発者に明確に伝えるための標準的なプラクティスです。これにより、将来的なビルドプロセスでの混乱や、手動変更によるバグの発生を防ぎます。

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

src/cmd/gc/builtin.c

--- a/src/cmd/gc/builtin.c
+++ b/src/cmd/gc/builtin.c
@@ -1,3 +1,4 @@
+// AUTO-GENERATED by mkbuiltin; DO NOT EDIT
 char *runtimeimport =
 	"package runtime\n"
 	"import runtime \"runtime\"\n"

builtin.cの先頭に自動生成コメントが追加されています。

src/cmd/gc/mkbuiltin

--- a/src/cmd/gc/mkbuiltin
+++ b/src/cmd/gc/mkbuiltin
@@ -19,6 +19,7 @@ fi
 GC=${GOCHAR}g
 gcc -o mkbuiltin1 mkbuiltin1.c
 rm -f _builtin.c
+echo "// AUTO-GENERATED by mkbuiltin; DO NOT EDIT" >>_builtin.c
 for i in runtime unsafe
 do
 	go tool $GC -A $i.go

mkbuiltinスクリプトに、_builtin.cファイルを生成する際に自動生成コメントを書き込むechoコマンドが追加されています。

src/cmd/gc/runtime.go

--- a/src/cmd/gc/runtime.go
+++ b/src/cmd/gc/runtime.go
@@ -89,7 +89,13 @@ func equal(typ *byte, x1, x2 any) (ret bool)
 // *byte is really *runtime.Type
 func makemap(mapType *byte, hint int64) (hmap map[any]any)
 func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any)
+func mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any)
+func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any)
+func mapaccess1_faststr(mapType *byte, hmap map[any]any, key any) (val *any)
 func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool)
+func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
+func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
+func mapaccess2_faststr(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
 func mapassign1(mapType *byte, hmap map[any]any, key any, val any)
 func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
 func mapdelete(mapType *byte, hmap map[any]any, key any)

runtime.goに、新しいマップ実装で導入された高速マップアクセス関数(mapaccess1_fast32, mapaccess1_fast64, mapaccess1_faststr, mapaccess2_fast32, mapaccess2_fast64, mapaccess2_faststr)の宣言が追加されています。これらの関数は、コンパイラが特定のキー型に対して直接呼び出すためのものです。

コアとなるコードの解説

このコミットの最も重要な部分は、src/cmd/gc/runtime.goに追加されたmapaccessX_fastY関数群の宣言です。

  • func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any): これは汎用的なマップアクセス関数で、キーの型に関わらず使用されます。
  • func mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any): 32ビット整数(int32など)をキーとするマップから値を取得するための高速パスです。val *anyを返すことで、値のポインタを直接操作できる可能性があります。
  • func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any): 64ビット整数(int64など)をキーとするマップから値を取得するための高速パスです。
  • func mapaccess1_faststr(mapType *byte, hmap map[any]any, key any) (val *any): 文字列(string)をキーとするマップから値を取得するための高速パスです。
  • func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool): 汎用的なマップアクセス関数で、値とキーの存在有無を返します。
  • func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres bool): 32ビット整数キーのマップから値と存在有無を取得する高速パスです。
  • func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool): 64ビット整数キーのマップから値と存在有無を取得する高速パスです。
  • func mapaccess2_faststr(mapType *byte, hmap map[any]any, key any) (val *any, pres bool): 文字列キーのマップから値と存在有無を取得する高速パスです。

これらの関数は、Goコンパイラがマップ操作を最適化する際に利用する「フック」のようなものです。コンパイラは、コードを解析し、マップのキーがint32int64、またはstringであることが判明した場合、従来の汎用関数ではなく、これらの_fast関数を呼び出すようにコードを生成します。これにより、ランタイムでの型チェックやインターフェースのオーバーヘッドが削減され、マップアクセスの実行速度が向上します。

builtin.cmkbuiltinの変更は、このruntime.goの変更とは直接的な機能変更ではありませんが、Goのビルドシステムにおけるベストプラクティスを適用するものです。builtin.cはGoコンパイラがランタイムの組み込み関数を認識するために必要な情報を提供するため、その生成プロセスが明確にされることは、開発の健全性を保つ上で重要です。

関連リンク

  • Go 1.1 Release Notes (Maps section): Go 1.1のリリースノートには、マップのパフォーマンス改善に関する言及があるはずです。
  • Goのマップ実装に関する設計ドキュメントや議論(もし公開されていれば):Goのマップの内部構造や最適化に関する詳細な情報源。

参考にした情報源リンク

参考にした情報源リンク

  • Go Change List (CL) 8051044: コミットメッセージに記載されているhttps://golang.org/cl/8051044は、Goプロジェクトのコードレビューシステムにおける変更リストへのリンクですが、この番号は非常に古く、現在のシステムでは直接アクセスできない可能性があります。Go 1.1リリース時のマップ実装変更に関する詳細な議論やコードレビューがここで行われていたと考えられますが、現時点ではその内容を直接参照することは困難です。
  • Go言語公式ドキュメント: Go言語のマップに関する基本的な情報や、その内部実装の概要については、Go言語の公式ドキュメントやブログ記事が参考になります。特に、Go 1.1のリリースノートや、マップのパフォーマンス改善に関する記事が関連する情報源となります。
  • Go言語のマップ実装に関する技術記事: Goのマップの内部構造や最適化手法について解説している技術ブログや論文も、このコミットの背景を深く理解する上で役立ちます。
    • "Go's map implementation" (Goのマップ実装に関する一般的な解説記事)
    • "Optimizing Go maps" (Goマップの最適化に関する記事)