"The JSR-133 Cookbook for Compiler Writers"
original website is http://g.oswego.edu/dl/jmm/cookbook.html. by Doug Lea, with help from members of the JMM mailing list.
Japanese edition is translated by T.Murayama and Java Reading Group.
(*)部は訳注予定.
This is an unofficial guide to implementing the new Java Memory Model (JMM) specified by JSR-133 . It provides at most brief backgrounds about why various rules exist, instead concentrating on their consequences for compilers and JVMs with respect to instruction reorderings, multiprocessor barrier instructions, and atomic operations. It includes a set of recommended recipes for complying to JSR-133. This guide is "unofficial" because it includes interpretations of particular processor properties and specifications. We cannot guarantee that the intepretations are correct. Also, processor specifications and implementations may change over time.
これは JSR-133 で定義された新しい Java Memory Model (JMM) についての非公式の手引き書である.多くのルールが設けられている背景については簡潔に説明するに留め、命令の順序変更、マルチプロセッサのバリア命令、およびアトミック演算に関してコンパイラとJVMに与える影響について重点的に解説する。それはJSR133に準拠する上で推奨されるレシピを含んでいる.この手引きは,特定のプロセッサの特徴と仕様に関する解釈を含んでいるので「非公式」である.我々は,この解釈が正しいとは保証できない.また時が経てばプロセッサの仕様や実装も変更されるかもしれない.
For a compiler writer, the JMM mainly consists of rules disallowing reorderings of certain instructions that access fields (where "fields" include array elements) as well as monitors (locks).
コンパイラ作成者にとってJMMは,主にモニター(ロック)だけでなくフィールド(ここでいう「フィールド」には配列の要素も含む)にアクセスする特定の命令列に関して,順序変更 (reorderings) を禁止するルールより構成されている.
The main JMM rules for volatiles and monitors can be viewed as a matrix with cells indicating that you cannot reorder instructions associated with particular sequences of bytecodes. This table is not itself the JMM specification; it is just a useful way of viewing its main consequences for compilers and runtime systems.
volatile変数とモニタに関するJMMの主要なルールは,各要素が特定のバイトコード命令の並びと関連した順序変更不可能な命令群を意味する行列とみなすことができる.この表はそれ自体はJMMの仕様ではない;これは単にコンパイラやランタイムシステムに関するJMMの主な結論を,便利な形で図示したものだ.
Can Reorder | 2nd operation | ||
1st operation | Normal Load Normal Store |
Volatile Load MonitorEnter |
Volatile Store MonitorExit |
Normal Load Normal Store |
No | ||
Volatile Load MonitorEnter |
No | No | No |
Volatile store MonitorExit |
No | No |
Where:
この時
The cells for Normal Loads are the same as for Normal Stores, those for Volatile Loads are the same as MonitorEnter, and those for Volatile Stores are same as MonitorExit, so they are collapsed together here (but are expanded out as needed in subsequent tables).
Nomal Loadのマス目とNormal Storeのマス目は完全に一致する.同様にVolatile LoadとMonitorEnter,Volatile StoreとMonitorExitも完全に一致する.よってこの表では一つにまとめてある.(以下で出てくる表においては,必要に応じて分割する.)
Any number of other operations might be present between the indicated 1st and 2nd operations in the table. So, for example, the "No" in cell [Normal Store, Volatile Store] says that a non-volatile store cannot be reordered with ANY subsequent volatile store; at least any that can make a difference in multithreaded program semantics.
1stと2ndで指定される命令の間には,任意の個数のその他の演算が出現することが許される.だから,例えば, [Normal StoreとVolatile Store]の組に対するマス目に書かれている「No」とは,「非volatileストアは,それより後にある(少なくともマルチスレッドプログラム上のセマンティクス的に違いを生じる可能性のある)全ての Volatile Storeと,順序変更してはならない.」ということを意味する.
The JSR-133 specification is worded such that the rules for both volatiles and monitors apply only to those that may be accessed by multiple threads. If a compiler can somehow (usually only with great effort) prove that a lock is only accessible from a single thread, it may be eliminated. Similarly, a volatile field provably accessible from only a single thread acts as a normal field. More fine-grained analyses and optimizations are also possible, for example, those relying on provable inaccessibility from multiple threads only during certain intervals.
JSR-133は複数スレッドよりアクセスされる可能性のある, volatile変数とモニタに関してのみ適用されるルールについて述べている.もしコンパイラが(通常は多大なる努力を伴った)何らかの方法により,あるロックが単一スレッドからのみアクセス可能であることが証明できたならば,それは消去可能である.同様に,単一スレッドよりアクセスされることが証明できる volatileフィールドは, Normalフィールドのように振る舞う.さらなる細粒度の分析と最適化も可能である.例えば,特定の間隔で発生するであろう,複数スレッドからのアクセス不可能性に依存した,分析と最適化のようなものが考えられる.
Blank cells in the table mean that the reordering is allowed if the accesses aren't otherwise dependent with respect to basic Java semantics (as specified in the JLS). For example even though the table doesn't say so, you can't reorder a load with a subsequent store to the same location. But you can reorder a load and store to two distinct locations, and may wish to do so in the course of various compiler transformations and optimizations. This includes cases that aren't usually thought of as reorderings; for example reusing a computed value based on a loaded field rather than reloading and recomputing the value acts as a reordering. However, the JMM spec permits transformations that eliminate avoidable dependencies, and in turn allow reorderings.
表中の空白部分は,それらのアクセスが他の理由により(JLSで定義された)Java のセマンティクス上の依存関係がない限り,順序変更可能であることをを意味している.例えば,表中に何も言及されていなくても,同一箇所に対するロードとそれに続くストアとは順序変更不可能である.しかし異なる箇所に対するロードとそれに続くストアについては順序変更可能であり,あなたはコンパイラによる様々な変更と最適化の中で,実際にそうなることを期待するかもしれない.これには,通常は順序変更と見なされないケースも含んでいる.例えば,フィールドを再ロードして再計算するのではなく,ロード済みの値を元に計算した値の再利用は,順序変更のように振る舞う.しかしながら,JMM仕様は無視しても良い依存性を消去するような変換を許しており,そうすると順序変更も認められるようになる.
In all cases, permitted reorderings must maintain minimal Java safety properties even when accesses are incorrectly synchronized by programmers: All observed field values must be either the default zero/null "pre-construction" values, or those written by some thread. This usually entails zeroing all heap memory holding objects before it is used in constructors and never reordering other loads with the zeroing stores. A good way to do this is to zero out reclaimed memory within the garbage collector. See the JSR-133 spec for rules dealing with other corner cases surrounding safety guarantees.
全ての例において,たとえプログラマによってアクセスが正しく同期されていなかったとしても,許された順序変更はJavaの安全性に関する最低限の特性を維持しなければならない.全ての観測されたフィールド値は,デフォルトのゼロ/nullの「構築前 (pre-construction) 」の値か,あるスレッドによって書き込まれた値のいずれかでなければならない.これは通常は,コンストラクタ内で使用される前にオブジェクトを保持しているヒープメモリが0クリアされ,その0ストアに対してその他のロードが順序変更されないということを意味する.これを実現する良い方法は,ガベージコレクタ内でメモリを再生 (reclaim) する時に全て0にすることだ(*).安全性の保証に関する,他の困難な事例については JSR-133仕様を参照のこと.
The rules and properties described here are for accesses to Java-level fields. In practice, these will additionally interact with accesses to internal bookkeeping fields and data, for example object headers, GC tables, and dynamically generated code.
ここで描かれた全てのルールと特性は,Javaレベルのフィールドへのアクセスに関するものである.実際には,これに加えてオブジェクトのヘッダ,GCのテーブル,動的生成されるコードなどの,内部的に保持/利用されるフィールドやデータとの相互作用も追加される.
Loads and Stores of final fields act as "normal" accesses with respect to locks and volatiles, but impose two additional reordering rules:
finalフィールドに対するロードとストアは,ロックとvolatileに関しては「通常」のアクセスと同様に振る舞うが,順序変更に関する二つのルールが追加される.
These rules imply that reliable use of final fields by Java programmers requires that the load of a shared reference to an object with a final field itself be synchronized, volatile, or final, or derived from such a load, thus ultimately ordering the initializing stores in constructors with subsequent uses outside constructors.
これらのルールは以下のことを示している. Javaプログラマが finalフィールドを信頼して使用するには, finalフィールドを持つオブジェクトへの参照型が共有される時に,その参照型のロードは,それ自体が同期化されているか, volatile又は finalであるか,又はそのようなロードに由来するかのいずれかでなければならない.そして,それゆえに究極的にはコンストラクタ中の初期化ストアと,それの後に続くコンストラクタ外の参照の利用が順序づけられる.
Compilers and processors must both obey reordering rules. No particular effort is required to ensure that uniprocessors maintain proper ordering, since they all guarantee "as-if-sequential" consistency. But on multiprocessors, guaranteeing conformance often requires emitting barrier instructions. Even if a compiler optimizes away a field access (for example because a loaded value is not used), barriers must still be generated as if the access were still present. (Although see below about independently optimizing away barriers.)
コンパイラとプロセッサは共に順序変更のルールに従わなければならない.単一プロセッサでは,「まるで逐次であるかのような (as-if sequential *)」一貫性がいつも保証されるので,命令の特定の順序を維持することを保証するのに,特殊な努力は必要ない.しかしマルチプロセッサ上で準拠していることを保証するには,多くはバリア命令(*)の発行が必要になる.たとえコンパイラが最適化の結果,フィールドアクセス自体を無くしてしまったとしても,(たとえば,ロードされた値が使用されない場合,)それでもアクセスがある時と同じ様にバリアを生成しなければならない.(しかし,独立した最適化によるバリアの除去については,以下を参照すること.)
Memory barriers are only indirectly related to higher-level notions described in memory models such as "acquire" and "release". And memory barriers are not themselves "synchronization barriers". And memory barriers are unrelated to the kinds of "write barriers" used in some garbage collectors. Memory barrier instructions directly control only the interaction of a CPU with its cache, with its write-buffer that holds stores waiting to be flushed to memory, and/or its buffer of waiting loads or speculatively executed instructions. These effects may lead to further interaction among caches, main memory and other processors. But there is nothing in the JMM that mandates any particular form of communication across processors so long as stores eventually become globally performed; i.e., visible across all processors, and that loads retrieve them when they are visible.
メモリ=バリアは,「獲得」や「解放」のようなメモリモデルで描写される,上位の概念とは間接的にしか関連していない.メモリ=バリアはそれ自体は「同期バリア(*)」ではない.そして,メモリ=バリアはある種のGCで使われる「ライト= バリア(*)」とも関係がない.メモリ=バリア命令はCPUとそのキャッシュ−もう少し具体的言えば,メモリへフラッシュされるストアデータを保持している書き込みバッファと,ロードデータと投機的実行される命令(*)の(読み込み)バッファ− 間の相互作用を直接的に制御する.これらの効果は,キャッシュ,メインメモリ,他のプロセッサとの間に,さらなる相互作用をもたらすかも知れない.しかし最終的にストアが大域的に実行される−即ち,全てのプロセッサに見えるようになり,そしてそれが見えるようになった時に,そのロードはそれらを取得する.(?)−限り,JMMはプロセッサ間通信の形式についてはなんら規定していない.
Nearly all processors support at least a coarse-grained barrier instruction, often just called a Fence, that guarantees that all loads and stores initiated before the fence will be strictly ordered before any load or store initiated after the fence. This is usually among the most time-consuming instructions on any given processor (often nearly as, or even more expensive than atomic instructions). Most processors additionally support more fine-grained barriers.
ほとんど全てのプロセッサは,少なくとも粗粒度のバリア命令をサポートしている.それはFenceとも呼ばれ,fenceの前に開始された全てのロードとストアが,fenceの後に開始された全てのロードとストアの前に,強く順序づけされることを保証する.これは通常は既知のプロセッサにおいて最も時間のかかる命令の一つである.(多くは,アトミック命令と同程度か,或いはそれ以上に高く付く.)多くのプロセッサは細粒度のバリアを付加的にサポートしている.
A property of memory barriers that takes some getting used to is that they apply BETWEEN memory accesses. Despite the names given for barrier instructions on some processors, the right/best barrier to use depends on the kinds of accesses it separates. Here's a common categorization of barrier types that maps pretty well to specific instructions (sometimes no-ops) on existing processors:
慣れるのに少々手間がかかる(?)メモリバリアの特性は,それがメモリアクセス間に適用されるということだ.幾つかのプロセッサでのバリア命令の名前にも関わらず,使用するのに正しい/最適なバリアは個々のアクセスの種類に依存する.特定の命令に上手く(時にはno-opsに)マッピングされる,バリアの一般的な分類を,ここに示す.
On all processors discussed below, it turns out that instructions that perform StoreLoad also obtain the other three barrier effects, so StoreLoad can serve as a general-purpose (but usually expensive) Fence. (This is an empirical fact, not a necessity.) The opposite doesn't hold though. It is NOT usually the case that issuing any combination of other barriers gives the equivalent of a StoreLoad.
以下で議論されている全てのプロセッサにおいて,StoreLoadを実行する命令は,その他の三つのバリアの効果も持つことが分かっており,それゆえ汎用の(しかし通常は高価な)FenceとしてStoreLoadを提供できる.(これはただの経験的な事実であり,必然ではない.)しかし,その逆は成立しない.その他の三つのバリアのいかなる組み合わせにおいても,StoreLoadと等価になることは,通常はありえない.
The following table shows how these barriers correspond to JSR-133 ordering rules.
次に続く表は,これらのバリアがJSR-133の順序に関するルールと如何に関連するかを示している.
Required barriers | 2nd operation | |||
1st operation | Normal Load | Normal Store | Volatile Load MonitorEnter |
Volatile Store MonitorExit |
Normal Load | LoadStore | |||
Normal Store | StoreStore | |||
Volatile Load MonitorEnter |
LoadLoad | LoadStore | LoadLoad | LoadStore |
Volatile Store MonitorExit |
StoreLoad | StoreStore |
Plus the special final-field rule requiring a StoreStore barrier in
x.finalField = v; StoreStore; sharedRef = x;
Here's an example showing placements.
加えて,以下の例ではStoreStoreバリアには,
finalフィールドについて特別なルールが必要になる.
x.finalField = v; StoreStore; sharedRef = x;
ここに,その一例を示す.
Java |
Instructions |
class X { int a, b; volatile int v, u; void f() { int i, j; i = a; j = b; i = v; j = u; a = i; b = j; v = i; u = j; i = u; j = b; a = i; } } |
load a load b load v LoadLoad load u LoadStore store a store b StoreStore store v StoreStore store u StoreLoad load u load b store a |
The need for LoadLoad and LoadStore barriers on some processors interacts with
their ordering guarantees for dependent instructions. On some (most)
processors, a load or store that is dependent on the value of a previous
load are ordered by the processor without need for an explicit
barrier. This commonly arises in two kinds of cases, indirection:
Load x; Load x.field
and control
Load x; if (predicate(x)) Load or Store y;
幾つかのプロセッサにおけるLoadLoadバリア,及びLoadStoreバリアの必要性は,依存する命令群の順序の保証と相互に影響し合う(?).幾つかの(或いはほとんどの)プロセッサでは,前回のロードの値に依存するロード又はストア命令は,明示的なバリアの必要無しに,プロセッサにより順序づけられる,これは主に次の二種類の形で間接的に現れる
Load x; Load x.field
そして制御は次のようになる.(?)
Load x; if (predicate(x)) Load or Store y;
Processors that do NOT respect indirection ordering in
particular require barriers for final field access for references
initially obtained through shared references:
x = sharedRef; ... ; LoadLoad; i = x.finalField;
間接的な順序付けに関して特に注意を払わないプロセッサは,共有している参照を通じて最初に獲得される参照のfinalフィールドへのアクセスのためにバリアを要求する:
x = sharedRef; ... ; LoadLoad; i = x.finalField;
Conversely, as discussed below, processors that DO respect data dependencies provide several opportunities to optimize away LoadLoad and LoadStore barrier instructions that would otherwise need to be issued. (However, dependency does NOT automatically remove the need for StoreLoad barriers on any processor.)
逆に,以下で議論するように,データの依存性に注目するプロセッサは,本来なら発行するであろう LoadLoadと LoadStoreを除去することで最適化するチャンスを提供する(しかし依存性は, StoreLoadバリアの必要性を全てのプロセッサにおいて自動的に除去するわけではない.)
The kinds of barriers needed on different processors further interact with implementation of MonitorEnter and MonitorExit. Locking and/or unlocking usually entail the use of atomic conditional update operations CompareAndSwap (CAS) or LoadLinked/StoreConditional (LL/SC) that have the semantics of performing a volatile load followed by a volatile store. While CAS or LL/SC minimally suffice, some processors also support other atomic instructions (for example, an unconditional exchange) that can sometimes be used instead of or in conjunction with atomic conditional updates.
異なるプロセッサ上で必要とされるその種のバリアは(?), MonitorEnterと MonitorExit(*)の実装ににさらなる相互作用を引き起こす.ロックとアンロックはアトミックな条件付き更新操作である CompareAndSwap(CAS)や LoadLinked/StoreConditional (LL/SC)を含んでいる.それらはセマンティクス的には volatileロードに続く volatileストアになる.CASや LL/SCがあれば最低限の機能は満たすけれど,幾つかのプロセッサでは他のアトミックな命令(例えば "unconditional exchange",無条件変換)もサポートしている.それはアトミックな条件付き更新( "conditional update")の代わりに,あるいはそれと一緒に使われる.
On all processors, atomic operations protect against read-after-write problems for the locations being read/updated. (Otherwise standard loop-until-success constructions wouldn't work in the desired way.) But processors differ in whether atomic instructions provide more general barrier properties than the implicit StoreLoad for their target locations. On some processors these instructions also intrinsically perform barriers that would otherwise be needed for MonitorEnter/Exit; on others some or all of these barriers must be specifically issued.
全てのプロセッサにおいて,アトミックな操作はその場所を読んで/書くという read-after-write問題を防いている.(さもなくば,標準的な loop-until-success 構築は望んだようには機能しない.)しかしターゲットの場所に対する暗黙的な StoreLoad命令よりも,より一般的なバリア機能をアトミック命令が提供するかどうかで,プロセッサには違いが見られる.幾つかのプロセッサでは,これらの命令もまた本質的にバリアとして振る舞っており,さもなくば MonitorEnter/Exitが必要になるだろう.:その他の幾つかのプロセッサでは,これらのバリアの全て,或いは幾らかは,明確に発行する必要がある.
Volatiles and Monitors have to be separated to disentangle these effects, giving:
これの効果をより分けるためには, volatile変数やモニタは独立していなければならない.与えられた表について,(?)
Required Barriers | 2nd operation | |||||
1st operation | Normal Load | Normal Store | Volatile Load | Volatile Store | MonitorEnter | MonitorExit |
Normal Load | LoadStore | LoadStore | ||||
Normal Store | StoreStore | StoreExit | ||||
Volatile Load | LoadLoad | LoadStore | LoadLoad | LoadStore | LoadEnter | LoadExit |
Volatile Store | StoreLoad | StoreStore | StoreEnter | StoreExit | ||
MonitorEnter | EnterLoad | EnterStore | EnterLoad | EnterStore | EnterEnter | EnterExit |
MonitorExit | ExitLoad | ExitStore | ExitEnter | ExitExit |
Plus the special final-field rule requiring a StoreStore barrier in:
x.finalField = v; StoreStore; sharedRef =
x;
加えて以下の例において,finalフィールドに関する特別なルールがStoreStoreバリアに特別なルールが要求される.(?)
x.finalField = v; StoreStore; sharedRef =x;
In this table, "Enter" is the same as "Load" and "Exit" is the same as "Store", unless overridden by the use and nature of atomic instructions. In particular:
この表において,アトミック命令の使用と本質によって上書きされない限り(?),"Enter"は"Load"に等しく,"Exit"は"Store"に等しい.特に;
The other types are specializations that are unlikely to play a role in compilation (see below) and/or reduce to no-ops on current processors. For example, EnterEnter is needed to separate nested MonitorEnters when there are no intervening loads or stores. Here's an example showing placements of most types:
もう一つのタイプは,コンパイル中で役割を果たしそうになく(下の例を参照),そして現在のプロセッサ上では no-opsに縮小される特殊化である.例えば,インターリーブされたロードとストアが無い時に,複数のネストされた MonitorEnterを分離するのに EnterEnterが必要である.以下に,ほとんどのパターンを含む(?)例を示す.
Java |
Instructions |
class X { int a; volatile int v; void f() { int i; synchronized(this) { i = a; a = i; } synchronized(this) { synchronized(this) { } } i = v; synchronized(this) { } v = i; synchronized(this) { } } } |
enter EnterLoad EnterStore load a store a LoadExit StoreExit exit ExitEnter enter EnterEnter enter EnterExit exit ExitExit exit ExitEnter ExitLoad load v LoadEnter enter EnterExit exit ExitEnter ExitStore store v StoreEnter enter EnterExit exit |
Java-level access to atomic conditional update operations will be available in JDK1.5 via JSR-166 (concurrency utilities) so compilers will need to issue associated code, using a variant of the above table that collapses MonitorEnter and MonitorExit -- semantically, and sometimes in practice, these Java-level atomic updates act as if they are surrounded by locks.
Javaレベルでアクセスできるアトミックな条件付き更新操作はJDK1.5の JSR-166(concurrency utilities) で利用可能になる.そのため,コンパイラは関連するコードを出力する必要があり, MonitorEnterと MonitorExitを押し潰す上の表のバリエーションを使って,−セマンティクス的に,時には実践的に,これらの Javaレベルのアトミックな更新は,まるでロックで取り囲まれたかのように動作する.
Here's a listing of processors that are commonly used in MPs, along with links to documents providing information about them. (Some require some clicking around from the linked site and/or free registration to access manuals). This isn't an exhaustive list, but it includes processors used in all current and near-future multiprocessor Java implementations I know of. The list and the properties of processors decribed below are not definitive. In some cases I'm just reporting what I read, and could have misread. Several reference manuals are not very clear about some properties relevant to the JMM. Please help make it definitive.
ここに,マルチプロセッサで良く使われるプロセッサ一覧を,ドキュメントへのリンクも付けて示す.(うち幾つかは,マニュアルにアクセスするために,リンクされたサイトでの何度かのクリックと無料登録を要求する.)これは網羅的なリストではないとは言え,私が知る限り現在又は近い将来においてJavaが実装されるのに使われる全てのプロセッサを含んでいる.このリストとそこで記述されているプロセッサの特徴は決定版とは言えない.幾つかの例では,私が読んだ物をただ報告しているだけなので,誤解しているかもしれない.幾つかのリファレンスマニュアルではJMM関係の特徴について分かりやすくはなっていない.読者の方は,これを決定版にするのに力を貸して欲しい.
Good sources of hardware-specific information about barriers and related properties of machines not listed here are Hans Boehm's atomic_ops library, the Linux Kernel Source, and Linux Scalability Effort. Barriers needed in the linux kernel correspond in straightforward ways to those discussed here, and have been ported to most processors. For descriptions of the underlying models supported on different processors, see Sarita Adve et al, Recent Advances in Memory Consistency Models for Hardware Shared-Memory Systems and Sarita Adve and Kourosh Gharachorloo, Shared Memory Consistency Models: A Tutorial.
ここに記載されていないマシンの,バリアとそれに関係する特徴のハードウエア固有の情報についての良い資料には, Hans Boehm's atomic_ops library (Hans Boehm氏による atomic_opsライブラリ)と Linux Kernel Source (Linuxカーネルのソースコード), Linux Scalability Effort がある. linuxカーネル内で必要となるバリアは,直接的にここで議論されたものと合致するし,多くのプロセッサ上へ移植されている.異なるプロセッサ上でサポートされている基礎を成すモデルの記述は, Sarita Adve et al, Recent Advances in Memory Consistency Models for Hardware Shared-Memory Systems (ハードウエア 共有メモリシステムのためのメモリ一貫性モデルの最近の進歩)と Sarita Adve and Kourosh Gharachorloo, Shared Memory Consistency Models: A Tutorial (共有メモリ一貫性モデル:チュートリアル)を参照すること.
Here's how these processors support barriers and atomics:
ここにこれらのプロセッサがバリアとアトミック命令をどのようにサポートするか示す.
Processor | LoadStore | LoadLoad | StoreStore | StoreLoad | Data dependency orders? |
Atomic Conditional |
Other Atomics |
Atomics provide barrier? |
sparc-TSO | no-op | no-op | no-op | membar (StoreLoad) |
yes | CAS: casa |
swap, ldstub |
full |
x86-PO | no-op | no-op | no-op | mfence or cpuid or locked insn |
yes | CAS: cmpxchg |
xchg, locked insn |
full |
x86-SPO | no-op | lfence | no-op | mfence | yes | CAS: cmpxchg |
xchg, locked insn |
full |
ia64 | combine with st.rel or ld.acq |
ld.acq | st.rel | mf | yes | CAS: cmpxchg |
xchg, fetchadd |
target + acq/rel |
ppc | dependency or isync |
dependency plus isync |
mbar eieio lwsync |
msync sync |
yes | LL/SC: ldarx/stwcx |
target only |
|
alpha | mb | mb | wmb | mb | no | LL/SC: ldx_l/stx_c |
target only |
|
pa-risc | no-op | no-op | no-op | no-op | yes | build from ldcw |
ldcw | (NA) |
If you are generating code that is guaranteed to only run on a uniprocessor, then you can probably skip the rest of this section. Because uniprocessors preserve apparent sequential consistency, you never need to issue barriers unless object memory is somehow shared with asynchrononously accessible IO memory. This might occur with specially mapped java.nio buffers, but probably only in ways that affect internal JVM support code, not Java code. Also, it is conceivable that some special barriers would be needed if context switching doesn't entail sufficient synchronization.
もし,あなたが作っているコードが単一プロセッサ上で実行されることが保証されるなら,この章の残りはスキップしても構わない.何故なら単一プロセッサ環境では明らかに順序整合性(*) (sequential consistency) が保証されるので,非同期でアクセス可能なIOメモリとオブジェクトメモリとが何らかの形で共有されない限り,バリアを発行する必要がないからだ.これは特別にマップされた java.nio.buffer で起こる可能性のあるものだが, JVM内部でサポートするコードに影響を与えるかもしれないだけで Javaコードには影響はない.またコンテキストスイッチが十分な同期を含んでいない場合は,特別なバリアが必要になることも考えられる.(*)
Barrier instructions apply between different kinds of accesses as they occur during execution of a program. Finding an "optimal" placement that minimizes the total number of executed barriers is all but impossible. Compilers often cannot tell if a given load or store will be preceded or followed by another that requires a barrier; for example, when a volatile store is followed by a return. The easiest conservative strategy is to assume that the kind of access requiring the "heaviest" kind of barrier will occur when generating code for any given load, store, lock, or unlock:
バリア命令はプログラムの実行中に発生する,異なる種類のアクセスの間に適用される.実行されるバリアの総数を最小化する「最適」な設置場所の特定は,ほとんど不可能だ.コンパイラはしばしばバリアが必要な何かに対し,定められたload/storeが先行するか後に続くかどうか分からない.例えばリターンに続く volatileストアがそうだ.最も簡単で保守的な戦略は,定められたload, store, lock,unlockのコードを生成する時はいつも,「最も重い」種類のバリアを要求するある種のアクセスが発生すると仮定することだ.
Many of these barriers usually reduce to no-ops. In fact, most of them reduce to no-ops, but in different ways under different processors and locking schemes. For the simplest examples, basic conformance to JSR-133 on x86-PO or sparc-TSO using CAS for locking amounts only to placing a StoreLoad barrier after volatile stores.
これらのバリアの多くは削減されて no-opになる.事実,多くは削減されて no-opになるが,異なるプロセッサとロックスキーム下においては様々な手法が存在する(?).最も単純な例では, x86-PO又は sparc-TSO上でロックにCASを用いて JSR-133に基本的に準拠すると, 結局は volatileストアの後に StoreLoadバリアを設置するだけになる.
The conservative strategy above is likely to perform acceptably for many programs. The main performance issues surrounding volatiles occur for the StoreLoad barriers associated with stores. These ought to be relatively rare -- the main reason for using volatiles in concurrent programs is to avoid the need to use locks around reads, which is only an issue when reads greatly overwhelm writes. But this strategy can be improved in at least the following ways:
上記の保守的な戦略は,多くのプログラムにおいて許容できるだろう. volatile変数を巡る主なパフォーマンス問題は,ストアに関連する StoreLoadバリアにおいて発生する.これは比較的少ないハズだ.なぜなら,並列プログラムで volatile変数を使う主な理由は,読み込みに関連してロックを使用する必要性を避けるためだからだ.書き込みより読み込みの方が圧倒的に多い時の,それは単なる事実だ(?).しかしこの戦略は,少なくとも以下のようなやり方で改善しうる.
Original | => | Transformed | ||||
1st | ops | 2nd | => | 1st | ops | 2nd |
LoadLoad | [no loads] | LoadLoad | => | [no loads] | LoadLoad | |
LoadLoad | [no loads] | StoreLoad | => | [no loads] | StoreLoad | |
StoreStore | [no stores] | StoreStore | => | [no stores] | StoreStore | |
StoreStore | [no stores] | StoreLoad | => | [no stores] | StoreLoad | |
StoreLoad | [no loads] | LoadLoad | => | StoreLoad | [no loads] | |
StoreLoad | [no stores] | StoreStore | => | StoreLoad | [no stores] | |
StoreLoad | [no volatile loads] | StoreLoad | => | [no volatile loads] | StoreLoad |
JSR-133 also addresses a few other issues that may entail barriers in more specialized cases:
またJSR-133は,より特殊な場合にバリアを引き起こすかもしれない,その他の幾つかの問題についても述べている.
Thanks to Bill Pugh, Dave Dice, Jeremy Manson, Kourosh Gharachorloo, Tim Harris, Cliff Click, Allan Kielstra, Yue Yang, Hans Boehm, Kevin Normoyle, Juergen Kreileder, Alexander Terekhov, Tom Deneau, Clark Verbrugge, and Peter Kessler for corrections and suggestions.
記載予定
Toru Takahashi,Yasuhiro Endoh,