[インデックス 18832] ファイルの概要
このコミットは、GoランタイムがFreeBSD上でユーザー空間ミューテックス(umtx)を使用する方法を改善するものです。具体的には、UMTX_OP_WAIT_UINT
および UMTX_OP_WAKE
オペレーションを、それぞれ UMTX_OP_WAIT_UINT_PRIVATE
および UMTX_OP_WAKE_PRIVATE
に変更することで、より効率的で安全な同期メカニズムを実現しています。これにより、GoのランタイムがFreeBSDシステム上でスレッド同期を行う際のパフォーマンスと安定性が向上します。
コミット
commit ae9b661fa859222b9bcbcafc1a63f3f305385e75
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Wed Mar 12 10:33:09 2014 +0900
runtime: make use of THREAD_SHARE userspace mutex on freebsd
For now Note, futexsleep and futexwakeup are designed for threads,
not for processes. The explicit use of UMTX_OP_WAIT_UINT_PRIVATE and
UMTX_OP_WAKE_PRIVATE can avoid unnecessary traversals of VM objects,
to hit undiscovered bugs related to VM system on SMP/SMT/NUMA
environment.
Update #7496
LGTM=iant
R=golang-codereviews, gobot, iant, bradfitz
CC=golang-codereviews
https://golang.org/cl/72760043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ae9b661fa859222b9bcbcafc1a63f3f305385e75
元コミット内容
このコミットの元のメッセージは以下の通りです。
runtime: make use of THREAD_SHARE userspace mutex on freebsd
For now Note, futexsleep and futexwakeup are designed for threads,
not for processes. The explicit use of UMTX_OP_WAIT_UINT_PRIVATE and
UMTX_OP_WAKE_PRIVATE can avoid unnecessary traversals of VM objects,
to hit undiscovered bugs related to VM system on SMP/SMT/NUMA
environment.
Update #7496
LGTM=iant
R=golang-codereviews, gobot, iant, bradfitz
CC=golang-codereviews
https://golang.org/cl/72760043
変更の背景
この変更の背景には、FreeBSDにおけるユーザー空間ミューテックス(umtx)の利用に関する最適化と、それに伴う潜在的なバグの回避があります。
Goランタイムは、ゴルーチン(Goの軽量スレッド)のスケジューリングや同期のために、OSが提供する低レベルの同期プリミティブを利用します。FreeBSDでは、umtx
システムコールがこの役割を担っています。以前の実装では、UMTX_OP_WAIT_UINT
と UMTX_OP_WAKE
といった一般的なumtx操作が使用されていました。
しかし、コミットメッセージに記載されているように、futexsleep
と futexwakeup
(Goランタイム内で使用されるフューテックスに相当する機能) は、プロセス間ではなく、スレッド間の同期のために設計されています。一般的なumtx操作は、プロセス間同期も考慮しているため、仮想メモリ(VM)オブジェクトの不要なトラバーサル(走査)が発生する可能性があります。
特に、SMP(Symmetric Multi-Processing)、SMT(Simultaneous Multi-threading)、NUMA(Non-Uniform Memory Access)といったマルチプロセッサ環境では、VMシステムの複雑性が増し、このような不要なトラバーサルがパフォーマンスの低下や、未発見のバグ(特にVMシステムに関連するもの)を引き起こす可能性がありました。
この問題を解決するため、よりスレッドローカルな同期に特化した UMTX_OP_WAIT_UINT_PRIVATE
と UMTX_OP_WAKE_PRIVATE
の使用が提案されました。これらの_PRIVATE
オペレーションは、同じプロセス内のスレッド間でのみ有効であり、VMオブジェクトの広範な走査を避けることができます。これにより、同期操作のオーバーヘッドが削減され、マルチスレッド環境でのGoランタイムの安定性と効率が向上します。
この変更は、GoのIssue #7496に関連しており、FreeBSD上でのGoランタイムの堅牢性を高めることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の概念について理解しておく必要があります。
-
Goランタイム: Go言語で書かれたプログラムを実行するための環境です。ガベージコレクション、ゴルーチンのスケジューリング、チャネルによる通信など、Go言語の並行処理モデルを支える重要なコンポーネントです。ランタイムは、OSのシステムコールを直接利用して、低レベルの操作(メモリ管理、スレッド同期など)を行います。
-
フューテックス (futex): "Fast Userspace muTEX" の略で、LinuxやFreeBSDなどのUnix系OSで提供される、ユーザー空間での高速な同期プリミティブです。フューテックスは、競合がない場合にはカーネルへのシステムコールを発生させずにユーザー空間で同期を完結させ、競合が発生した場合にのみカーネルに介入を要求することで、高いパフォーマンスを実現します。これにより、ミューテックスやセマフォなどの同期メカニズムを効率的に実装できます。
-
FreeBSDのumtx (Userspace Mutex): FreeBSDにおけるフューテックスの実装です。
umtx
システムコールを通じて、ユーザー空間のスレッドが同期オブジェクト(ミューテックスなど)の状態を待機したり、他のスレッドを起こしたりすることができます。umtx
システムコールは、様々な操作(UMTX_OP_*
)をサポートしており、それぞれ異なる同期セマンティクスを提供します。 -
UMTX_OP_WAIT_UINT
とUMTX_OP_WAKE
: これらはumtxシステムコールで利用される一般的な操作です。UMTX_OP_WAIT_UINT
: 指定されたメモリアドレスの値が期待する値と異なる場合に、スレッドをスリープさせます。UMTX_OP_WAKE
: 指定されたメモリアドレスで待機しているスレッドをウェイクアップ(起こす)します。 これらの操作は、プロセス間同期にも利用できる汎用的なものです。
-
UMTX_OP_WAIT_UINT_PRIVATE
とUMTX_OP_WAKE_PRIVATE
: これらはumtxシステムコールで利用される_PRIVATE
版の操作です。UMTX_OP_WAIT_UINT_PRIVATE
:UMTX_OP_WAIT_UINT
と同様ですが、同じプロセス内のスレッド間でのみ有効です。UMTX_OP_WAKE_PRIVATE
:UMTX_OP_WAKE
と同様ですが、同じプロセス内のスレッド間でのみ有効です。_PRIVATE
版の操作は、プロセス間同期のオーバーヘッドを回避し、より高速なスレッド間同期を提供します。これは、カーネルがVMオブジェクトの広範な走査を行う必要がないためです。
-
仮想メモリ (VM) オブジェクト: オペレーティングシステムがプロセスに提供する仮想アドレス空間を管理するための内部データ構造です。プロセスがメモリにアクセスする際、OSはVMオブジェクトを使用して、仮想アドレスを物理アドレスにマッピングします。プロセス間同期では、共有メモリ領域が異なるプロセスのVMオブジェクトにマッピングされるため、カーネルはこれらのマッピングを考慮して同期操作を行う必要があります。
-
SMP/SMT/NUMA 環境:
- SMP (Symmetric Multi-Processing): 複数のCPUが同じメモリとI/Oバスを共有するシステムアーキテクチャ。
- SMT (Simultaneous Multi-threading): 1つの物理CPUコアが複数のスレッドを同時に実行できる技術(例: IntelのHyper-Threading)。
- NUMA (Non-Uniform Memory Access): 複数のCPUがそれぞれローカルメモリを持つシステムアーキテクチャ。ローカルメモリへのアクセスは高速ですが、他のCPUのローカルメモリへのアクセスは低速になります。 これらの環境では、複数のスレッドやプロセスが同時にメモリにアクセスするため、同期メカニズムの効率がシステム全体のパフォーマンスに大きく影響します。
技術的詳細
このコミットの技術的な核心は、FreeBSDのumtx
システムコールにおける_PRIVATE
フラグの利用にあります。
Goランタイムは、ゴルーチンのスケジューリングやチャネル操作など、内部的な同期のためにフューテックスのようなメカニズムを使用します。FreeBSDでは、これはumtx
システムコールを介して行われます。runtime·futexsleep
とruntime·futexwakeup
は、GoランタイムがFreeBSD上でフューテックスの待機と起床を行うための内部関数です。
従来のUMTX_OP_WAIT_UINT
とUMTX_OP_WAKE
は、プロセス間で共有されるミューテックスやセマフォなどの同期プリミティブを実装するために設計されています。これらの操作では、カーネルは同期対象のメモリアドレスがどのプロセスにマッピングされているかを判断し、必要に応じて複数のプロセスの仮想メモリ空間を走査して、待機しているスレッドや起こすべきスレッドを見つける必要があります。このVMオブジェクトの走査は、特に多数のプロセスやスレッドが存在するシステム、あるいはSMP/SMT/NUMAのような複雑なメモリ階層を持つシステムでは、無視できないオーバーヘッドとなります。
一方、UMTX_OP_WAIT_UINT_PRIVATE
とUMTX_OP_WAKE_PRIVATE
は、同じプロセス内のスレッド間でのみ同期を行うことを前提としています。この「プライベート」な性質により、カーネルは同期対象のメモリアドレスが現在のプロセスのアドレス空間内にあることだけを考慮すればよく、他のプロセスのVMオブジェクトを走査する必要がなくなります。これにより、システムコールのパスが短縮され、オーバーヘッドが削減されます。
コミットメッセージが指摘するように、Goのfutexsleep
とfutexwakeup
は、Goプログラム内のゴルーチン(Goランタイムのスレッドとして実装される)間の同期のために使用されます。これらは通常、単一のGoプロセス内で動作するため、プロセス間同期の機能は不要です。したがって、_PRIVATE
版のumtx操作を使用することは、Goランタイムの設計と利用パターンに合致しており、より効率的な選択となります。
この変更は、GoランタイムがFreeBSD上でより効率的に動作し、特に高負荷な並行処理環境やマルチコアシステムにおいて、同期関連のパフォーマンスボトルネックを軽減し、潜在的なVM関連のバグを回避することに貢献します。
コアとなるコードの変更箇所
このコミットでは、以下の5つのファイルが変更されています。
src/pkg/runtime/defs_freebsd.go
src/pkg/runtime/defs_freebsd_386.h
src/pkg/runtime/defs_freebsd_amd64.h
src/pkg/runtime/defs_freebsd_arm.h
src/pkg/runtime/os_freebsd.c
これらの変更は、主にFreeBSD固有のランタイム定義ファイルと、futexsleep
/futexwakeup
の実装ファイルに集中しています。
src/pkg/runtime/defs_freebsd.go
の変更点:
--- a/src/pkg/runtime/defs_freebsd.go
+++ b/src/pkg/runtime/defs_freebsd.go
@@ -49,8 +49,10 @@ const (
SA_RESTART = C.SA_RESTART
SA_ONSTACK = C.SA_ONSTACK
- UMTX_OP_WAIT_UINT = C.UMTX_OP_WAIT_UINT
- UMTX_OP_WAKE = C.UMTX_OP_WAKE
+ UMTX_OP_WAIT_UINT = C.UMTX_OP_WAIT_UINT
+ UMTX_OP_WAIT_UINT_PRIVATE = C.UMTX_OP_WAIT_UINT_PRIVATE
+ UMTX_OP_WAKE = C.UMTX_OP_WAKE
+ UMTX_OP_WAKE_PRIVATE = C.UMTX_OP_WAKE_PRIVATE
SIGHUP = C.SIGHUP
SIGINT = C.SIGINT
UMTX_OP_WAIT_UINT_PRIVATE
と UMTX_OP_WAKE_PRIVATE
の定数が追加されています。これらはC言語のヘッダファイルからGoの定数としてインポートされます。
src/pkg/runtime/defs_freebsd_386.h
, src/pkg/runtime/defs_freebsd_amd64.h
, src/pkg/runtime/defs_freebsd_arm.h
の変更点:
これらのファイルは、それぞれ32-bit x86、AMD64、ARMアーキテクチャ向けのFreeBSD固有のランタイム定義ヘッダファイルです。変更内容はすべて同じで、UMTX_OP_WAIT_UINT_PRIVATE
と UMTX_OP_WAKE_PRIVATE
の数値定数が追加されています。
例: src/pkg/runtime/defs_freebsd_386.h
--- a/src/pkg/runtime/defs_freebsd_386.h
+++ b/src/pkg/runtime/defs_freebsd_386.h
@@ -21,8 +21,10 @@ enum {
SA_RESTART = 0x2,
SA_ONSTACK = 0x1,
- UMTX_OP_WAIT_UINT = 0xb,
- UMTX_OP_WAKE = 0x3,
+ UMTX_OP_WAIT_UINT = 0xb,
+ UMTX_OP_WAIT_UINT_PRIVATE = 0xf,
+ UMTX_OP_WAKE = 0x3,
+ UMTX_OP_WAKE_PRIVATE = 0x10,
SIGHUP = 0x1,
SIGINT = 0x2,
UMTX_OP_WAIT_UINT_PRIVATE
に 0xf
、UMTX_OP_WAKE_PRIVATE
に 0x10
という値が割り当てられています。これらの値はFreeBSDのカーネルによって定義されています。
src/pkg/runtime/os_freebsd.c
の変更点:
このファイルは、FreeBSD上でのGoランタイムのOS固有の実装を含んでいます。futexsleep
とfutexwakeup
関数のumtx_op
システムコール呼び出しが変更されています。
--- a/src/pkg/runtime/os_freebsd.c
+++ b/src/pkg/runtime/os_freebsd.c
@@ -50,7 +50,7 @@ runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
Timespec ts;
if(ns < 0) {
- ret = runtime·sys_umtx_op(addr, UMTX_OP_WAIT_UINT, val, nil, nil);
+ ret = runtime·sys_umtx_op(addr, UMTX_OP_WAIT_UINT_PRIVATE, val, nil, nil);
if(ret >= 0 || ret == -EINTR)
return;
goto fail;
@@ -58,7 +58,7 @@ runtime·futexsleep(uint32 *addr, uint32 val, int64 ns)
// NOTE: tv_nsec is int64 on amd64, so this assumes a little-endian system.
ts.tv_nsec = 0;
ts.tv_sec = runtime·timediv(ns, 1000000000, (int32*)&ts.tv_nsec);
- ret = runtime·sys_umtx_op(addr, UMTX_OP_WAIT_UINT, val, nil, &ts);
+ ret = runtime·sys_umtx_op(addr, UMTX_OP_WAIT_UINT_PRIVATE, val, nil, &ts);
if(ret >= 0 || ret == -EINTR)
return;
@@ -78,7 +78,7 @@ runtime·futexwakeup(uint32 *addr, uint32 cnt)
{
int32 ret;
- ret = runtime·sys_umtx_op(addr, UMTX_OP_WAKE, cnt, nil, nil);
+ ret = runtime·sys_umtx_op(addr, UMTX_OP_WAKE_PRIVATE, cnt, nil, nil);
if(ret >= 0)
return;
runtime·futexsleep
関数内では、UMTX_OP_WAIT_UINT
がUMTX_OP_WAIT_UINT_PRIVATE
に置き換えられています。同様に、runtime·futexwakeup
関数内では、UMTX_OP_WAKE
がUMTX_OP_WAKE_PRIVATE
に置き換えられています。
コアとなるコードの解説
このコミットの核心は、src/pkg/runtime/os_freebsd.c
におけるruntime·futexsleep
とruntime·futexwakeup
関数の変更です。
runtime·futexsleep
関数:
この関数は、Goランタイムが特定のメモリアドレス(addr
)の値がval
と一致するまで、現在のゴルーチンをスリープさせるために使用されます。タイムアウト(ns
)も指定できます。
変更前は、runtime·sys_umtx_op
システムコールをUMTX_OP_WAIT_UINT
オペレーションと共に呼び出していました。
変更後は、このオペレーションがUMTX_OP_WAIT_UINT_PRIVATE
に変更されています。これにより、カーネルは待機操作を現在のプロセス内のスレッドに限定して処理するため、不要なVMオブジェクトの走査が回避され、効率が向上します。
runtime·futexwakeup
関数:
この関数は、addr
で待機しているゴルーチンを最大cnt
個までウェイクアップするために使用されます。
変更前は、runtime·sys_umtx_op
システムコールをUMTX_OP_WAKE
オペレーションと共に呼び出していました。
変更後は、このオペレーションがUMTX_OP_WAKE_PRIVATE
に変更されています。これにより、カーネルはウェイクアップ操作を現在のプロセス内のスレッドに限定して処理するため、同様に効率が向上します。
これらの変更は、GoランタイムがFreeBSD上でゴルーチンの同期を行う際に、より特化された効率的なumtx操作を利用するように切り替えることを意味します。Goのゴルーチンは単一のプロセス内で動作する軽量スレッドであるため、プロセス間同期の機能を持つ汎用的なumtx操作は不要であり、_PRIVATE
版の操作がより適切です。この最適化により、特に高並行なGoアプリケーションにおいて、同期のオーバーヘッドが削減され、全体的なパフォーマンスと安定性が向上することが期待されます。
他の.h
ファイルや.go
ファイルでの変更は、これらの新しい_PRIVATE
オペレーションの定数をGoランタイムが認識できるようにするための定義の追加であり、os_freebsd.c
での実際のシステムコール呼び出しの変更をサポートするものです。
関連リンク
- Go Issue #7496: https://github.com/golang/go/issues/7496 (このコミットが解決するIssue)
- Go CL 72760043: https://golang.org/cl/72760043 (このコミットのGerrit Code Reviewリンク)
参考にした情報源リンク
- FreeBSD man page for
umtx_op
: https://www.freebsd.org/cgi/man.cgi?query=umtx_op&sektion=2 (umtxシステムコールとそのオペレーションに関する公式ドキュメント) - Futex (Fast Userspace Mutex) - Wikipedia: https://en.wikipedia.org/wiki/Futex (フューテックスの一般的な概念に関する情報)
- Go Runtime Source Code: https://github.com/golang/go/tree/master/src/runtime (Goランタイムのソースコード)
- Go言語の並行処理 - ゴルーチンとチャネル: (Goの並行処理モデルに関する一般的な情報源)
- SMP, SMT, NUMAに関する情報源: (これらのアーキテクチャに関する一般的な情報源)
- 仮想メモリに関する情報源: (仮想メモリの概念に関する一般的な情報源)