#63999active
sync/atomic: add Max/Min operators
新規提案
要約
AIによる要約であり、誤りを含む場合があります。
概要
sync/atomic パッケージにアトミックなMax/Min演算を追加するproposal。複数のgoroutineが共有する数値カウンタに対して、ロックなしで最大値・最小値を安全に更新するためのプリミティブを提供する。
ステータス変更
(なし) → active
2026年6月17日の週次Proposal Review Meetingにて、sync/atomic: add Max/Min operators が初めてアクティブ列に追加された。2023年11月に提案されてから約2年半が経過しており、類似提案である sync/atomic へのAnd/Or演算子追加(#61395)が先に承認・実装されたことで、Max/Minも自然な次のステップとして議論のテーブルに乗ったと考えられる。
技術的背景
現状の問題点
並行プログラムで「複数スレッドが競合する最大値カウンタ」を安全に更新したい場面は多い。現在はCompare-And-Swap(CAS)ループで自前実装するしかない。Goランタイムのガベージコレクタ内(runtime/mgcsweep.go)でも同様のパターンが使われており、TODOコメントで「atomic maxがあれば共通化できるのに」と言及されていた。
// 現状のワークアラウンド: CASループ
func atomicMax(addr *int64, val int64) int64 {
for {
old := atomic.LoadInt64(addr)
if old >= val {
return old
}
if atomic.CompareAndSwapInt64(addr, old, val) {
return old
}
}
}
提案された解決策
sync/atomic パッケージに以下のAPIを追加する。
// スタンドアロン関数
func MaxInt32(addr *int32, val int32) (old int32)
func MaxInt64(addr *int64, val int64) (old int64)
func MaxUint32(addr *uint32, val uint32) (old uint32)
func MaxUint64(addr *uint64, val uint64) (old uint64)
func MaxUintptr(addr *uintptr, val uintptr) (old uintptr)
func MinInt32(addr *int32, val int32) (old int32)
// ... 以下同様
// 型付きアトミック変数のメソッド
func (*Int32) Max(val int32) (old int32)
func (*Int64) Max(val int64) (old int64)
func (*Uint32) Max(val uint32) (old uint32)
func (*Uint64) Max(val uint64) (old uint64)
func (*Uintptr) Max(val uintptr) (old uintptr)
func (*Int32) Min(val int32) (old int32)
// ... 以下同様
戻り値は更新前の値(old)。アーキテクチャによってはネイティブ命令で実装できる。
- RISC-V:
AMOMAX/AMOMIN - Arm64:
LDSMAXなど - AMD64: ネイティブ命令なし、CASループにフォールバック
runtime/internal/atomicにも同等の低レベル関数(Max、Max64、MaxUintptr他)が必要になる。
これによって何ができるようになるか
並行処理における最大値・最小値の管理が、ロック不要かつ簡潔に書けるようになる。
コード例
// Before: CASループによる手動実装
var maxSeen int64
func recordValue(v int64) {
for {
old := atomic.LoadInt64(&maxSeen)
if old >= v {
break
}
if atomic.CompareAndSwapInt64(&maxSeen, old, v) {
break
}
}
}
// After: 新APIを使った書き方
var maxSeen atomic.Int64
func recordValue(v int64) {
maxSeen.Max(v)
}
主なユースケース:
- 統計収集: 複数ワーカーが処理した入力の最大値・最小値をゴルーチンセーフに記録する
- 最適化の進捗管理: 最適化アルゴリズムで「これ以上改善が見込めないスレッドを終了させる」ためのカットオフ値共有
- GCや低レベルランタイム: ランタイム内の単調増加カウンタの更新(実際にGoランタイム内に需要がある)
議論のハイライト
- ハードウェアサポートの非対称性: Arm64・RISC-Vはネイティブ命令が存在するが、AMD64はCASループになる。それでも標準ライブラリに持つ価値はあるという判断
- And/Or演算子との一貫性: 同様の動機で承認された
#61395(And/Or追加)との整合性が明確に示されており、本proposalもその自然な拡張として位置づけられている - レースディテクタの対応: TSANには
fetch_max/fetch_minが存在しないため、LLVMアップストリームへの対応追加が必要。提案者(@mauri870)は自ら対応を申し出ている - スタンドアロン関数と型付きメソッドの両方を追加: 既存のAnd/Or追加と同様のパターンで、
atomic.Int32等の型メソッドと生ポインタ版の両方を提供する設計 - ランタイム内需要が動機: 外部ユーザーの要望だけでなく、Goランタイム自身が必要としているという点が説得力を持つ