[インデックス 11076] ファイルの概要
このコミットは、GoランタイムがFreeBSDシステム上で利用可能なCPUコア数を正確に検出できるようにするための変更を導入しています。具体的には、FreeBSDのsysctl
システムコールを利用してhw.ncpu
の値を読み取り、Goランタイムの内部変数runtime.ncpu
に設定する機能を追加しています。これにより、GoプログラムがFreeBSD上でより効率的にリソースを利用できるようになります。
コミット
commit 12bf00054e7bab156edd793fed97c61ba212389a
Author: Devon H. O'Dell <devon.odell@gmail.com>
Date: Tue Jan 10 17:39:17 2012 +1100
runtime: enable runtime.ncpu on FreeBSD
R=adg, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/5528062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/12bf00054e7bab156edd793fed97c61ba212389a
元コミット内容
runtime: enable runtime.ncpu on FreeBSD
R=adg, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/5528062
変更の背景
Goランタイムは、プログラムの並行処理を最適化するために、システムが利用可能なCPUコア数(論理プロセッサ数)を認識する必要があります。この情報は、Goのスケジューラがゴルーチンを効率的にM(OSスレッド)にマッピングし、GOMAXPROCS環境変数のデフォルト値を決定する際に重要となります。
FreeBSDシステムでは、以前のGoランタイムはCPUコア数を正しく取得できていませんでした。その結果、GoプログラムがFreeBSD上で実行される際に、利用可能なCPUリソースを最大限に活用できない可能性がありました。このコミットは、FreeBSD固有のシステムコールであるsysctl
を使用してCPUコア数を取得するメカニズムを導入することで、この問題を解決し、FreeBSD上でのGoプログラムのパフォーマンスとリソース利用効率を向上させることを目的としています。
前提知識の解説
Goランタイムとスケジューラ
Go言語は、独自のランタイムとスケジューラを持っています。これは、GoプログラムがOSスレッド(M)上でゴルーチン(G)を効率的に実行するためのものです。スケジューラは、P(プロセッサ、論理CPU)という抽象概念を用いて、ゴルーチンをMに割り当てます。runtime.ncpu
は、このPの数を決定する上で重要な役割を果たします。デフォルトでは、GOMAXPROCS
はruntime.ncpu
の値に設定されます。
sysctl
システムコール
sysctl
は、Unix系オペレーティングシステム(FreeBSD、Linux、macOSなど)でカーネルのパラメータを動的に読み書きするためのシステムコールです。システムに関する様々な情報(ハードウェア情報、ネットワーク設定、メモリ情報など)を、実行中のカーネルから取得したり、設定したりするために使用されます。
sysctl
は通常、名前空間によって階層的に整理された変数にアクセスします。例えば、FreeBSDではCPUコア数はhw.ncpu
という名前でアクセスできます。この名前は、CTL_HW
(ハードウェア関連)とHW_NCPU
(CPU数)という2つの整数値の配列(MIB: Management Information Base)として表現されます。
アセンブリ言語とシステムコール
Goランタイムは、OS固有の低レベルな処理(システムコールなど)を実行するために、アセンブリ言語を使用することがあります。これは、C言語などの高水準言語では直接アクセスできないOSの機能を利用するため、またはパフォーマンス上の理由からです。システムコールは、ユーザー空間のプログラムがカーネル空間の機能にアクセスするための唯一の手段です。
runtime.ncpu
runtime.ncpu
は、Goランタイムが認識するシステム上の論理CPUコア数を格納する内部変数です。この値は、Goのスケジューラが並行処理の度合いを決定する際に利用されます。
技術的詳細
このコミットの主要な技術的変更点は、FreeBSD上でsysctl
システムコールを呼び出すためのGoランタイムのサポートを追加したことです。
-
sysctl
システムコールのラッパー関数:src/pkg/runtime/os_freebsd.h
にruntime·sysctl
関数のプロトタイプが追加されました。これは、GoランタイムがC言語で書かれた部分からsysctl
を呼び出すための宣言です。src/pkg/runtime/sys_freebsd_386.s
とsrc/pkg/runtime/sys_freebsd_amd64.s
に、それぞれ32ビット(i386)と64ビット(amd64)アーキテクチャ向けのアセンブリ言語によるruntime·sysctl
の実装が追加されました。- これらのアセンブリコードは、
sys___sysctl
というFreeBSD固有のシステムコールを呼び出します。システムコール番号は202
です。 - 引数はスタックからレジスタにロードされ、システムコールが実行されます。
- システムコールの戻り値(成功/失敗)は、Goランタイムの規約に従って処理されます。
- これらのアセンブリコードは、
-
getncpu
関数の追加:src/pkg/runtime/thread_freebsd.c
にgetncpu
というC言語関数が追加されました。- この関数は、
CTL_HW
とHW_NCPU
というMIB(Management Information Base)配列を定義し、runtime·sysctl
を呼び出してhw.ncpu
の値を問い合わせます。 sysctl
の呼び出しは、mib
(問い合わせる情報の識別子)、namelen
(識別子の長さ)、oldp
(結果を格納するバッファ)、oldlenp
(バッファのサイズ)、newp
(設定する新しい値、今回はNULL)、newlen
(新しい値のサイズ、今回は0)を引数として取ります。getncpu
は、sysctl
が成功した場合は取得したCPU数を返し、失敗した場合はデフォルト値として1
を返します。
-
runtime.ncpu
の設定:src/pkg/runtime/thread_freebsd.c
のruntime·osinit
関数内で、getncpu()
が呼び出され、その戻り値がruntime·ncpu
に代入されます。runtime·osinit
は、Goランタイムが初期化される際にOS固有の初期化処理を行う関数です。これにより、Goプログラムが起動する際にFreeBSDのCPUコア数が正しく検出され、ランタイムに設定されるようになります。
コアとなるコードの変更箇所
src/pkg/runtime/os_freebsd.h
--- a/src/pkg/runtime/os_freebsd.h
+++ b/src/pkg/runtime/os_freebsd.h
@@ -1,12 +1,13 @@
#define SIG_DFL ((void*)0)
#define SIG_IGN ((void*)1)
-int32 runtime·thr_new(ThrParam*, int32);
-void runtime·sigpanic(void);
-void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
-struct sigaction;
-void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
+int32 runtime·thr_new(ThrParam*, int32);
+void runtime·sigpanic(void);
+void runtime·sigaltstack(Sigaltstack*, Sigaltstack*);
+struct sigaction;
+void runtime·sigaction(int32, struct sigaction*, struct sigaction*);
void runtiem·setitimerval(int32, Itimerval*, Itimerval*);
void runtime·setitimer(int32, Itimerval*, Itimerval*);
+int32 runtime·sysctl(uint32*, uint32, byte*, uintptr*, byte*, uintptr);
void runtime·raisesigpipe(void);
runtime·sysctl
関数のプロトタイプ宣言が追加されました。
src/pkg/runtime/sys_freebsd_386.s
--- a/src/pkg/runtime/sys_freebsd_386.s
+++ b/src/pkg/runtime/sys_freebsd_386.s
@@ -265,4 +265,23 @@ TEXT runtime·i386_set_ldt(SB),7,$16
INT $3
RET
+TEXT runtime·sysctl(SB),7,$28
+ LEAL arg0+0(FP), SI
+ LEAL 4(SP), DI
+ CLD
+ MOVSL // arg 1 - name
+ MOVSL // arg 2 - namelen
+ MOVSL // arg 3 - oldp
+ MOVSL // arg 4 - oldlenp
+ MOVSL // arg 5 - newp
+ MOVSL // arg 6 - newlen
+ MOVL $202, AX // sys___sysctl
+ INT $0x80
+ JCC 3(PC)
+ NEGL AX
+ RET
+ MOVL $0, AX
+ RET
+
+
GLOBL runtime·tlsoffset(SB),$4
i386アーキテクチャ向けにruntime·sysctl
のアセンブリ実装が追加されました。システムコール番号202
(sys___sysctl
)を呼び出しています。
src/pkg/runtime/sys_freebsd_amd64.s
--- a/src/pkg/runtime/sys_freebsd_amd64.s
+++ b/src/pkg/runtime/sys_freebsd_amd64.s
@@ -199,3 +199,19 @@ TEXT runtime·settls(SB),7,$8
JCC 2(PC)
CALL runtime·notok(SB)
RET
+
+TEXT runtime·sysctl(SB),7,$0
+ MOVQ 8(SP), DI // arg 1 - name
+ MOVL 16(SP), SI // arg 2 - namelen
+ MOVQ 24(SP), DX // arg 3 - oldp
+ MOVQ 32(SP), R10 // arg 4 - oldlenp
+ MOVQ 40(SP), R8 // arg 5 - newp
+ MOVQ 48(SP), R9 // arg 6 - newlen
+ MOVQ $202, AX // sys___sysctl
+ SYSCALL
+ JCC 3(PC)
+ NEGL AX
+ RET
+ MOVL $0, AX
+ RET
+
amd64アーキテクチャ向けにruntime·sysctl
のアセンブリ実装が追加されました。こちらもシステムコール番号202
(sys___sysctl
)を呼び出しています。
src/pkg/runtime/thread_freebsd.c
--- a/src/pkg/runtime/thread_freebsd.c
+++ b/src/pkg/runtime/thread_freebsd.c
@@ -9,6 +9,30 @@
extern SigTab runtime·sigtab[];
extern int32 runtime·sys_umtx_op(uint32*, int32, uint32, void*, void*);
+// From FreeBSD's <sys/sysctl.h>
+#define CTL_HW 6
+#define HW_NCPU 3
+
+static int32
+getncpu(void)
+{
+ uint32 mib[2];
+ uint32 out;
+ int32 ret;
+ uintptr nout;
+
+ // Fetch hw.ncpu via sysctl.
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ nout = sizeof out;
+ out = 0;
+ ret = runtime·sysctl(mib, 2, (byte*)&out, &nout, nil, 0);
+ if(ret >= 0)
+ return out;
+ else
+ return 1;
+}
+
// FreeBSD's umtx_op syscall is effectively the same as Linux's futex, and
// thus the code is largely similar. See linux/thread.c and lock_futex.c for comments.
@@ -81,6 +105,7 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))\n void
runtime·osinit(void)
{
+ runtime·ncpu = getncpu();
}
void
CTL_HW
とHW_NCPU
の定義、getncpu
関数の実装、そしてruntime·osinit
内でruntime·ncpu
にgetncpu()
の戻り値を設定する処理が追加されました。
コアとなるコードの解説
このコミットの核心は、FreeBSDのsysctl
システムコールをGoランタイムから呼び出し、システムのCPUコア数を取得するメカニズムを構築した点にあります。
-
runtime·sysctl
アセンブリ関数:- Goランタイムは、OSの低レベル機能にアクセスするために、アセンブリ言語で書かれた関数を使用します。
runtime·sysctl
は、FreeBSDのsys___sysctl
システムコールを直接呼び出すためのラッパーです。 - i386とamd64の両アーキテクチャで実装されており、それぞれがシステムコールを呼び出すための適切なレジスタ設定と命令(
INT $0x80
またはSYSCALL
)を使用しています。 - システムコールが成功した場合は0を返し、失敗した場合は負の値を返します。Goの規約では、システムコールエラーは負の値で表現されることが多いです。
- Goランタイムは、OSの低レベル機能にアクセスするために、アセンブリ言語で書かれた関数を使用します。
-
getncpu
C関数:- この関数は、
sysctl
システムコールを呼び出すための高レベルなインターフェースを提供します。 mib
配列は、sysctl
に問い合わせる情報のパスを指定します。CTL_HW
はハードウェア関連の情報を、HW_NCPU
はその中のCPU数を意味します。runtime·sysctl
を呼び出すことで、カーネルからhw.ncpu
の値を取得し、out
変数に格納します。- エラーハンドリングも含まれており、
sysctl
呼び出しが失敗した場合は、安全のためにデフォルト値の1
を返します。これは、CPU数が取得できない場合でも、Goランタイムが少なくとも1つのCPUコアがあるものとして動作を継続できるようにするためです。
- この関数は、
-
runtime·osinit
での統合:runtime·osinit
は、Goプログラムが起動し、ランタイムが初期化される際に一度だけ呼び出される関数です。- この関数内で
getncpu()
を呼び出し、その結果をグローバル変数runtime·ncpu
に代入することで、GoランタイムはFreeBSDシステムが持つ実際のCPUコア数を認識できるようになります。 - この
runtime·ncpu
の値は、Goのスケジューラがゴルーチンを効率的にOSスレッドに割り当て、並行処理の度合いを最適化するために利用されます。
この変更により、FreeBSD上でのGoプログラムは、システムのCPUリソースをより適切に利用し、パフォーマンスが向上することが期待されます。
関連リンク
- Go言語のランタイムとスケジューラに関する公式ドキュメントやブログ記事
- FreeBSDの
sysctl(3)
マニュアルページ - FreeBSDの
sysctl(8)
コマンドに関する情報
参考にした情報源リンク
- https://golang.org/cl/5528062 (元のGerritチェンジリスト)
- FreeBSD Handbook: https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/
sysctl
man page (FreeBSD):man sysctl
(ローカルシステムで実行)- Go runtime source code (GitHub): https://github.com/golang/go/tree/master/src/runtime
- Go scheduler documentation: https://go.dev/doc/effective_go#concurrency (Goの並行処理に関する一般的な情報)
- Go's
GOMAXPROCS
environment variable: https://go.dev/doc/go1.5#gomaxprocs (Go 1.5以降のGOMAXPROCSのデフォルト挙動に関する情報)