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

[インデックス 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_UINTUMTX_OP_WAKE といった一般的なumtx操作が使用されていました。

しかし、コミットメッセージに記載されているように、futexsleepfutexwakeup (Goランタイム内で使用されるフューテックスに相当する機能) は、プロセス間ではなく、スレッド間の同期のために設計されています。一般的なumtx操作は、プロセス間同期も考慮しているため、仮想メモリ(VM)オブジェクトの不要なトラバーサル(走査)が発生する可能性があります。

特に、SMP(Symmetric Multi-Processing)、SMT(Simultaneous Multi-threading)、NUMA(Non-Uniform Memory Access)といったマルチプロセッサ環境では、VMシステムの複雑性が増し、このような不要なトラバーサルがパフォーマンスの低下や、未発見のバグ(特にVMシステムに関連するもの)を引き起こす可能性がありました。

この問題を解決するため、よりスレッドローカルな同期に特化した UMTX_OP_WAIT_UINT_PRIVATEUMTX_OP_WAKE_PRIVATE の使用が提案されました。これらの_PRIVATEオペレーションは、同じプロセス内のスレッド間でのみ有効であり、VMオブジェクトの広範な走査を避けることができます。これにより、同期操作のオーバーヘッドが削減され、マルチスレッド環境でのGoランタイムの安定性と効率が向上します。

この変更は、GoのIssue #7496に関連しており、FreeBSD上でのGoランタイムの堅牢性を高めることを目的としています。

前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

  1. Goランタイム: Go言語で書かれたプログラムを実行するための環境です。ガベージコレクション、ゴルーチンのスケジューリング、チャネルによる通信など、Go言語の並行処理モデルを支える重要なコンポーネントです。ランタイムは、OSのシステムコールを直接利用して、低レベルの操作(メモリ管理、スレッド同期など)を行います。

  2. フューテックス (futex): "Fast Userspace muTEX" の略で、LinuxやFreeBSDなどのUnix系OSで提供される、ユーザー空間での高速な同期プリミティブです。フューテックスは、競合がない場合にはカーネルへのシステムコールを発生させずにユーザー空間で同期を完結させ、競合が発生した場合にのみカーネルに介入を要求することで、高いパフォーマンスを実現します。これにより、ミューテックスやセマフォなどの同期メカニズムを効率的に実装できます。

  3. FreeBSDのumtx (Userspace Mutex): FreeBSDにおけるフューテックスの実装です。umtxシステムコールを通じて、ユーザー空間のスレッドが同期オブジェクト(ミューテックスなど)の状態を待機したり、他のスレッドを起こしたりすることができます。umtxシステムコールは、様々な操作(UMTX_OP_*)をサポートしており、それぞれ異なる同期セマンティクスを提供します。

  4. UMTX_OP_WAIT_UINTUMTX_OP_WAKE: これらはumtxシステムコールで利用される一般的な操作です。

    • UMTX_OP_WAIT_UINT: 指定されたメモリアドレスの値が期待する値と異なる場合に、スレッドをスリープさせます。
    • UMTX_OP_WAKE: 指定されたメモリアドレスで待機しているスレッドをウェイクアップ(起こす)します。 これらの操作は、プロセス間同期にも利用できる汎用的なものです。
  5. UMTX_OP_WAIT_UINT_PRIVATEUMTX_OP_WAKE_PRIVATE: これらはumtxシステムコールで利用される_PRIVATE版の操作です。

    • UMTX_OP_WAIT_UINT_PRIVATE: UMTX_OP_WAIT_UINT と同様ですが、同じプロセス内のスレッド間でのみ有効です。
    • UMTX_OP_WAKE_PRIVATE: UMTX_OP_WAKE と同様ですが、同じプロセス内のスレッド間でのみ有効です。 _PRIVATE版の操作は、プロセス間同期のオーバーヘッドを回避し、より高速なスレッド間同期を提供します。これは、カーネルがVMオブジェクトの広範な走査を行う必要がないためです。
  6. 仮想メモリ (VM) オブジェクト: オペレーティングシステムがプロセスに提供する仮想アドレス空間を管理するための内部データ構造です。プロセスがメモリにアクセスする際、OSはVMオブジェクトを使用して、仮想アドレスを物理アドレスにマッピングします。プロセス間同期では、共有メモリ領域が異なるプロセスのVMオブジェクトにマッピングされるため、カーネルはこれらのマッピングを考慮して同期操作を行う必要があります。

  7. 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·futexsleepruntime·futexwakeupは、GoランタイムがFreeBSD上でフューテックスの待機と起床を行うための内部関数です。

従来のUMTX_OP_WAIT_UINTUMTX_OP_WAKEは、プロセス間で共有されるミューテックスやセマフォなどの同期プリミティブを実装するために設計されています。これらの操作では、カーネルは同期対象のメモリアドレスがどのプロセスにマッピングされているかを判断し、必要に応じて複数のプロセスの仮想メモリ空間を走査して、待機しているスレッドや起こすべきスレッドを見つける必要があります。このVMオブジェクトの走査は、特に多数のプロセスやスレッドが存在するシステム、あるいはSMP/SMT/NUMAのような複雑なメモリ階層を持つシステムでは、無視できないオーバーヘッドとなります。

一方、UMTX_OP_WAIT_UINT_PRIVATEUMTX_OP_WAKE_PRIVATEは、同じプロセス内のスレッド間でのみ同期を行うことを前提としています。この「プライベート」な性質により、カーネルは同期対象のメモリアドレスが現在のプロセスのアドレス空間内にあることだけを考慮すればよく、他のプロセスのVMオブジェクトを走査する必要がなくなります。これにより、システムコールのパスが短縮され、オーバーヘッドが削減されます。

コミットメッセージが指摘するように、Goのfutexsleepfutexwakeupは、Goプログラム内のゴルーチン(Goランタイムのスレッドとして実装される)間の同期のために使用されます。これらは通常、単一のGoプロセス内で動作するため、プロセス間同期の機能は不要です。したがって、_PRIVATE版のumtx操作を使用することは、Goランタイムの設計と利用パターンに合致しており、より効率的な選択となります。

この変更は、GoランタイムがFreeBSD上でより効率的に動作し、特に高負荷な並行処理環境やマルチコアシステムにおいて、同期関連のパフォーマンスボトルネックを軽減し、潜在的なVM関連のバグを回避することに貢献します。

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

このコミットでは、以下の5つのファイルが変更されています。

  1. src/pkg/runtime/defs_freebsd.go
  2. src/pkg/runtime/defs_freebsd_386.h
  3. src/pkg/runtime/defs_freebsd_amd64.h
  4. src/pkg/runtime/defs_freebsd_arm.h
  5. 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_PRIVATEUMTX_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_PRIVATEUMTX_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_PRIVATE0xfUMTX_OP_WAKE_PRIVATE0x10 という値が割り当てられています。これらの値はFreeBSDのカーネルによって定義されています。

src/pkg/runtime/os_freebsd.c の変更点:

このファイルは、FreeBSD上でのGoランタイムのOS固有の実装を含んでいます。futexsleepfutexwakeup関数の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_UINTUMTX_OP_WAIT_UINT_PRIVATEに置き換えられています。同様に、runtime·futexwakeup関数内では、UMTX_OP_WAKEUMTX_OP_WAKE_PRIVATEに置き換えられています。

コアとなるコードの解説

このコミットの核心は、src/pkg/runtime/os_freebsd.cにおけるruntime·futexsleepruntime·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での実際のシステムコール呼び出しの変更をサポートするものです。

関連リンク

参考にした情報源リンク