ページ表示設定

29 メモリモデル

メモリ一貫性モデル、またはメモリモデルは、SharedArrayBuffer を基盤とする TypedArray インスタンスへのアクセス、および Atomics オブジェクトのメソッドを介して発生する Shared Data Block イベントの取り得る順序を規定します。プログラムにデータ競合(以下で定義)がない場合、イベントの順序は逐次一貫しているように見えます。すなわち、各 agent の動作を交互に配置したものとして見えます。プログラムにデータ競合がある場合、共有メモリ操作は逐次一貫していないように見えることがあります。たとえば、プログラムが因果関係に反する動作やその他の驚くべき動作を示す場合があります。これらの驚くべき動作は、コンパイラー変換や CPU の設計(たとえばアウト・オブ・オーダー実行や投機実行)から生じます。メモリモデルは、プログラムが逐次一貫した動作を示す正確な条件と、データ競合から読み取られ得る値の両方を定義します。つまり、未定義動作はありません。

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

注

この節では、SharedArrayBuffer 上の抽象操作によって導入される Memory イベントについて公理的モデルを提供します。このモデルは、この仕様の他の部分とは異なり、アルゴリズムとして表現できないことを強調しておく必要があります。抽象操作によるイベントの非決定的な導入が、ECMAScript 評価の操作的意味論とメモリモデルの公理的意味論とのインターフェイスになります。これらのイベントの意味論は、1回の評価におけるすべてのイベントのグラフを考慮することで定義されます。これらは静的意味論でも実行時意味論でもありません。実証されたアルゴリズム実装は存在せず、代わりに特定のイベントグラフが許可されるか許可されないかを決定する一連の制約があります。

29.1 メモリモデルの基礎

共有メモリアクセス(読み取りおよび書き込み)は、以下で定義する atomic アクセスと data アクセスの2つのグループに分けられます。Atomic アクセスは逐次一貫しており、すなわち agent cluster 内のすべての agent が合意するイベントの厳密な全順序があります。非 atomic アクセスには、すべての agent が合意する厳密な全順序はなく、すなわち順序付けされていません。

注 1

release-acquire のような、逐次一貫性より弱く unordered より強い順序付けはサポートされません。

Shared Data Block イベントは、ReadSharedMemory、WriteSharedMemory、または ReadModifyWriteSharedMemory Record のいずれかです。read イベントは ReadSharedMemory または ReadModifyWriteSharedMemory のいずれかです。write イベントは WriteSharedMemory または ReadModifyWriteSharedMemory のいずれかです。

表 97: ReadSharedMemory イベントのフィールド
フィールド名 値 意味
[[Order]] seq-cst or unordered イベントについてメモリモデルによって保証される最も弱い順序付け。
[[NoTear]] a Boolean このイベントと等しいメモリ範囲を持つ複数の write イベントから、このイベントが読み取ることを許可されるかどうか。
[[Block]] a Shared Data Block イベントが操作するブロック。
[[ByteIndex]] a non-negative integer [[Block]] 内の読み取りのバイトアドレス。
[[ElementSize]] a non-negative integer 読み取りのサイズ。
表 98: WriteSharedMemory イベントのフィールド
フィールド名 値 意味
[[Order]] seq-cst, unordered, or init イベントについてメモリモデルによって保証される最も弱い順序付け。
[[NoTear]] a Boolean このイベントと等しいメモリ範囲を持つ複数の read イベントから、このイベントが読み取られることを許可されるかどうか。
[[Block]] a Shared Data Block イベントが操作するブロック。
[[ByteIndex]] a non-negative integer [[Block]] 内の書き込みのバイトアドレス。
[[ElementSize]] a non-negative integer 書き込みのサイズ。
[[Payload]] a List of byte values 他のイベントによって読み取られるバイト値の List。
表 99: ReadModifyWriteSharedMemory イベントのフィールド
フィールド名 値 意味
[[Order]] seq-cst read-modify-write イベントは常に逐次一貫しています。
[[NoTear]] true read-modify-write イベントは tear できません。
[[Block]] a Shared Data Block イベントが操作するブロック。
[[ByteIndex]] a non-negative integer [[Block]] 内の read-modify-write のバイトアドレス。
[[ElementSize]] a non-negative integer read-modify-write のサイズ。
[[Payload]] a List of byte values [[ModifyOp]] に渡されるバイト値の List。
[[ModifyOp]] a read-modify-write modification function 読み取ったバイト値の List と [[Payload]] から、変更されたバイト値の List を返す Abstract Closure。

Shared Data Block イベントは、抽象操作または Atomics オブジェクトのメソッドによって候補実行の Agent Events Record に導入されます。一部の操作は、フィールドを持たず、他のイベントの許可される順序を直接制約するためだけに存在する Synchronize イベントも導入します。そして最後に、ホスト固有のイベントがあります。Memory イベントは、Shared Data Block イベント、Synchronize イベント、またはそのようなホスト固有イベントのいずれかです。

Shared Data Block イベント e のメモリ範囲を、e.[[ByteIndex]](含む)から e.[[ByteIndex]] + e.[[ElementSize]](含まない)までの区間内のすべての整数の Set とします。2つのイベントの [[Block]]、[[ByteIndex]]、および [[ElementSize]] が同じ場合、それらのイベントのメモリ範囲は等しいものとします。2つのイベントの [[Block]] が同じで、範囲が等しくなく、かつその積集合が空でない場合、それらのイベントのメモリ範囲は重なっているものとします。2つのイベントの [[Block]] が同じでないか、またはその範囲が等しくも重なってもいない場合、それらのイベントのメモリ範囲は互いに素であるものとします。

注 2

考慮されるべきホスト固有の同期イベントの例としては、SharedArrayBuffer をある agent から別の agent へ送信すること(たとえばブラウザーでの postMessage)、agent の開始と停止、および共有メモリ以外のチャネルを介した agent cluster 内の通信があります。特定の実行 execution について、これらのイベントは host-synchronizes-with 厳密半順序を介してホストから提供されます。さらに、ホストは is-agent-order-before Relation に参加させるため、ホスト固有の同期イベントを execution.[[EventList]] に追加できます。

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

29.2 Agent Events Record

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

表 100: Agent Events Record のフィールド
フィールド名 値 意味
[[AgentSignifier]] an agent signifier その評価によってこの順序付けが生じた agent。
[[EventList]] a List of Memory events イベントは評価中にこの List へ追加されます。
[[AgentSynchronizesWith]] a List of pairs of Synchronize events 操作的意味論によって導入される Synchronize 関係。

29.3 Chosen Value Record

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

表 101: Chosen Value Record のフィールド
フィールド名 値 意味
[[Event]] a Shared Data Block event この chosen value のために導入された ReadSharedMemory または ReadModifyWriteSharedMemory イベント。
[[ChosenValue]] a List of byte values 評価中に非決定的に選択されたバイト。

29.4 候補実行

agent cluster の評価の候補実行は、次のフィールドを持つ Record です。

表 102: 候補実行 Record のフィールド
フィールド名 値 意味
[[EventsRecords]] a List of Agent Events Records agent を、評価中に追加された Memory イベントの List に対応付けます。
[[ChosenValues]] a List of Chosen Value Records ReadSharedMemory または ReadModifyWriteSharedMemory イベントを、評価中に選択されたバイト値の List に対応付けます。

空の候補実行とは、そのフィールドが空の List である候補実行 Record です。

29.5 メモリモデルのための抽象操作

29.5.1 EventSet ( execution )

抽象操作 EventSet。引数 execution (a candidate execution)。戻り値:a Set of Memory events。 呼び出されると、次の手順を実行する。

  1. events を空の Set とする。
  2. execution.[[EventsRecords]] の各 Agent Events Record eventsRecord について、次を行う。
    1. eventsRecord.[[EventList]] の各 Memory event event について、次を行う。
      1. event を events に追加する。
  3. events を返す。

29.5.2 SharedDataBlockEventSet ( execution )

抽象操作 SharedDataBlockEventSet。引数 execution (a candidate execution)。戻り値:a Set of Shared Data Block events。 呼び出されると、次の手順を実行する。

  1. events を空の Set とする。
  2. EventSet(execution) の各 Memory event event について、次を行う。
    1. event が Shared Data Block event である場合、event を events に追加する。
  3. events を返す。

29.5.3 HostEventSet ( execution )

抽象操作 HostEventSet。引数 execution (a candidate execution)。戻り値:a Set of Memory events。 呼び出されると、次の手順を実行する。

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

29.5.4 ComposeWriteEventBytes ( execution, byteIndex, writes )

抽象操作 ComposeWriteEventBytes。引数 execution (a candidate execution)、byteIndex (非負整数) および writes (リスト (要素:(WriteSharedMemory または ReadModifyWriteSharedMemory events)))。戻り値:リスト (要素:バイト値)。 呼び出されると、次の手順を実行する。

  1. byteLocation を byteIndex とする。
  2. bytesRead を新しい空の List とする。
  3. writes の各要素 writeEvent について、次を行う。
    1. 表明: writeEvent のメモリ範囲には byteLocation が含まれる。
    2. payloadIndex を byteLocation - writeEvent.[[ByteIndex]] とする。
    3. writeEvent が WriteSharedMemory event である場合、
      1. byte を writeEvent.[[Payload]][payloadIndex] とする。
    4. そうでない場合、
      1. 表明: writeEvent は ReadModifyWriteSharedMemory event である。
      2. bytes を ValueOfReadEvent(execution, writeEvent) とする。
      3. bytesModified を writeEvent.[[ModifyOp]](bytes, writeEvent.[[Payload]]) とする。
      4. byte を bytesModified[payloadIndex] とする。
    5. byte を bytesRead に追加する。
    6. byteLocation を byteLocation + 1 に設定する。
  4. bytesRead を返す。
注 1

read-modify-write の変更 [[ModifyOp]] は、ReadModifyWriteSharedMemory イベントを導入する Atomics オブジェクト上の関数プロパティによって与えられます。

注 2

この抽象操作は、write イベントの List をバイト値の List に合成します。ReadSharedMemory および ReadModifyWriteSharedMemory イベントのイベント意味論で使用されます。

29.5.5 ValueOfReadEvent ( execution, readEvent )

抽象操作 ValueOfReadEvent。引数 execution (a candidate execution) および readEvent ((a ReadSharedMemory または ReadModifyWriteSharedMemory event))。戻り値:リスト (要素:バイト値)。 呼び出されると、次の手順を実行する。

  1. writes を execution における reads-bytes-from(readEvent) とする。
  2. 表明: writes は、長さが readEvent.[[ElementSize]] と等しい WriteSharedMemory または ReadModifyWriteSharedMemory イベントの List である。
  3. ComposeWriteEventBytes(execution, readEvent.[[ByteIndex]], writes) を返す。

29.6 候補実行の関係

次の関係および数学関数は、特定の候補実行をパラメーターとして取り、その Memory イベントを順序付けます。

29.6.1 is-agent-order-before

候補実行 execution について、その is-agent-order-before Relation は、次を満たす Memory イベント上の最小の Relation です。

  • イベント eventA および eventB について、execution.[[EventsRecords]] 内に eventA と eventB の両方を含む [[EventList]] を持つ Agent Events Record eventsRecord が存在し、eventsRecord.[[EventList]] の List 順序で eventA が eventB より前にある場合、execution において eventA is-agent-order-before eventB です。
注

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

29.6.2 reads-bytes-from

候補実行 execution について、その reads-bytes-from 関数は、SharedDataBlockEventSet(execution) 内の Memory イベントを SharedDataBlockEventSet(execution) 内のイベントの List に写像し、次の条件を満たす数学関数です。

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

29.6.3 reads-from

候補実行 execution について、その reads-from Relation は、次を満たす Memory イベント上の最小の Relation です。

  • イベント readEvent および writeEvent について、SharedDataBlockEventSet(execution) が readEvent と writeEvent の両方を含み、かつ execution における reads-bytes-from(readEvent) が writeEvent を含む場合、execution において readEvent reads-from writeEvent です。

29.6.4 host-synchronizes-with

候補実行 execution について、その host-synchronizes-with Relation は、ホスト固有の Memory イベント上のホスト提供の厳密半順序であり、少なくとも次を満たします。

  • execution において eventA host-synchronizes-with eventB である場合、HostEventSet(execution) は eventA と eventB を含みます。
  • execution における host-synchronizes-with と is-agent-order-before の和集合には循環がありません。
注 1

候補実行 execution 内の2つのホスト固有イベント eventA および eventB について、execution における eventA host-synchronizes-with eventB は、execution における eventA happens-before eventB を意味します。

注 2

この Relation により、ホストは HTML worker 間の postMessage などの追加の同期機構を提供できます。

29.6.5 synchronizes-with

候補実行 execution について、その synchronizes-with Relation は、次を満たす Memory イベント上の最小の Relation です。

  • イベント readEvent および writeEvent について、execution において readEvent reads-from writeEvent であり、readEvent.[[Order]] が seq-cst、writeEvent.[[Order]] が seq-cst、かつ readEvent と writeEvent のメモリ範囲が等しい場合、execution において writeEvent synchronizes-with readEvent です。
  • execution.[[EventsRecords]] の各要素 eventsRecord について、次が成り立ちます。
    • イベント eventA および eventB について、eventsRecord.[[AgentSynchronizesWith]] が (eventA, eventB) を含む場合、execution において eventA synchronizes-with eventB です。
  • イベント eventA および eventB について、execution において eventA host-synchronizes-with eventB である場合、execution において eventA synchronizes-with eventB です。
注 1

メモリモデル文献における慣例により、候補実行 execution では、read イベント synchronizes-with write イベントではなく、write イベント synchronizes-with read イベントとなります。

注 2

候補実行 execution では、init イベントはこの Relation に参加せず、代わりに happens-before によって直接制約されます。

注 3

候補実行 execution では、reads-from によって関連付けられるすべての seq-cst イベントが synchronizes-with によって関連付けられるわけではありません。メモリ範囲も等しいイベントだけが synchronizes-with によって関連付けられます。

注 4

候補実行 execution 内の Shared Data Block イベント readEvent および writeEvent について、writeEvent synchronizes-with readEvent である場合でも、readEvent は writeEvent 以外の write から reads-from する場合があります。

29.6.6 happens-before

候補実行 execution について、その happens-before Relation は、次を満たす Memory イベント上の最小の Relation です。

  • イベント eventA および eventB について、次の条件のいずれかが真である場合、execution において eventA happens-before eventB です。

    • execution において eventA is-agent-order-before eventB である。
    • execution において eventA synchronizes-with eventB である。
    • SharedDataBlockEventSet(execution) が eventA と eventB の両方を含み、eventA.[[Order]] が init であり、かつ eventA と eventB のメモリ範囲が重なっている。
    • execution において eventA happens-before eventC かつ eventC happens-before eventB となるイベント eventC が存在する。
注

happens-before は agent-order の上位集合であるため、候補実行は ECMAScript の単一スレッド評価意味論と一貫しています。

29.7 有効な実行の性質

29.7.1 有効な Chosen Read

候補実行 execution は、次のアルゴリズムが true を返す場合、有効な chosen read を持ちます。

  1. SharedDataBlockEventSet(execution) の各 ReadSharedMemory または ReadModifyWriteSharedMemory イベント readEvent について、次を行う。
    1. chosenValueRecord を、その [[Event]] フィールドが readEvent である execution.[[ChosenValues]] の要素とする。
    2. chosenValue を chosenValueRecord.[[ChosenValue]] とする。
    3. readValue を ValueOfReadEvent(execution, readEvent) とする。
    4. chosenLength を chosenValue の要素数とする。
    5. readLength を readValue の要素数とする。
    6. chosenLength ≠ readLength である場合、
      1. false を返す。
    7. 0(含む)から chosenLength(含まない)までの区間内のある整数 i について chosenValue[i] ≠ readValue[i] である場合、
      1. false を返す。
  2. true を返す。

29.7.2 一貫した Read

候補実行 execution は、次のアルゴリズムが true を返す場合、一貫した read を持ちます。

  1. SharedDataBlockEventSet(execution) の各 ReadSharedMemory または ReadModifyWriteSharedMemory イベント readEvent について、次を行う。
    1. writes を execution における reads-bytes-from(readEvent) とする。
    2. byteLocation を readEvent.[[ByteIndex]] とする。
    3. writes の各要素 writeEvent について、次を行う。
      1. execution において readEvent happens-before writeEvent である場合、
        1. false を返す。
      2. byteLocation をメモリ範囲に含み、execution において writeEvent happens-before value かつ value happens-before readEvent となる WriteSharedMemory または ReadModifyWriteSharedMemory イベント value が存在する場合、
        1. false を返す。
      3. byteLocation を byteLocation + 1 に設定する。
  2. true を返す。

29.7.3 Tear しない Read

候補実行 execution は、次のアルゴリズムが true を返す場合、tear しない read を持ちます。

  1. SharedDataBlockEventSet(execution) の各 ReadSharedMemory または ReadModifyWriteSharedMemory イベント readEvent について、次を行う。
    1. readEvent.[[NoTear]] が true である場合、
      1. 表明: readEvent.[[ByteIndex]] を readEvent.[[ElementSize]] で除算した余りは 0 である。
      2. execution において readEvent reads-from writeEvent かつ writeEvent.[[NoTear]] が true となる各 Memory event writeEvent について、次を行う。
        1. readEvent と writeEvent のメモリ範囲が等しく、value と writeEvent のメモリ範囲が等しく、value.[[NoTear]] が true、writeEvent と value が同じ Shared Data Block event ではなく、かつ execution において readEvent reads-from value となる Memory event value が存在する場合、
          1. false を返す。
  2. true を返す。
注

Shared Data Block イベントの [[NoTear]] フィールドは、そのイベントが整数 TypedArray へのアクセスによって導入された場合は true、浮動小数点 TypedArray または DataView へのアクセスによって導入された場合は false です。

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

29.7.4 逐次一貫した Atomic

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

候補実行は、is-memory-order-before Relation を認める場合、逐次一貫した atomic を持ちます。

注 3

is-memory-order-before は EventSet(execution) 内のすべてのイベントを含みますが、execution における happens-before または synchronizes-with によって制約されないイベントは、その順序の任意の位置で発生することが許可されます。

29.7.5 有効な実行

候補実行 execution は、次のすべてが真である場合、有効な実行(または単に実行)です。

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

29.8 競合

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

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

29.9 データ競合

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

  1. execution において eventA と eventB が race にある場合、
    1. eventA.[[Order]] が seq-cst でないか、または eventB.[[Order]] が seq-cst でない場合、
      1. true を返す。
    2. eventA と eventB のメモリ範囲が重なっている場合、
      1. true を返す。
  2. false を返す。

29.10 データ競合からの自由

実行 execution は、SharedDataBlockEventSet(execution) 内にデータ競合する2つのイベントが存在しない場合、データ競合がないものとします。

プログラムは、そのすべての実行にデータ競合がない場合、データ競合がありません。

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

29.11 共有メモリのガイドライン

注 1

以下は、共有メモリを扱う ECMAScript プログラマー向けのガイドラインです。

プログラムをデータ競合のない状態に保つ、すなわち同じメモリ位置に対する非 atomic 操作が同時に行われることを不可能にすることを推奨します。データ競合のないプログラムは、各 agent の評価意味論における各手順が互いに交互に配置される interleaving semantics を持ちます。データ競合のないプログラムでは、メモリモデルの詳細を理解する必要はありません。その詳細を理解しても、ECMAScript をより適切に記述するのに役立つ直感が得られる可能性は低いでしょう。

より一般的には、プログラムにデータ競合があっても、atomic 操作がいずれのデータ競合にも関与せず、競合する操作がすべて同じアクセスサイズを持つ限り、予測可能な動作を持つことがあります。atomic が競合に関与しないようにする最も単純な方法は、atomic 操作と非 atomic 操作で異なるメモリセルを使用し、異なるサイズの atomic アクセスが同時に同じセルへアクセスしないようにすることです。実質的には、プログラムは共有メモリを可能な限り強く型付けされたものとして扱うべきです。それでも競合する非 atomic アクセスの順序やタイミングに依存することはできませんが、メモリを強く型付けされたものとして扱えば、競合するアクセスが「tear」することはありません(それらの値のビットが混在することはありません)。

注 2

以下は、共有メモリを使用するプログラム向けにコンパイラー変換を記述する ECMAScript 実装者向けのガイドラインです。

各 agent の性能を単一 agent 環境と同等に良好に保つため、単一 agent 環境で有効なほとんどのプログラム変換を複数 agent 環境でも許可することが望まれます。多くの場合、これらの変換を判断することは困難です。ここでは、規範的なものとして扱われることを意図した(すなわちメモリモデルによって含意されるか、メモリモデルが含意するものより強い)が、おそらく網羅的ではないプログラム変換に関するいくつかの規則を概説します。これらの規則は、is-agent-order-before Relation を構成する Memory イベントが導入される前のプログラム変換に適用されることを意図しています。

agent-order slice を、単一 agent に関係する is-agent-order-before Relation の部分集合とします。

read イベントの可能な read 値を、すべての有効な実行にわたるそのイベントの ValueOfReadEvent のすべての値の集合とします。

共有メモリがない場合に有効な agent-order slice の変換は、次の例外を除き、共有メモリが存在する場合にも有効です。

  • Atomic は不変である: プログラム変換は、[[Order]] が seq-cst である Shared Data Block イベントを is-agent-order-before Relation から削除させてはならず、それらを互いに対して並べ替えてはならず、また agent-order slice 内で [[Order]] が unordered であるイベントに対して並べ替えてはなりません。

    (実際には、この並べ替え禁止により、コンパイラーはすべての seq-cst 操作が同期であり、最終的な is-memory-order-before Relation に含まれると仮定することを強制されます。これは agent 間のプログラム解析がない場合には、いずれにせよ通常仮定しなければならないことです。また、callee の memory-order への影響が不明なすべての呼出しが seq-cst 操作を含み得ると仮定することも強制されます。)

  • Read は安定していなければならない: 任意の共有メモリ read は、1回の実行において単一の値だけを観測しなければなりません。

    (たとえば、プログラム内で意味論的には単一の read であるものが複数回実行された場合、その後プログラムが観測できるのは読み取られた値のうち1つだけです。rematerialization と呼ばれる変換はこの規則に違反する可能性があります。)

  • Write は安定していなければならない: 共有メモリへの観測可能なすべての write は、1回の実行におけるプログラム意味論に由来しなければなりません。

    (たとえば、より小さいデータを書き込むためにより大きな位置に対する read-modify-write 操作を使用する、プログラムが書き込めなかった値をメモリへ書き込む、または読み取った直後の値を読み取った位置へ書き戻す、といった特定の観測可能な write を変換によって導入してはなりません。特に、その位置が read 後に別の agent によって上書きされ得る場合です。)

  • 可能な read 値は空であってはならない: プログラム変換は、共有メモリ read の可能な read 値を空にしてはなりません。

    (直感に反して、この規則は実質的には write に対する変換を制限します。メモリモデルにおいて write は read イベントから読み取られることによって効力を持つからです。たとえば、2つの seq-cst 操作の間で write を移動、統合、場合によっては並べ替えることはできますが、ある位置を更新するすべての write を変換によって削除してはならず、何らかの write を保持しなければなりません。)

引き続き有効な変換の例として、同じ位置からの複数の非 atomic read の統合、非 atomic read の並べ替え、投機的な非 atomic read の導入、同じ位置への複数の非 atomic write の統合、異なる位置への非 atomic write の並べ替え、およびそれが終了性に影響する場合でもループ外へ非 atomic read を hoist することがあります。一般に、alias された TypedArray により位置が異なることを証明するのは困難であることに注意してください。

注 3

以下は、共有メモリアクセス用の機械語コードを生成する ECMAScript 実装者向けのガイドラインです。

ARM または Power のメモリモデル以上に弱くないメモリモデルを持つアーキテクチャでは、非 atomic store および load は、対象アーキテクチャ上の単純な store および load にコンパイルできます。Atomic store および load は、逐次一貫性を保証する命令へコンパイルできます。そのような命令が存在しない場合、単純な store または load の両側に barrier を配置するなど、メモリ barrier を使用します。read-modify-write 操作は、x86 の LOCK プレフィックス付き命令、ARM の load-exclusive/store-exclusive 命令、Power の load-link/store-conditional 命令など、対象アーキテクチャ上の read-modify-write 命令へコンパイルできます。

具体的には、メモリモデルは次のようなコード生成を許可することを意図しています。

  • プログラム内のすべての atomic 操作は必要であると仮定されます。
  • Atomic 操作は、互いに対しても非 atomic 操作に対しても決して並べ替えられません。
  • 関数は常に atomic 操作を実行すると仮定されます。
  • Atomic 操作は、より大きなデータ上の read-modify-write 操作として実装されることはなく、プラットフォームが適切なサイズの atomic 操作を持たない場合は、lock-free ではない atomic として実装されます。(すでに、すべてのプラットフォームが対象となるすべてのサイズの通常のメモリアクセス操作を持つと仮定しています。)

単純なコード生成では次のパターンを使用します。

  • 通常の load および store は、単一の load 命令および store 命令にコンパイルされます。
  • lock-free atomic load および store は、完全な(逐次一貫した)fence、通常の load または store、および完全な fence にコンパイルされます。
  • lock-free atomic read-modify-write アクセスは、完全な fence、atomic read-modify-write 命令列、および完全な fence にコンパイルされます。
  • lock-free でない atomic は、spinlock の取得、完全な fence、一連の非 atomic load および store 命令、完全な fence、および spinlock の解放にコンパイルされます。

この対応付けは、あるメモリ範囲上の atomic 操作が非 atomic write または異なるサイズの atomic 操作と競合しない限り正しいものです。しかし、必要なのはそれだけです。メモリモデルは、競合に関与する atomic 操作を実質的に非 atomic 状態へ降格させます。一方、単純な対応付けはかなり強力です。atomic 操作を逐次一貫した fence として使用できますが、これはメモリモデルが実際には保証していないことです。

メモリモデルの制約に従う限り、これらの基本パターンに対する局所的な改善も許可されます。たとえば:

  • 冗長な fence を削除する、明らかなプラットフォーム依存の改善があります。たとえば x86 では、lock-free atomic load および store の周囲の fence は、store の後に続く fence を除いて常に省略でき、lock-free read-modify-write 命令では、これらがすべて LOCK プレフィックス付き命令を使用するため fence は不要です。多くのプラットフォームには複数の強度の fence があり、逐次一貫性を損なわずに特定の文脈でより弱い fence を使用できます。
  • ほとんどの現代的なプラットフォームは、ECMAScript atomic に必要なすべてのデータサイズについて lock-free atomic をサポートします。lock-free でない atomic が必要な場合、atomic 操作の本体を囲む fence は通常、lock および unlock の手順へ折り込むことができます。lock-free でない atomic に対する最も単純な解決策は、SharedArrayBuffer ごとに単一の lock word を持つことです。
  • 一部のコード解析を必要とする、より複雑なプラットフォーム依存の局所的改善もあります。たとえば、連続する2つの fence はしばしば単一の fence と同じ効果を持つため、2つの atomic 操作用のコードが連続して生成される場合、それらを分離する fence は1つだけで済みます。x86 では、store の後の fence は store を後続の load から分離するためだけに必要であるため、atomic store を分離する単一の fence さえ省略できます。