メモリ整合性モデル、またはメモリモデルは、SharedArrayBufferに基づくTypedArrayインスタンスへのアクセスや、Atomicsオブジェクトのメソッドによる、共有データブロックイベントの制御をおこないます。プログラムにデータ競合がない場合(以下に定義)、イベントの順序は順次一貫しているように見えます。つまり、各エージェントからのアクションのインターリーブです。データ競合がある場合、共有メモリ操作は順番に一貫性がないことがあります。 たとえば、プログラムが因果関係に違反する行動やその他の問題をおこすことがあります。これらの問題は、コンパイラーの変換とCPUの設計(制御外からの実行や推測など)から生じます。メモリモデルは、プログラムが順次一貫した動作をおこなう条件と、データ競合から読み取られる値の両方を定義します。つまり、未定義の動作はありません。
メモリモデルは、SharedArrayBufferの抽象演算や、評価中にAtomicsオブジェクトのメソッドによってセットされたイベントに対するリレーショナル制約として定義されます。
27.1 メモリモデルの基礎(Memory Model Fundamentals)
共有メモリアクセス(読み取りと書き込み)は、以下に定義するアトミックアクセスとデータアクセスの2つのグループに分けられます。アトミックアクセスは順次一貫性があります。つまり、エージェントクラスタ内のすべてのエージェントによってイベントの順序が決定されます。非アトミックアクセスは、各エージェントによって決定された順序付けはありません。つまり、順序付けされていません。
フィールド名 | 値 | 意味 |
---|---|---|
[[Order]] | SeqCst | Unordered |
イベントのメモリモデルによって保証される最も弱い順序。 |
[[NoTear]] | ブール値 | このイベントと同じ範囲の書き込みイベントからの読み取りを許可するどうか。 |
[[Block]] | 共有データブロック | イベントが動作するブロック。 |
[[ByteIndex]] | 非負の整数 | [[Block]]に読み込まれたバイトアドレス。 |
[[ElementSize]] | 非負の整数 | 読み取りのサイズ |
フィールド名 | 値 | 意味 |
---|---|---|
[[Order]] | SeqCst | Unordered | Init |
イベントのメモリモデルによって保証される最も弱い順序。 |
[[NoTear]] | ブール値 | このイベントが、このイベントと同じ範囲の読み取りイベントからの読み取りを許可するかどうか。 |
[[Block]] | 共有データブロック | イベントが動作するブロック。 |
[[ByteIndex]] | 非負の整数 | [[Block]]への書き込みのバイトアドレス。 |
[[ElementSize]] | 非負の整数 | 書き込みのサイズ。 |
[[Payload]] | リスト | 他のイベントによって読み取られるバイト値のリスト。 |
フィールド名 | 値 | 意味 |
---|---|---|
[[Order]] | SeqCst | Read-modify-writeイベントは常に順次一貫性があります。 |
[[NoTear]] | true | Read-modify-writeイベントは破棄できません。 |
[[Block]] | 共有データブロック | イベントが動作するブロック。 |
[[ByteIndex]] | 非負の整数 | [[Block]]のread-modify-writeのバイトアドレス。 |
[[ElementSize]] | 非負の整数 | read-modify-writeのサイズ |
[[Payload]] | リスト | [[ModifyOp]]に渡されるバイト値のリスト。 |
[[ModifyOp]] | セマンティック関数 | 読み取られたバイト値のリストと[[Payload]]から変更されたバイト値のリストを返す純粋なセマンティック関数。 |
これらのイベントは、抽象操作またはAtomicsオブジェクトのメソッドによって導入されます。
Synchronizeイベント
一部の操作では、同期イベントが発生する場合があります。 Synchronizeイベントにはフィールドがなく、純粋に他のイベントの順序を直接制御するために存在します。
共有データブロックイベントとSynchronizeイベントに加えて、ホスト固有のイベントがあります。
ReadSharedMemory、WriteSharedMemory、ReadModifyWriteSharedMemoryイベントの範囲を、[[ByteIndex]]から[[ByteIndex]] + [[ElementSize]]-1までの連続する整数のセットとします。イベントの[[Block]]が同じで、範囲が要素ごとに等しい場合、2つのイベントの範囲は等しくなります。イベントの[[Block]]が同じで、範囲が等しくなく、それらの交差が空でない場合、2つのイベントの範囲は重複しています。イベントの[[Block]]が同じでない場合、またはそれらの範囲が等しくないか重複していない場合、2つのイベントの範囲は互いに素です。
イベントは、候補の実行内で、以下に定義されている関係によって順序付けられます。
27.2 エージェントイベントレコード(Agent Events Records)
エージェントイベントレコードは、次のフィールドを持つレコードです。
フィールド名 | 値 | 意味 |
---|---|---|
[[AgentSignifier]] | 同値かどうか比較するための値 | 順序付けの結果、評価されたエージェント。 |
[[EventList]] | イベントのリスト | 評価中にリストにイベントが追加されます。 |
[[AgentSynchronizesWith]] | Synchronizeイベントのペアのリスト | 操作的意味論によって導入されたリレーションを同期します |
27.3 選択値レコード(Chosen Value Records)
選択値レコードは、次のフィールドを持つレコードです。
フィールド名 | 値 | 意味 |
---|---|---|
[[Event]] | 共有データブロックイベント | この選択値で導入されたReadSharedMemoryまたはReadModifyWriteSharedMemoryイベント。 |
[[ChosenValue] | バイト値のリスト | 評価中に非決定論的に選択されたバイト。 |
27.4 実行候補(Candidate Executions)
エージェントクラスタの評価の実行候補は、次のフィールドを持つレコードです。
フィールド名e | 値 | 意味 |
---|---|---|
[[EventsRecords]] | エージェントイベントレコードのリスト | 評価中に追加されたイベントのリストにエージェントをマップします。 |
[[ChosenValues]] | 選択値レコードのリスト | ReadSharedMemoryまたはReadModifyWriteSharedMemoryイベントを、評価中に選択されたバイト値のリストにマップします。 |
[[AgentOrder]] | agent-orderのRelation | 以下に定義されています。 |
[[ReadsBytesFrom]] | reads-bytes-fromセマンティック関数 | 以下に定義されています。 |
[[ReadsFrom]] | reads-fromのRelation | 以下に定義されています。 |
[[HostSynchronizesWith]] | host-synchronizes-withのRelation | 以下に定義されています。 |
[[SynchronizesWith]] | synchronizes-withのRelation | 以下に定義されています。 |
[[HappensBefore]] | happens-beforeのRelation/td> | 以下に定義されています。 |
空の実行候補
空の実行候補は、フィールドがemptyリストとRelationである実行候補レコードです。
27.5 メモリモデルの抽象演算(Abstract Operations for the Memory Model)
27.5.1 EventSet ( execution )
抽象操作EventSetは、引数 実行候補 executionを取ります。 次の手順を実行します。
- empty Set型を events とする
- execution.[[EventsRecords]] の 各 エージェントイベントレコード 要素を aer とし、 aer ごとに次を実行する
- aer.[[EventList]] の 各イベント要素を E とし、 E ごとに次を実行する
- events に E を追加
- aer.[[EventList]] の 各イベント要素を E とし、 E ごとに次を実行する
- events を返す
27.5.2 SharedDataBlockEventSet ( execution )
抽象操作SharedDataBlockEventSetは、引数 実行候補 executionを取ります。 次の手順を実行します。
- empty Set型を events とする
- EventSet(execution) の 各イベント要素を E とし、 E ごとに次を実行する
- E が イベント ReadSharedMemory 、 WriteSharedMemory 、 ReadModifyWriteSharedMemory のどれかなら events に E を追加する
- events を返す
27.5.3 HostEventSet ( execution )
抽象操作HostEventSetは、引数 実行候補 execution を取ります。 次の手順を実行します。
- empty Set型を events とする
- EventSet(execution) の 各イベント要素を E とし、 E ごとに次を実行する
- E が SharedDataBlockEventSet(execution) に含まれていないなら、 events に E を追加する
- events を返す
27.5.4 ComposeWriteEventBytes ( execution, byteIndex, Ws )
抽象操作ComposeWriteEventBytesは、4つの引数、実行候補 execution、非負の整数byteIndex、およびWriteSharedMemoryイベントまたはReadModifyWriteSharedMemoryイベントのリストWsを取ります。 次の手順を実行します。
- byteIndex を byteLocation とする
- 空の新規List を bytesRead とする
- Ws の各要素を W とし、リスト中の順番で W ごとに次を実行する
- Assert: W の範囲に byteLocation がある
- byteLocation - W.[[ByteIndex]] を payloadIndex とする
- W が WriteSharedMemory イベント なら、
- W.[[Payload]][payloadIndex] を byte とする
- c. と異なるなら、
- Assert: W は ReadModifyWriteSharedMemory イベント
- ValueOfReadEvent(execution, W) を bytes とする
- W.[[ModifyOp]](bytes, W.[[Payload]]) を bytesModified とする
- bytesModified[payloadIndex] を byte とする
- bytesRead に byte を追加する
- byteLocation + 1 を byteLocation にセットする
- bytesRead を返す
27.5.5 ValueOfReadEvent ( execution, R )
抽象操作ValueOfReadEventは、実行候補 executionと、ReadSharedMemoryイベントまたはReadModifyWriteSharedMemoryイベントRの2つの引数を取ります。これは、次の手順を実行します。
- Assert: R は ReadSharedMemory または ReadModifyWriteSharedMemory イベント
- execution.[[ReadsBytesFrom]](R) を Ws とする
- Assert: Ws は長さが R[[ElementSize]] と等しい WriteSharedMemory または ReadModifyWriteSharedMemory イベントのリスト
- ComposeWriteEventBytes(execution, R.[[ByteIndex]], Ws) を返す
27.6 実行候補のリレーション(Relations of Candidate Executions)
27.6.1 agent-order
実行候補 executionにおいて、execution.[[AgentOrder]]は、以下を満たすイベントのRelationです。
- EventSet(execution)の各ペアを(E,D)とする。execution.[[EventsRecords]]の各エージェントイベントレコードをaerとする。このとき、aer.[[EventList]]リスト内にEとDがあり、リスト上でEがDの前にある場合、(E,D)はexecution.[[AgentOrder]]にある
27.6.2 reads-bytes-from
実行候補 executionにおいて、execution.[[ReadsBytesFrom]]は、下記の条件を満たすイベントのリストを生成するセマンティック関数です。
- SharedDataBlockEventSet(execution)の各イベント(ReadSharedMemoryまたはReadModifyWriteSharedMemory)をRとする。WriteSharedMemoryとReadModifyWriteSharedMemoryの複数イベントをWsとする。execution.[[ReadsBytesFrom]](R)は、下記を満たすイベントの中でR.[[ElementSize]]にと長さが等しいイベントのリストである。
- Ws中の各イベントWは、インデックスをiとすると、その範囲にR.[[ByteIndex]] + iがある。
- Rは対象外
27.6.3 reads-from
実行候補 executionにおいて、execution.[[ReadsFrom]]は、以下を満たすイベントの最小のRelationです。
- SharedDataBlockEventSet(execution) の各ペアを (R,W) としたとき、W が execution.[[ReadsBytesFrom]](R) に含まれているなら、(R,W)は execution.[[ReadsFrom]] にある。
27.6.4 host-synchronizes-with
実行候補 executionにおいて、execution.[[HostSynchronizesWith]] は、以下を満たす、ホスト提供のホスト固有のイベントに対する厳密な部分的順序です。
- execution.[[HostSynchronizesWith]] に (E, D) が含まれるなら、E と D は、 HostEventSet(execution) に含まれている
- execution.[[HostSynchronizesWith]]とexecution.[[AgentOrder]]の結合にはサイクルがありません
27.6.5 synchronizes-with
実行候補 executionにおいて、execution.[[SynchronizesWith]]は、以下を満たす、イベントの最小のRelationです。
- execution.[[ReadsFrom]] の各ペア (R, W) としたとき、R.[[Order]] と W.[[Order]] が両方とも SeqCst で、R と W の範囲が同じなら、execution.[[SynchronizesWith]] に (W, R) が含まれる
- execution.[[EventsRecords]] の各要素を eventsRecord としたとき、次の条件が満たされる
- eventsRecord.[[AgentSynchronizesWith]] の 各ペアを (S, Sw) としたとき、execution.[[SynchronizesWith]] に (S, Sw) が含まれる
27.6.6 happens-before
実行候補 executionにおいて、execution.[[HappensBefore]]は、以下を満たすイベントの最小のRelationです。
- execution.[[AgentOrder]] の各ペアを (E, D) としたとき、(E, D) は、execution.[[HappensBefore]] に含まれる
- execution.[[SynchronizesWith]] の各ペアを (E, D) としたとき、(E, D) は、execution.[[HappensBefore]] に含まれる
- SharedDataBlockEventSet(execution) の各ペアを (E, D) としたとき、E.[[Order]] がInitで、E と D の範囲が重複しているなら、(E, D) は、execution.[[HappensBefore]] に含まれる
- EventSet(execution) の各ペアを (E, D) としたとき、ペア(E, F) とペア (F, D)が execution.[[HappensBefore]] に含まれるような F があるなら、(E, D) は、execution.[[HappensBefore]] に含まれる
27.7 有効な実行のプロパティ(Properties of Valid Executions)
27.7.1 有効な選択読み取り(Valid Chosen Reads)
次の抽象操作がtrueを返す場合、実行候補 execution には有効な選択された読み取りがあります。
- SharedDataBlockEventSet(execution) の 各 ReadSharedMemory または ReadModifyWriteSharedMemory イベント要素を R とし、 R ごとに次を実行する
- [[Event]]フィールドが R である execution.[[ChosenValues]]の要素を chosenValueRecord とする
- chosenValueRecord.[[ChosenValue]] を chosenValue とする
- ValueOfReadEvent(execution, R) を readValue とする
- chosenValue の要素数を chosenLen とする
- readValue の要素数を readLen とする
- chosenLen と readLen が等しくないなら、
- false を返す
- i を 0から chosenLen -1 の範囲の整数値としたとき、chosenValue[i] と readValue[i] が等しくないなら、
- false を返す
- true を返す
27.7.2 一貫した読み取り(Coherent Reads)
次の抽象操作がtrueを返す場合、実行候補 execution には一貫した読み取りがあります。
- SharedDataBlockEventSet(execution) の 各 ReadSharedMemory または ReadModifyWriteSharedMemory イベント要素を R とし、 R ごとに次を実行する
- execution.[[ReadsBytesFrom]](R) を Ws とする
- R.[[ByteIndex]] を byteLocation とする
- Ws 各要素を W とし、リスト内の順番で W ごとに次を実行する
- (R, W) が execution.[[HappensBefore]] に含まれているなら、
- false を返す
- 各WriteSharedMemoryまたはReadModifyWriteSharedMemoryイベントを V としたとき、(W, V) と (V, R)のペアが execution.[[HappensBefore]] に含まれる範囲内に byteLocation があるなら、
- false を返す
- byteLocation + 1 を byteLocation にセットする
- (R, W) が execution.[[HappensBefore]] に含まれているなら、
- true を返す
27.7.3 ティアフリー読み込み(Tear Free Reads)
次の抽象操作がtrueを返す場合、実行候補 execution にはティアフリー読み取りがあります。
- SharedDataBlockEventSet(execution) の 各 ReadSharedMemory または ReadModifyWriteSharedMemory イベント要素を R とし、 R ごとに次を実行する
- R.[[NoTear]] が true なら、
- Assert: R.[[ByteIndex]] を R.[[ElementSize]] で割った余りは0
- (R, W) が execution.[[ReadsFrom]] に含まれ、W.[[NoTear]] が true を満たす各 W 毎に、次を実行する
- R と W の範囲が等しいとき、W と範囲が等しい各イベントを V とし、V.[[NoTear]] が true で、execution.[[ReadsFrom]] に (R, V) が含まれる V があるなら
- false を返す
- R と W の範囲が等しいとき、W と範囲が等しい各イベントを V とし、V.[[NoTear]] が true で、execution.[[ReadsFrom]] に (R, V) が含まれる V があるなら
- R.[[NoTear]] が true なら、
- true を返す
直感的には、この要件は、整数TypedArrayを介してメモリ範囲に整列してアクセスする場合、同じ範囲の他の書き込みイベントとのデータ競合時に、その範囲の単一の書き込みイベントが「勝つ」必要があることを示しています。より正確には、この要件は、整列された読み取りイベントが、すべて等しい範囲の複数の異なる書き込みイベントからのバイトで構成される値を読み取ることができないことを示しています。ただし、整列された読み取りイベントが、範囲が重複する複数の書き込みイベントから読み取ることは可能です。
27.7.4 逐次一貫性のあるAtomics(Sequentially Consistent Atomics)
実行候補 executionにおいて、memory-orderは、EventSet(execution)内のすべてのイベントの厳密な総順序であり、次の条件を満たします。
- ペア(E, D)が、 execution.[[HappensBefore]] に含まれるなら、(E, D)は、memory-orderです
- execution.[[ReadsFrom]] の各ペアを (R, W) としたとき、SharedDataBlockEventSet(execution) には、V.[[Order]] ] が SeqCst で、ペア(W, V) と (V, R) が memory-order に含まれ、以下の条件のどれかが当てはまるような、WriteSharedMemoryまたはReadModifyWriteSharedMemoryイベント V がありません。
- execution.[[SynchronizesWith]] に ペア(W, R) が含まれ、V と R は同じ範囲
- execution.[[HappensBefore]] に ペア (W, R) と (V, R) が含まれ、W.[[Order]] が SeqCst で、W と V は同じ範囲
- execution.[[HappensBefore]] に ペア (W, R) と (W, V) が含まれ、R.[[Order]] が SeqCst で、V と R は同じ範囲
この条件はさらに、SeqCstイベントを等しい範囲に制限します。 - SharedDataBlockEventSet(execution) の 各 WriteSharedMemory または ReadModifyWriteSharedMemory イベントを W としたとき、W.[[Order]] が SeqCst なら、SharedDataBlockEventSet(execution) に、W より前のmemory-orderと等しい範囲のReadSharedMemoryまたはReadModifyWriteSharedMemoryイベントの数は、無限ではありません。
この条件はさらに、エージェントの順方向進行を保証するとともに、SeqCst書き込みが有限時間で同じ範囲のSeqCst読み取りに表示される活性条件を保証します。
memory-orderが存在する場合、実行候補には逐次一貫性のあるアトミックがあります。
27.7.5 有効な実行(Valid Executions)
次のすべてが当てはまる場合、実行候補 executionは有効な実行(または単に実行)です。
- ホストは、 execution.[[HostSynchronizesWith]]のsynchronizes-with Relationを提供します。
- execution.[[HappensBefore]]は、厳密な部分的順序です
- executionは、有効な選択読み取り(Valid Chosen Reads)です
- executionは、一貫した読み取り(Coherent Reads)です
- executionは、ティアフリー読み込み(Tear Free Reads)です
- executionは、逐次一貫性のあるAtomics(Sequentially Consistent Atomics)です
すべてのプログラムには、少なくとも1つの有効な実行があります。
27.8 競合(Races)
次の抽象操作がtrueを返す場合、SharedDataBlockEventSet(execution)の2つのイベントEとDが競合しています。
- E が D でないなら、
- execution.[[HappensBefore]] に ペア (E, D) と (D, E) が含まれないなら、
- E と D の両方が WriteSharedMemory または ReadModifyWriteSharedMemory イベントで E と D に互いに素な範囲がないなら、
- true を返す
- (E, D) か (D, E) のどちらかが execution.[[ReadsFrom]] に含まれるなら、
- true を返す
- E と D の両方が WriteSharedMemory または ReadModifyWriteSharedMemory イベントで E と D に互いに素な範囲がないなら、
- execution.[[HappensBefore]] に ペア (E, D) と (D, E) が含まれないなら、
- false を返す
27.9 データ競合(Data Races)
次の抽象操作がtrueを返す場合、SharedDataBlockEventSet(execution)の2つのイベントEとDがデータ競合状態にあります。
- E と D が execution 内で競合しているなら、
- E.[[Order]] が SeqCst ではない、または D.[[Order]] が SeqCst でないなら、
- true を返す
- E and D の範囲が重複しているなら、
- true を返す
- E.[[Order]] が SeqCst ではない、または D.[[Order]] が SeqCst でないなら、
- false を返す
27.10 データ競合の自由(Data Race Freedom)
SharedDataBlockEventSet(execution)にデータ競合の2つのイベントがない場合、executionはデータ競合が発生しません。
すべての実行がデータ競合なしである場合、プログラムはデータ競合がありません。
メモリモデルは、データ競合のないプログラムのすべてのイベントの逐次一貫性を保証します。
27.11 共有メモリのガイドライン(Shared Memory Guidelines)
プログラムはデータの競合がない状態に保つことをお勧めします。つまり、同じメモリ位置で非アトミック操作が同時に発生することが不可能になるようにします。 データ競合のないプログラムには、各エージェントの評価セマンティクスの各ステップが相互にインターリーブされるインターリーブセマンティクスがあります。 データ競合のないプログラムの場合、メモリモデルの詳細を理解する必要はありません。 詳細は、ECMAScriptをよりよく書くのに役立つ直感を構築する可能性は低いです。
より一般的には、プログラムにデータ競合がない場合でも、アトミック操作がデータ競合に関与せず、競合する操作がすべて同じアクセスサイズである限り、予測可能な動作が発生する可能性があります。 アトミックがレースに関与しないように調整する最も簡単な方法は、アトミック操作と非アトミック操作で異なるメモリセルが使用され、同じセルに同時にアクセスするために異なるサイズのアトミックアクセスが使用されないようにすることです。 事実上、プログラムは共有メモリを可能な限り強く型付けされたものとして扱う必要があります。 それでも、そのレースの非アトミックアクセスの順序とタイミングに依存することはできませんが、メモリが強く型付けされたものとして扱われる場合、レースアクセスは「引き裂かれ」ません(値のビットが混合されません)。
マルチエージェントプログラムの各エージェントのパフォーマンスがシングルエージェント設定の場合と同じくらい良好であることを保証するために、マルチエージェント設定中のシングルエージェント設定で有効なほとんどのプログラム変換を許可することが望ましいです。多くの場合、これらの変換は判断が困難です。 規範的であると見なされることを意図しているが(メモリモデルによって暗示されている、またはメモリモデルが暗示しているものよりも強力であるという点で)、プログラム変換に関するいくつかのルールの概要を説明します。これらのルールは、agent-orderを構成するイベントの導入に先行するプログラム変換に適用することを目的としています。
エージェントオーダースライスを、単一のエージェントに関連するagent-orderのサブセットとします。
読み取りイベントの読み取り値を、すべての有効な実行に関わるイベントのValueOfReadEventのすべての値のセットとします。
共有メモリがない場合に有効なエージェントオーダースライスの変換は、次の例外を除いて、共有メモリがある場合に有効です。
- アトミックは石に刻まれています。プログラム変換によって、エージェントオーダースライス内のSeqCstイベントが順序付けされていない操作で並べ替えられたり、SeqCst操作が相互に並べ替えられたりすることはありません。また、プログラム変換によってagent-orderからのSeqCst操作が削除されることもありません。
(実際には、並べ替えの禁止により、コンパイラはすべてのSeqCst操作が同期し、最終的なmemory-orderに含まれていると見なす必要があります。これは通常、エージェント間プログラム分析がない場合に想定する必要があります。 コンパイラは、呼び出し先のmemory-orderへの影響が不明なすべての呼び出しにSeqCst操作が含まれている可能性があると想定します。)
- 読み取りは安定している必要があります。特定の共有メモリの読み取りでは、実行時に1つの値のみを監視する必要があります。
(たとえば、プログラムで意味的に1回の読み取りが複数回実行された場合、プログラムはその後、読み取られた値の1つのみを監視できます。再実体化と呼ばれる変換はこのルールに違反する可能性があります。)
- 書き込みは安定している必要があります。共有メモリへのすべての監視可能な書き込みは、実行中のプログラムセマンティクスに従う必要があります。
(たとえば、変換では、大きな場所でread-modify-write操作を使用して小さなデータを書き込む、プログラムが書き込めなかった値をメモリに書き込む、またはジャストを書き込むなど、特定の監視可能な書き込みが導入されない場合があります。 読み取り後に別のエージェントによってその場所が上書きされた可能性がある場合は、値を読み取り元の場所に戻します。)
- 読み取り値は空でない必要があります。プログラム変換によって、共有メモリ読み取りの読み取り値が空になることはありません。
(直感に反して、このルールは事実上、書き込みの変換を制限します。これは、書き込みが読み取りイベントによって読み取られる限り、メモリモデルに力があるためです。たとえば、書き込みは2つのSeqCst操作間で移動および合体され、場合によっては並べ替えられますが、変換は行われない場合があります。 場所を更新するすべての書き込みを削除します。一部の書き込みは保持する必要があります。)
有効な変換の例は、同じ場所からの複数の非アトミック読み取りのマージ、非アトミック読み取りの並べ替え、投機的非アトミック読み取りの導入、同じ場所への複数の非アトミック書き込みのマージ、異なる場所への非アトミック書き込みの並べ替えです。 位置、およびそれが終了に影響を与える場合でも、ループからの非アトミック読み取りを引き上げます。 一般に、エイリアスされたTypedArrayは、場所が異なることを証明するのが難しいことに注意してください。
ARMまたはPowerのメモリモデルよりも弱いメモリモデルを備えたアーキテクチャの場合、非アトミックストアおよびロードは、ターゲットアーキテクチャ上のベアストアおよびロードにコンパイルされる場合があります。アトミックストアとロードは、逐次一貫性を保証する命令にコンパイルされる場合があります。 そのような命令が存在しない場合は、ベアストアまたはロードの両側にバリアを配置するなど、メモリバリアを使用する必要があります。read-modify-write操作は、x86のLOCKプレフィックス付き命令、ARMのload-exclusive/store-exclusive命令、Powerのload-link/store-conditional命令など、ターゲットアーキテクチャのread-modify-write命令にコンパイルできます。
具体的には、メモリモデルは、次のようなコード生成を可能にすることを目的としています。
- プログラム内のすべてのアトミック操作が必要であると想定されています。
- アトミック操作は、相互に、または非アトミック操作で再配置されることはありません。
- 関数は常にアトミック操作を実行すると想定されています。
- アトミック操作は、より大きなデータに対するread-modify-write操作として実装されることはありません。ただし、プラットフォームに適切なサイズのアトミック操作がない場合は、ロックフリーではないアトミックとして実装されます。 (すべてのプラットフォームには、興味深いサイズの通常のメモリアクセス操作があると既に想定しています。)
ナイーブなコード生成では、次のパターンを使用します。
- 通常のロードおよびストアは、単一のロードおよびストア命令にコンパイルされます。
- ロックフリーのアトミックロードおよびストアは、完全な(順次一貫性のある)フェンス、通常のロードまたはストア、および完全なフェンスにコンパイルされます。
- ロックフリーのアトミックリードモディファイライトアクセスは、フルフェンス、アトミックread-modify-write命令シーケンス、およびフルフェンスにコンパイルされます。
- 非ロックフリーアトミックは、スピンロック取得、フルフェンス、一連の非アトミックロードおよびストア命令、フルフェンス、およびスピンロックリリースにコンパイルされます。
アドレス範囲でのアトミック操作が非アトミック書き込みまたは異なるサイズのアトミック操作と競合しない限り、そのマッピングは正しいです。 ただし、必要なのはそれだけです。メモリモデルは、競合に関係するアトミック操作を非アトミックステータスに効果的に降格します。 一方、単純なマッピングは非常に強力です。これにより、アトミック操作を逐次一貫性のあるフェンスとして使用できますが、これはメモリモデルでは実際には保証されません。
これらの基本的なパターンに対するいくつかのローカルな改善も有効であることが意図されています。
- 冗長なフェンスを削除する、プラットフォームに依存する明らかな改善があります。 たとえば、x86では、ストアに続くフェンスを除いて、ロックフリーのアトミックロードとストアの周囲のフェンスは常に省略できます。ロックフリーの読み取り-変更-書き込み命令にはフェンスは必要ありません。これらはすべてLOCKプレフィックスを使用しているためです。 指示。 多くのプラットフォームにはいくつかの長所のフェンスがあり、逐次一貫性を損なうことなく、特定のコンテキストでより弱いフェンスを使用できます。
- 最新のプラットフォームのほとんどは、ECMAScriptアトミックに必要なすべてのデータサイズに対してロックフリーアトミックをサポートしています。 ロックフリーでないアトミックが必要な場合、アトミック操作の本体を囲むフェンスは通常、ロックおよびロック解除のステップに折りたたむことができます。 ロックフリーでないアトミックの最も簡単な解決策は、SharedArrayBufferごとに1つのロックワードを持つことです。
- プラットフォームに依存するより複雑なローカルの改善もあり、コード分析が必要です。 たとえば、2つの連続したフェンスは1つのフェンスと同じ効果を持つことが多いため、2つのアトミック操作に対してコードが順番に生成される場合、1つのフェンスだけでそれらを分離する必要があります。 x86では、ストアに続くフェンスはストアを後続のロードから分離するためにのみ必要であるため、アトミックストアを分離する単一のフェンスでも省略できます。