29 メモリーモデル (Memory Model)

メモリ一貫性モデル、または メモリモデル は、SharedArrayBuffer をバックとする TypedArray インスタンスへのアクセスや Atomics オブジェクトのメソッドを通じて発生する Shared Data Block イベントの可能な順序付けを規定します。プログラムにデータ競合(下記で定義)がない場合、イベントの順序は逐次一貫性があるように見えます。すなわち、各エージェントの動作が交互に実行されるものとして現れます。プログラムにデータ競合がある場合は、共有メモリ操作は逐次一貫性がないように見えることがあります。例えば、プログラムは因果律に反する振る舞いや、他にも驚くべき現象を示すことがあります。これらの驚きは、コンパイラの変換や CPU 設計(例えばアウトオブオーダー実行や投機実行)に起因します。メモリモデルは、プログラムが逐次一貫性のある挙動を示すための正確な条件と、データ競合から読み取られる可能性のある値の両方を定めています。言い換えれば、「未定義動作」はありません。

メモリモデルは、SharedArrayBuffer に対する抽象操作や Atomics オブジェクトのメソッドによって評価中に導入される Memory イベントに対する関係的制約として定義されます。

Note

この節では、SharedArrayBuffer に対する抽象操作によって導入される Memory イベントに関する公理的モデルを提供します。特に強調すべきは、このモデルが他の仕様とは異なり、アルゴリズムとして表現できるものではないという点です。抽象操作によるイベントの非決定的な導入は、ECMAScript 評価の操作的セマンティクスとメモリモデルの公理的セマンティクスのインターフェースとなっています。これらイベントのセマンティクスは、評価中のすべてのイベントのグラフを考察することで定義されます。これらは静的セマンティクスでも実行時セマンティクスでもありません。アルゴリズム的な実装が示されているわけではなく、特定のイベントグラフが許可されるか否かを決定する一連の制約があるのみです。

29.1 メモリーモデルの基本 (Memory Model Fundamentals)

共有メモリアクセス(読み書き)は後述の定義に従い atomic アクセスと data アクセスの 2 群に分けられる。Atomic アクセスは逐次的一貫性を持つ、すなわちエージェントクラスタ内の全エージェントが合意する厳密な全順序が存在する。非 atomic アクセスは全エージェントが合意する厳密な全順序を持たず、すなわち unordered である。

Note 1

逐次的一貫性より弱く、unordered より強い順序(例: release-acquire)はサポートされない。

共有データブロックイベントは、ReadSharedMemoryWriteSharedMemory、またはReadModifyWriteSharedMemoryレコードのいずれかです。読み出しイベントは、ReadSharedMemory または ReadModifyWriteSharedMemory のいずれかです。書き込みイベントは、WriteSharedMemory または ReadModifyWriteSharedMemory のいずれかです。

Table 94: ReadSharedMemory イベントフィールド (ReadSharedMemory Event Fields)
フィールド名 意味
[[Order]] seq-cst または unordered メモリモデルがこのイベントに対して保証する最も弱い順序。
[[NoTear]] ブール値 このイベントが、自身と等しいメモリ範囲を持つ複数の書き込みイベントから読み取ることを許可されているかどうか。
[[Block]] 共有データブロック イベントが操作するブロック。
[[ByteIndex]] 0 以上の整数 [[Block]] 内での読み取りのバイトアドレス。
[[ElementSize]] 0 以上の整数 読み取りのサイズ。
Table 95: WriteSharedMemory イベントフィールド (WriteSharedMemory Event Fields)
フィールド名 意味
[[Order]] seq-cstunordered、または init メモリモデルがこのイベントに対して保証する最も弱い順序。
[[NoTear]] ブール値 このイベントが、自身と等しいメモリ範囲を持つ複数のリードイベントから読み取られることを許可されているかどうか。
[[Block]] 共有データブロック イベントが操作するブロック。
[[ByteIndex]] 0 以上の整数 [[Block]] 内での書き込みのバイトアドレス。
[[ElementSize]] 0 以上の整数 書き込みのサイズ。
[[Payload]] バイト値のリスト 他のイベントによって読み取られるバイト値のリスト。
Table 96: ReadModifyWriteSharedMemory イベントフィールド (ReadModifyWriteSharedMemory Event Fields)
Field Name Value Meaning
[[Order]] seq-cst Read-modify-write イベントは常に逐次的一貫性。
[[NoTear]] true Read-modify-write イベントは tear しない。
[[Block]] Shared Data Block イベントが作用するブロック。
[[ByteIndex]] 非負整数 [[Block]] 内での read-modify-write のバイト位置。
[[ElementSize]] 非負整数 read-modify-write のサイズ。
[[Payload]] バイト値List [[ModifyOp]] に渡されるバイト値List
[[ModifyOp]] read-modify-write 変更関数 読み取ったバイト値List[[Payload]] から変更後のバイト値List を返す抽象クロージャ。

共有データブロックイベントは、抽象操作や Atomics オブジェクトのメソッドによって、候補実行のエージェントイベントレコードに導入されます。一部の操作は 同期イベント も導入します。これらにはフィールドがなく、他のイベントの許可される順序を直接制約するためだけに存在します。最後に、ホスト固有のイベントも存在します。メモリエベントは、共有データブロックイベント、同期イベント、またはそのようなホスト固有イベントのいずれかです。

共有データブロックイベント eメモリ範囲 とは、e.[[ByteIndex]](含む)から e.[[ByteIndex]] + e.[[ElementSize]](含まない)までの区間に含まれるすべての整数の集合とする。二つのイベントのメモリ範囲は、両イベントが同じ [[Block]][[ByteIndex]][[ElementSize]] を持つ場合に等しいとする。二つのイベントのメモリ範囲は、両イベントが同じ [[Block]] を持ち、範囲が等しくなく、かつその共通部分が空でない場合に重複するとする。二つのイベントのメモリ範囲は、[[Block]] が異なる場合、または範囲が等しくも重複もしていない場合に互いに素であるとする。

Note 2

考慮すべきホスト固有同期イベント例:SharedArrayBuffer を一方のエージェントから他方へ送る(ブラウザでの postMessage など)、エージェントの開始と停止、共有メモリ以外のチャネルによるエージェントクラスタ内通信。特定の実行 execution において、それらイベントは host-synchronizes-with 厳密半順序を通じてホストにより提供される。さらにホストis-agent-order-before 関係に参加するため execution.[[EventList]]ホスト固有同期イベントを追加できる。

イベントは以下で定義する関係によって候補実行内で順序付けられる。

29.2 Agent Events レコード (Agent Events Records)

Agent Events Record は次のフィールドを持つ Record である。

Table 97: エージェントイベントレコードのフィールド
フィールド名 意味
[[AgentSignifier]] エージェント識別子 この順序付けをもたらした評価を行ったエージェント。
[[EventList]] メモリエベントのリスト イベントは評価中にこのリストに追加される。
[[AgentSynchronizesWith]] 同期イベントのペアのリスト 操作的意味論によって導入される同期関係。

29.3 Chosen Value レコード (Chosen Value Records)

Chosen Value Record は次のフィールドを持つ Record である。

Table 98: Chosen Value Record フィールド (Chosen Value Record Fields)
Field Name Value Meaning
[[Event]] Shared Data Block event この選択値のために導入された ReadSharedMemory または ReadModifyWriteSharedMemory イベント。
[[ChosenValue]] バイト値List 評価中に非決定的に選択されたバイト。

29.4 候補実行 (Candidate Executions)

candidate execution とは次のフィールドを持つエージェントクラスタ評価の Record である。

Table 99: 候補実行レコードのフィールド
フィールド名 意味
[[EventsRecords]] エージェントイベントレコードのリスト エージェントを、評価中に追加されたメモリエベントのリストへマッピングする。
[[ChosenValues]] 選択された値レコードのリスト ReadSharedMemory または ReadModifyWriteSharedMemory イベントを、評価中に選択されたバイト値のリストへマッピングする。

empty candidate execution はフィールドが空 List である candidate execution Record である。

29.5 メモリーモデル用抽象操作 (Abstract Operations for the Memory Model)

29.5.1 EventSet ( execution )

The abstract operation EventSet takes argument execution (候補実行) and returns メモリエベントの集合. It performs the following steps when called:

  1. events を空の集合とする。
  2. execution.[[EventsRecords]] の各エージェントイベントレコード aer について、次を行う
    1. aer.[[EventList]] の各メモリエベント E について、次を行う
      1. Eevents に追加する。
  3. events を返す。

29.5.2 SharedDataBlockEventSet ( execution )

The abstract operation SharedDataBlockEventSet takes argument execution (候補実行) and returns 共有データブロックイベントの集合. It performs the following steps when called:

  1. events を空の集合とする。
  2. EventSet(execution) の各メモリエベント E について、次を行う
    1. E共有データブロックイベントであれば、Eevents に追加する。
  3. events を返す。

29.5.3 HostEventSet ( execution )

The abstract operation HostEventSet takes argument execution (候補実行) and returns メモリエベントの集合. It performs the following steps when called:

  1. EventSet(execution) のすべての要素のうち、SharedDataBlockEventSet(execution) に含まれないものを集めた新しい Set を返す。

29.5.4 ComposeWriteEventBytes ( execution, byteIndex, Ws )

The abstract operation ComposeWriteEventBytes takes arguments execution (候補実行), byteIndex (0 以上の整数), and Ws (WriteSharedMemory または ReadModifyWriteSharedMemory イベントのリスト) and returns バイト値のリスト. It performs the following steps when called:

  1. byteLocationbyteIndex とする。
  2. bytesRead を新しい空のリストとする。
  3. Ws の各要素 W について、次を行う
    1. Wメモリ範囲byteLocation が含まれていることをアサートする。
    2. payloadIndexbyteLocation - W.[[ByteIndex]] とする。
    3. WWriteSharedMemory イベントである場合、
      1. byteW.[[Payload]][payloadIndex] とする。
    4. それ以外の場合、
      1. WReadModifyWriteSharedMemory イベントであることをアサートする。
      2. bytesValueOfReadEvent(execution, W) とする。
      3. bytesModifiedW.[[ModifyOp]](bytes, W.[[Payload]]) とする。
      4. bytebytesModified[payloadIndex] とする。
    5. bytebytesRead に追加する。
    6. byteLocationbyteLocation + 1 を設定する。
  4. bytesRead を返す。
Note 1

Read-Modify-Write の修飾 [[ModifyOp]] は、ReadModifyWriteSharedMemory イベントを導入する Atomics オブジェクト上の関数プロパティによって与えられる。

Note 2

この抽象操作は、書き込みイベントのリストをバイト値のリストに合成するものである。これは ReadSharedMemoryReadModifyWriteSharedMemory イベントのイベント意味論で使われる。

29.5.5 ValueOfReadEvent ( execution, R )

The abstract operation ValueOfReadEvent takes arguments execution (a candidate execution) and R (a ReadSharedMemory or ReadModifyWriteSharedMemory event) and returns a List of byte values. It performs the following steps when called:

  1. Wsexecution における reads-bytes-from(R) とする。
  2. 事前条件: WsR.[[ElementSize]] と同じ長さの WriteSharedMemory または ReadModifyWriteSharedMemory イベントの List
  3. ComposeWriteEventBytes(execution, R.[[ByteIndex]], Ws) を返す。

29.6 候補実行における関係 (Relations of Candidate Executions)

以下の関係および数学関数は、特定の候補実行にパラメータ化され、そのメモリエベントに順序を与えます。

29.6.1 is-agent-order-before

候補実行 execution に対し、その is-agent-order-before 関係は、次の条件を満たすメモリエベント上の最小の関係です。

  • イベント ED について、execution の中で E is-agent-order-before D であるとは、あるエージェントイベントレコード aerexecution.[[EventsRecords]] に含まれ、かつ aer.[[EventList]]ED の両方を含み、Eaer.[[EventList]] のリスト順序で D より前である場合です。
Note

各エージェントは、評価中にエージェントごとの厳密な全順序でイベントを導入します。これは、それらの厳密な全順序の和集合です。

29.6.2 reads-bytes-from

候補実行 execution に対し、その reads-bytes-from 関数は、SharedDataBlockEventSet(execution) 内のメモリエベントから SharedDataBlockEventSet(execution) 内のイベントのリストへの写像として、次の条件を満たす数学関数です。

候補実行は常に reads-bytes-from 関数を持ちます。

29.6.3 reads-from

候補実行 execution に対し、その reads-from 関係は、次の条件を満たすメモリエベント上の最小の関係です。

  • イベント R および W について、execution において R reads-from W であるのは、SharedDataBlockEventSet(execution) が RW の両方を含み、かつ reads-bytes-from(R) in executionW を含む場合です。

29.6.4 host-synchronizes-with

候補実行 execution に対し、その host-synchronizes-with 関係は、次を少なくとも満たす、ホスト固有のメモリエベントに対するホスト提供の厳密部分順序です。

  • execution において、E host-synchronizes-with D であれば、HostEventSet(execution) は E および D を含む。
  • host-synchronizes-with および is-agent-order-before の和集合にサイクルは存在しない。
Note 1

候補実行 execution の二つのホスト固有イベント E および D について、E host-synchronizes-with D であるなら、EexecutionD より前に発生する (happens-before) ことを意味します。

Note 2

この関係は、ホストが HTML ワーカー間の postMessage などの追加的な同期機構を提供することを許可します。

29.6.5 synchronizes-with

候補実行 execution に対し、その synchronizes-with 関係は、次の条件を満たすメモリエベント上の最小の関係です。

  • イベント R および W について、execution において W synchronizes-with R であるのは、R reads-from W in execution かつ R.[[Order]]seq-cstW.[[Order]]seq-cst、さらに RWメモリ範囲が等しい場合です。
  • execution.[[EventsRecords]] の各要素 eventsRecord について、以下が成り立つ。
    • イベント S および Sw について、eventsRecord.[[AgentSynchronizesWith]] が (S, Sw) を含むとき、S synchronizes-with Sw in execution である。
  • イベント E および D について、E synchronizes-with D in execution であるのは、execution.[[HostSynchronizesWith]] が (E, D) を含む場合である。
Note 1

メモリモデル文献上の慣例として、候補実行 execution においては、書き込みイベント読み出しイベントに synchronizes-with する(読み出しイベント書き込みイベントに synchronizes-with するのではない)。

Note 2

候補実行 execution において、init イベントはこの関係に参加せず、代わりに happens-before によって直接制約される。

Note 3

候補実行 execution において、reads-from で関連付けられたすべての seq-cst イベントが synchronizes-with で関連付けられるとは限らない。同じメモリ範囲を持つイベントのみが synchronizes-with で関連付けられる。

Note 4

候補実行 execution における Shared Data Block イベント R および W について、W synchronizes-with R である場合でも、RW 以外の書き込みから reads-from することができる。

29.6.6 happens-before

候補実行 execution に対し、その happens-before 関係は、次の条件を満たすメモリエベント上の最小の関係です。

  • イベント E および D について、execution において E happens-before D であるのは、次のいずれかの条件が成り立つ場合です。

Note

happens-before は agent-order の上位集合であるため、候補実行は ECMAScript の単一スレッド評価意味論と整合性がある。

29.7 有効な実行の性質

29.7.1 妥当な選択読み

候補実行 execution が妥当な選択読みを持つとは、次のアルゴリズムが true を返す場合である。

  1. SharedDataBlockEventSet(execution) の各 ReadSharedMemory または ReadModifyWriteSharedMemory イベント R について、次を行う
    1. chosenValueRecordexecution.[[ChosenValues]] の中で [[Event]] フィールドが R である要素とする。
    2. chosenValuechosenValueRecord.[[ChosenValue]] とする。
    3. readValueValueOfReadEvent(execution, R) とする。
    4. chosenLenchosenValue の要素数とする。
    5. readLenreadValue の要素数とする。
    6. chosenLenreadLen ならば、
      1. false を返す。
    7. 0(含む)から chosenLen(含まない)までの区間整数 i について、chosenValue[i] ≠ readValue[i] であれば、
      1. false を返す。
  2. true を返す。

29.7.2 コヒーレントリード

候補実行 execution がコヒーレントリードを持つとは、次のアルゴリズムが true を返す場合である。

  1. SharedDataBlockEventSet(execution) の各 ReadSharedMemory または ReadModifyWriteSharedMemory イベント R について、次を行う
    1. Wsreads-bytes-from(R) in execution とする。
    2. byteLocationR.[[ByteIndex]] とする。
    3. Ws の各要素 W について、次を行う
      1. R happens-before W in execution であれば、
        1. false を返す。
      2. WriteSharedMemory または ReadModifyWriteSharedMemory イベント VbyteLocationメモリ範囲に含まれており、さらに W happens-before V in execution および V happens-before R in execution なるものが存在すれば、
        1. false を返す。
      3. byteLocationbyteLocation + 1 を設定する。
  2. true を返す。

29.7.3 ティアフリーリード

候補実行 execution がティアフリーリードを持つとは、次のアルゴリズムが true を返す場合である。

  1. SharedDataBlockEventSet(execution) の各 ReadSharedMemory または ReadModifyWriteSharedMemory イベント R について、次を行う
    1. R.[[NoTear]]true の場合、
      1. R.[[ByteIndex]]R.[[ElementSize]] で割った余りが 0 であることをアサートする。
      2. execution において R reads-from W かつ W.[[NoTear]]true である各 Memory event W について、次を行う
        1. RWメモリ範囲が等しく、さらに Memory event V が存在して VWメモリ範囲が等しく、V.[[NoTear]]trueWV が同じ Shared Data Block event でなく、かつ execution において R reads-from V であるなら、
          1. false を返す。
  2. true を返す。
Note

共有データブロックイベント[[NoTear]] フィールドは、そのイベントが整数TypedArray へのアクセスによって導入された場合 true となり、浮動小数点型 TypedArray や DataView へのアクセスによって導入された場合は false となる。

直感的には、この要件は、メモリ範囲整数TypedArray によりアラインされた形でアクセスされる場合、等しい範囲を持つ他の書き込みイベントとのデータ競合では、その範囲へのひとつの書き込みイベントが「勝たなければならない」ことを意味する。より正確には、アラインされた読み出しイベントが、範囲が等しい複数の異なる書き込みイベントのバイトを合成して値を読み取ることができない、ということである。ただし、アラインされた読み出しイベントが、範囲が重なった複数の書き込みイベントから値を読み取ることは可能である。

29.7.4 逐次一貫性のあるアトミック操作

候補実行 execution に対し、is-memory-order-beforeEventSet(execution) 内のすべての Memory イベントの厳密全順序であり、次を満たす。

候補実行が is-memory-order-before 関係を持つ場合、それは逐次一貫性のあるアトミック操作を持つという。

Note 3

is-memory-order-before は EventSet(execution) 内の全イベントを含むが、execution 内で happens-before または synchronizes-with によって拘束されないものは順序内のどこでも現れることができる。

29.7.5 有効な実行

候補実行 execution は、次のすべてが成り立つ場合、有効な実行(単に実行とも呼ぶ)である。

  • ホストexecution に対する host-synchronizes-with 関係を提供している。
  • execution が厳密部分順序である happens-before 関係を持つ。
  • execution が妥当な選択読みを持つ。
  • execution がコヒーレントリードを持つ。
  • execution がティアフリーリードを持つ。
  • execution が逐次一貫性のあるアトミック操作を持つ。

すべてのプログラムは少なくとも1つの有効な実行を持つ。

29.8 競合

実行 execution および SharedDataBlockEventSet(execution) に含まれるイベント ED について、次のアルゴリズムが true を返す場合、ED競合(race) している。

  1. ED が同じ Shared Data Block event でない場合、
    1. execution において E happens-before D かつ D happens-before E の両方が成り立たない場合、
      1. ED の両方が WriteSharedMemory または ReadModifyWriteSharedMemory イベントであり、かつ EDメモリ範囲が互いに素でない場合、
        1. true を返す。
      2. execution において E reads-from D または D reads-from E である場合、
        1. true を返す。
  2. false を返す。

29.9 データ競合

実行 execution および SharedDataBlockEventSet(execution) に含まれるイベント ED について、次のアルゴリズムが true を返す場合、EDデータ競合(data race) している。

  1. execution において ED競合 していれば、
    1. E.[[Order]]seq-cst でない、または D.[[Order]]seq-cst でない場合、
      1. true を返す。
    2. EDメモリ範囲が重複している場合、
      1. true を返す。
  2. false を返す。

29.10 データ競合なし

実行 executionデータ競合なし(data race free) であるとは、SharedDataBlockEventSet(execution) にデータ競合状態にあるイベントが2つ以上存在しないことである。

すべての実行がデータ競合なしであれば、そのプログラムはデータ競合なしである。

メモリモデルはデータ競合なしプログラムのすべてのイベントに対して逐次一貫性を保証する。

29.11 共有メモリ利用指針

Note 1

以下は共有メモリを扱う ECMAScript プログラマー向けの指針です。

プログラムはデータ競合なしに保つこと(すなわち、同一メモリ位置に対する非アトミック操作が並行して発生しないこと)が推奨されます。データ競合なしプログラムは、各エージェントの評価意味論が交互にインターリーブされる意味論になります。データ競合なしであれば、メモリモデルの細部を理解する必要はありません。その詳細は ECMAScript をよりうまく書く直感の形成には役立たないでしょう。

より一般的には、プログラムがデータ競合なしでなくとも、アトミック操作が競合に関与せず、競合する操作がすべて同じアクセスサイズであれば、予測可能な挙動となる場合があります。もっとも単純なのは、アトミックと非アトミックの操作で異なるメモリセルを使う・異なるサイズのアトミックアクセスで同一セルを同時にアクセスしないようにすることです。すなわち、プログラムは可能な限り共有メモリを強く型付けされたものとして扱うべきです。依然として競合する非アトミックアクセスの順序やタイミングには依存できませんが、メモリを強く型付けとして扱えば競合アクセスで「ティア」(値のビットが混ざり合う)が発生しません。

Note 2

以下は共有メモリ利用プログラムのコンパイラ変換を行う ECMAScript 実装者向けの指針です。

シングルエージェント環境で有効なほとんどのプログラム変換を、マルチエージェント環境でも許可することが望ましい。これにより、マルチエージェントプログラムにおける各エージェントの性能が、シングルエージェント環境と同様に良好であることが保証される。多くの場合、これらの変換の妥当性を判断するのは難しい。本節では、プログラム変換に関するいくつかの規則を示す。これらの規則は規範的に(つまりメモリモデルによって暗に含まれるか、それよりも強い)捉えることを意図しているが、必ずしも網羅的ではない。これらの規則は、is-agent-order-before 関係を構成する Memory event の導入に先立ち行われるプログラム変換に適用されることを想定している。

エージェント順序スライスとは、is-agent-order-before 関係のうち単一エージェントに関係する部分集合である。

ある読み出しイベント可能な読み値(possible read values) とは、そのイベントに対して全ての有効な実行における ValueOfReadEvent の値の集合である。

共有メモリなしの場合に有効なエージェント順序スライスの変換は、下記例外を除き、共有メモリがある場合でも有効である。

  • アトミックは常にそのまま: プログラム変換によって、[[Order]]seq-cst である Shared Data Block イベントが is-agent-order-before 関係から除外されたり、互いに順序を入れ替えたり、unordered イベントとの順序がエージェント順序スライス内で入れ替えたりしてはならない。

    (実際には、この順序変更の禁止によって、コンパイラはすべての seq-cst 操作が同期であり、最終的な is-memory-order-before 関係に含まれるものと仮定せざるを得ず、通常は他エージェントの解析がなければこう仮定することになる。また、呼び出し先のメモリ順序への影響が判明しない呼び出しも seq-cst を含む可能性を仮定する必要がある。)

  • 読み出しの安定性: 各共有メモリ読み出しは、同一実行中つねに一つの値しか観測できないこと。

    (例えば、意味的に単一の読み出しが複数回実行された場合、その後プログラムが観測できるのはそのうちいずれか一つだけになること。他の値も観測できるような変換(リマテリアリゼーション)は違反となる。)

  • 書き込みの安定性: 共有メモリへのすべての観測可能な書き込みは、各実行のプログラム意味論に従っていなければならない。

    (例えば、リードモディファイライトによる大きい単位での書き込みで小さい値を書き込む・本来書けない値を書き込む・読み出した値を読み出し後に他のエージェントが上書きした可能性のあるアドレスに再度書き戻すなどによって、観測可能な書き込みを増やしてはならない。)

  • 可能な読み値は空であってはならない: プログラム変換は、共有メモリ読み出しの可能な読み値が空になる原因になってはならない。

    (これは直感に反するが、実際にはこの規則が書き込み変換を強く制限する。書き込みイベントはリードイベントに観測されてこそ意味があるので、seq-cst の間で書きを合成・並び替えしても、位置ごとに一切書きを残さないことは許されない。)

妥当な変換例:同一アドレスへの複数の非アトミック読みをまとめる・非アトミック読みに順序をつけず並び替える・投機的な非アトミック読みを導入する・同一アドレスへの複数の非アトミック書きをまとめる・異なるアドレスへの非アトミック書きの順序を並び替える・巻き込み終端影響があってもループの外に非アトミック読みに抜き出す、など。一般に TypedArray が重複している場合はアドレスの違いの証明が難しいことに注意。

Note 3

以下は共有メモリアクセスに対する機械語生成を行う ECMAScript 実装者向けの指針です。

ARMやPowerより弱くないメモリモデルのアーキテクチャでは、非アトミックなstore/loadはハードウェアの単純なストア・ロード命令で実装できる。アトミックなストア・ロードは逐次一貫性を保証する命令に降下でき、そうでなければ両側にバリアを置く。Read-Modify-Write 操作は、x86のLOCKプレフィックス・ARMのload-exclusive/store-exclusive・Powerのload-link/store-conditional等の命令に降下できる。

メモリモデルは以下のようなコード生成を容認することを意図する:

  • プログラム内のすべてのアトミック操作は必要なものと仮定される。
  • アトミック操作同士・アトミック・非アトミック間で操作は再順序化されない。
  • 関数は常にアトミック操作を実施するものと仮定される。
  • アトミック操作はより大きな領域へのリードモディファイライトで実装されることはなく、プラットフォームに適切なアトミック命令がなければ非ロックフリーアトミックで実装する(どのサイズにも通常のメモリアクセス命令は想定ずみ)。

単純なコード生成パターン:

  • 通常のロード・ストア →1命令のロード・ストアに。
  • ロックフリーアトミック ロード・ストア → フルフェンス、ロード・ストア、フルフェンス。
  • ロックフリーアトミック リードモディファイライト → フルフェンス、アトミック命令列、フルフェンス。
  • 非ロックフリーアトミック → スピンロック獲得、フルフェンス、非アトミック命令実行、フルフェンス、スピンロック解放。

この対応は、アトミック操作が非アトミック書き込みや異なるサイズのアトミック操作と競合しなければ正しい。現実にはそれで十分であり、メモリモデルは競合中のアトミック操作を非アトミックなものとみなす。他方、単純対応はむしろ強力であり、アトミック操作を逐次一貫性フェンスであるかのように扱えるが、メモリモデルはそれを必ずしも保証しない。

この基本パターンへのローカルな改善も、メモリモデルの拘束条件の範囲内で認められる。例:

  • プラットフォーム依存の明らかな最適化によりフェンスの冗長性を除去可能。例えばx86ではロックフリーアトミックの前後フェンスはストア後のみで十分・リードモディファイライトには不要。他アーキテクチャでも弱いフェンスで逐次一貫性を壊さない範囲で利用可能。
  • 多くの現代的なプラットフォームは必要なサイズのロックフリーアトミックをサポート。不足時はバリアをロックの内側で吸収できる場合も多い。最も単純な非ロックフリーアトミックは SharedArrayBuffer ごとに単一ロック語を持つこと。
  • より複雑なプラットフォーム依存最適化も解析次第で可能。例えば2連続のフェンスは1フェンスと等価になる場合が多いし、x86 ではストア後フェンスすらロード後に分離しない限り省略可能など。