[インデックス 14777] ファイルの概要
このコミットは、Go言語の標準ライブラリ sync/atomic
パッケージにおける64ビットアトミック操作に関するドキュメントの更新です。特に、32ビットアーキテクチャ(ARMおよびx86-32)上での64ビット値のアトミック操作において、呼び出し元が64ビットアライメントを保証する必要があることを明記しています。
コミット
commit 64a0017d6e69f3a6ba11ad5ad35c66dc489e5cbf
Author: Russ Cox <rsc@golang.org>
Date: Wed Jan 2 15:44:00 2013 -0500
sync/atomic: document that users must deal with 64-bit alignment
Update #599.
R=dvyukov, iant, minux.ma
CC=golang-dev
https://golang.org/cl/7001056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/64a0017d6e69f3a6ba11ad5ad35c66dc489e5cbf
元コミット内容
sync/atomic
パッケージの doc.go
ファイルが更新され、64ビットアトミック関数を使用する際のメモリ配置(アライメント)に関する重要な注意点が追加されました。
変更前:
// BUG(rsc): On ARM, the 64-bit functions use instructions unavailable before ARM 11.
//
// On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
変更後:
// BUG(rsc): On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
//
// On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit
// alignment of 64-bit words accessed atomically. The first word in a global
// variable or in an allocated struct or slice can be relied upon to be
// 64-bit aligned.
変更の背景
このコミットは、Go言語のIssue #599に関連しています。Issue #599は、32ビットアーキテクチャ(特にARMとx86-32)上で64ビット値に対するアトミック操作が正しく機能しない可能性があるという問題提起でした。
アトミック操作は、複数のゴルーチン(またはスレッド)が同時に共有データにアクセスする際に、データの整合性を保つために不可欠な操作です。しかし、一部のアーキテクチャでは、特定のデータサイズ(この場合は64ビット)に対するアトミック操作が、そのデータが特定のメモリ境界(アライメント)に配置されていない場合に正しく実行できない、あるいは非常に効率が悪くなるという制約があります。
Goの sync/atomic
パッケージは、このようなアトミック操作を提供しますが、32ビットシステム上で64ビット値を扱う際に、その値が64ビット境界にアライメントされていることが前提となる場合があります。もしアライメントが保証されない場合、アトミック操作が非アトミックになったり、クラッシュしたりする可能性がありました。
このコミットは、この潜在的な問題をユーザーに明確に伝えるために、ドキュメントにその責任を明記することを目的としています。Go言語の設計哲学として、特定の低レベルな詳細についてはユーザーに責任を委ねる場合がありますが、その際にはドキュメントで明確に指示することが重要です。
前提知識の解説
アトミック操作 (Atomic Operations)
アトミック操作とは、複数の操作が不可分(atomic)であることを保証するプログラミングの概念です。つまり、その操作が完全に実行されるか、全く実行されないかのどちらかであり、途中で中断されたり、他の操作と並行して実行されたりすることはありません。並行プログラミングにおいて、共有データへのアクセスを同期し、データ競合(data race)を防ぐために不可欠です。Go言語では sync/atomic
パッケージがこれらの機能を提供します。
メモリのアライメント (Memory Alignment)
メモリのアライメントとは、コンピュータのメモリ上でデータが配置される際の特定の規則です。多くのプロセッサは、特定のデータ型(例: 4バイトの整数、8バイトの浮動小数点数)がそのデータ型のサイズと同じバイト数の倍数のアドレスに配置されている場合に、最も効率的にアクセスできます。例えば、4バイトの整数は4の倍数のアドレス(0x00, 0x04, 0x08など)に、8バイトの整数は8の倍数のアドレスに配置されることが望ましいとされます。
アライメントが正しくない場合(非アライメントアクセス)、プロセッサは追加のサイクルを費やしてデータを読み書きしたり、場合によってはハードウェア例外(アライメント違反)を発生させたりすることがあります。特に、アトミック操作においては、非アライメントアクセスが原因で操作がアトミックでなくなったり、予期せぬ動作を引き起こしたりする可能性があります。
32ビットアーキテクチャと64ビット値
32ビットアーキテクチャ(例: x86-32、ARMv5/v6/v7)は、一度に32ビット(4バイト)のデータを処理するように設計されています。これに対し、64ビット値(例: int64
, uint64
, float64
)は8バイトのデータを持ちます。
32ビットシステムで64ビット値を扱う場合、通常は2つの32ビットレジスタまたは2つの32ビットメモリワードに分割して処理されます。この分割された処理は、アトミック性を保証するのが難しい場合があります。例えば、64ビット値を読み書きする際に、上位32ビットと下位32ビットが別々に操作されると、その間に他のゴルーチンが値を変更した場合、部分的に更新された不整合な値を読み取ってしまう可能性があります。
このため、多くの32ビットアーキテクチャでは、64ビット値に対する真のアトミック操作をサポートするために、その値が64ビット境界にアライメントされていることを要求します。これにより、プロセッサは64ビット値を単一の不可分な単位として操作できるようになります。
技術的詳細
このコミットが対処している技術的な問題は、32ビットシステムにおける64ビットアトミック操作の信頼性です。
具体的には、x86-32(Pentium MMX以前のCPU)とARM(ARM 11以前のCPU)では、64ビット値に対する単一命令でのアトミックな読み書きや比較交換(CompareAndSwap)がサポートされていないか、特定の条件(アライメント)を満たさないと正しく機能しません。
- x86-32: Pentium MMX以降のx86プロセッサでは、
CMPXCHG8B
命令などを用いて64ビットアトミック操作が可能ですが、それ以前のプロセッサでは利用できません。また、この命令も通常は8バイトアライメントされたデータに対して最適化されています。 - ARM: ARMアーキテクチャでは、ARMv6以降でロード・ストア排他(Load-Exclusive/Store-Exclusive, LDREX/STREX)命令が導入され、これらを用いてアトミック操作を実装できます。しかし、それ以前のARMv5などでは、64ビット値に対するアトミック操作はソフトウェアエミュレーションに頼るか、アライメントが厳密に要求される場合があります。
Goの sync/atomic
パッケージは、これらのアーキテクチャ固有の制約を抽象化し、クロスプラットフォームでアトミック操作を提供しようとします。しかし、低レベルな実装では、基盤となるハードウェアの能力に依存します。
このコミットの変更は、Goランタイムが常に64ビット値の64ビットアライメントを保証できるわけではないという事実を反映しています。特に、ユーザーが構造体内で64ビットフィールドを定義したり、スライス内で64ビット要素を使用したりする場合、コンパイラやランタイムがデフォルトで64ビットアライメントを保証しない可能性があります(特に32ビットシステムでは、ポインタサイズが32ビットであるため、デフォルトのアライメントが32ビットになることが多い)。
したがって、ユーザーが sync/atomic
パッケージの64ビット関数(例: LoadInt64
, StoreInt64
, AddInt64
, CompareAndSwapInt64
)を安全に使用するためには、対象となる64ビット変数がメモリ上で64ビット境界に配置されていることを自ら確認し、必要であればアライメントを調整する責任があることをドキュメントで明記しています。
ドキュメントに記載されている「グローバル変数、または割り当てられた構造体やスライスの最初のワードは、64ビットアライメントされていると信頼できる」という記述は、Goのメモリ割り当ての特性に基づいています。Goのランタイムは、グローバル変数や make
、new
で割り当てられるメモリブロックの先頭が、通常は最大アライメント要件(64ビットシステムでは8バイト、32ビットシステムでも特定の条件下では8バイト)を満たすように配置される傾向があることを示唆しています。しかし、構造体内の後続のフィールドや、スライス内の要素が常に64ビットアライメントされるとは限らないため、注意が必要です。
コアとなるコードの変更箇所
変更は src/pkg/sync/atomic/doc.go
ファイルのみです。
--- a/src/pkg/sync/atomic/doc.go
+++ b/src/pkg/sync/atomic/doc.go
@@ -38,9 +38,12 @@ import (
"unsafe"
)
-// BUG(rsc): On ARM, the 64-bit functions use instructions unavailable before ARM 11.
+// BUG(rsc): On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
//
-// On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
+// On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit
+// alignment of 64-bit words accessed atomically. The first word in a global
+// variable or in an allocated struct or slice can be relied upon to be
+// 64-bit aligned.
// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
コアとなるコードの解説
変更は、doc.go
ファイル内のコメントの修正と追加です。
-
既存のBUGコメントの順序変更と修正:
- 元々「ARMではARM 11以前の命令を使用する」というコメントが最初にあり、その後に「x86-32ではPentium MMX以前の命令を使用する」というコメントがありました。
- 変更後、x86-32に関するコメントが最初に移動し、ARMに関するコメントは削除されました。これは、ARMに関するより一般的な注意点が後続の新しいコメントでカバーされるためと考えられます。
-
新しいアライメントに関する注意点の追加:
- 最も重要な変更は、ARMとx86-32の両方において、「呼び出し元が64ビット値の64ビットアライメントを保証する責任がある」という新しい文言が追加されたことです。
- さらに、「グローバル変数、または割り当てられた構造体やスライスの最初のワードは、64ビットアライメントされていると信頼できる」というヒントが追加されています。これは、Goのメモリ割り当てメカニズムが、これらのケースでは適切なアライメントを提供する傾向があることを示唆しています。しかし、これはあくまで「信頼できる」という表現であり、常に保証されるわけではないこと、特に構造体内の後続のフィールドやスライス内の任意の要素についてはユーザーが注意を払う必要があることを示唆しています。
このドキュメントの更新は、sync/atomic
パッケージの64ビット関数を32ビットシステムで使用する際の潜在的な落とし穴について、開発者により明確なガイダンスを提供することを目的としています。これにより、アライメントの問題に起因するバグや予期せぬ動作を防ぐことができます。
関連リンク
- Go Issue #599: https://github.com/golang/go/issues/599
- Go CL 7001056: https://golang.org/cl/7001056 (このコミットに対応するGoのコードレビューシステム上の変更リスト)
- Go
sync/atomic
package documentation: https://pkg.go.dev/sync/atomic
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のIssueトラッカー
- Go言語のソースコード
- メモリのアライメントに関する一般的なコンピュータアーキテクチャの知識
- アトミック操作に関する並行プログラミングの知識