一貫性モデル (ソフトウェア)
2023-01
一貫性モデルまたは整合性モデルは、分散共有メモリシステムや分散データストア(、データベース、楽観的レプリケーションシステム、ウェブキャッシングなど)のような分散システムで使用される。システムは、メモリ上の操作が特定のルールに従っている場合、あるモデルをサポートしていると言う。データ一貫性モデルは、プログラマーとシステムの間の契約を規定するもので、プログラマーがルールに従えば、メモリは一貫しており、メモリの読み書きや更新の結果は予測可能であることをシステムが保証する。これは、キャッシュやキャッシュレスのシステムで発生するコヒーレンスとは異なり、すべてのプロセッサに対するデータの一貫性を意味する。コヒーレンスは、1つの場所や1つの変数への書き込みをすべてのプロセッサが見ることができるようなグローバルな秩序を維持することを意味する。一貫性とは、複数の場所への操作の順序を、すべてのプロセッサに対して維持することである。
C++ や Java などの高レベル言語では、メモリ操作を低レベルの操作に変換することで、メモリセマンティクスを維持しながら契約を部分的に維持している。契約を守るために、コンパイラは一部のメモリ命令を並べ替えたり、pthread_mutex_lock()などのライブラリコールは必要な同期をカプセル化したりする。
モデル検査による逐次的な整合性の検証は、有限状態のキャッシュコヒーレンスプロトコルであっても、一般的には決定不可能である。
一貫性モデルは、更新の見かけ上の順序と可視性に関するルールを定義しており、トレードオフの連続性を持っている。
次のようなケースが想定できる。
一定時間t後、クライアントBがノードNから行Xを読み出す
一貫性モデルは、クライアントAが行った書き込みをクライアントBが見ているかどうかを判断しなければならない。
一貫性モデルの定義と分類には、発行と観測の2つの方法がある。
発行方式では、プロセスがオペレーションを発行する方法を定義する制限を記述する。
プロセスから見える操作の順序を定義する観測方式。
例えば、一貫性モデルでは、以前に発行された操作がすべて完了するまで、プロセスが操作を発行することはできないと定義することができる。一貫性モデルによって、適用される条件は異なる。ある一貫性モデルは、そのモデルのすべての条件とそれ以上の条件を必要とする場合、他のモデルよりも強いと考えることができる。言い換えれば、制約条件が少ないモデルは、弱い一貫性モデルと考えられうる。
これらのモデルは、ハードウェアがどのように配置されるべきかを定義し、プログラマがどのようにコーディングするべきかを高いレベルで定義する。選択されたモデルは、コンパイラがどのように命令を並べ替えるかにも影響してくる。一般的には、命令間の制御依存関係や同じ場所への書き込みが順序づけられていれば、コンパイラは必要に応じて命令を並べ替えることができる。しかし、以下に説明するモデルでは、ロード前の書き込みを順序変更できるものとできないものがある。
厳密な一貫性は、最も強力な一貫性モデルである。このモデルでは、任意のプロセッサによる変数への書き込みは、すべてのプロセッサによって瞬時に確認される必要がある。
厳密モデルの図と非厳密モデルの図は、時間の制約である「瞬間」を表している。これは、あたかもグローバルクロックが存在し、すべての書き込みがそのクロック期間の終わりまでにすべてのプロセッサのキャッシュに反映されなければならないように理解することができる。次の操作は、次のクロック期間にのみ行われなければならない。
これは最も厳格なモデルである。このモデルでは、プログラマーが期待した結果が毎回返ってくる。よって決定論的なモデルである。瞬間的なメッセージ交換は不可能なので、実用的な関連性は思考実験と形式論に限られる。また、同時書き込みが不可能であることを前提としているため、同じデータアイテムへの同時書き込みにおける衝突解決の問題にも役立たない。
逐次一貫性(整合性)モデルは、ランポート(1979)によって提唱された。厳密な一貫性モデルよりも弱いメモリモデルである。変数への書き込みは瞬時に確認される必要はないが、異なるプロセッサによる変数への書き込みは、すべてのプロセッサで同じ順序で確認されなければならない。ランポート(1979)の定義によると、「任意の実行結果が、すべてのプロセッサの操作が何らかの順序で実行された場合と同じであり、個々のプロセッサの操作が、そのプログラムで指定された順序でこの順序に現れる」場合に、逐次整合性が満たされる。
各プロセッサ内のプログラムの順序と、プロセッサ間の操作の順序は維持されるべきである。プロセッサ間の実行の順序を維持するためには、すべての操作が他のすべてのプロセッサに対して瞬間的または不可分的に実行されているように見えなければならない。情報を瞬時に送信することは物理的に不可能なので、これらの操作は完了したように見えればよい。例えば、単一のグローバル共有バスを利用したシステムでは、バスラインに情報がポストされると、すべてのプロセッサが同じ瞬間にその情報を見ることが保証される。そのため、バスラインに情報を渡すことで、すべてのプロセッサに対して実行が完了し、実行されたように見える。キャッシュレス・アーキテクチャや、インターコネクト・ネットワークが瞬間的ではないキャッシュ・アーキテクチャでは、プロセッサとメモリの間にスロー・パスが含まれることがある。このような遅い経路では、あるメモリが他のメモリよりも早くブロードキャストデータを受け取るため、シーケンシャルな不整合が生じる可能性がある。
逐次一貫性は、非決定的な結果をもたらすことがある。これは、プロセッサ間の逐次操作の順序が、プログラムの異なる実行時に異なる可能性があるためである。すべてのメモリ操作は、プログラムの順番通りに行われる必要がある。
線形化可能性(不可分整合性とも呼ばれる)は、逐次一貫性にリアルタイム制約を加えたものと定義できる。
因果一貫性(因果整合性)とは、イベントを因果関係のあるものとないものに分類することで、順序の整合性を弱めるモデルである。このモデルでは、因果関係のある書き込み操作だけが、すべてのプロセスから同じ順序で見られる必要があると定義している。
このモデルでは、プロセッサによる同時書き込みと、因果関係のない書き込みについて、逐次整合性(逐次一貫性)を緩和している。2つの書き込みが因果関係を持つのは、1つの変数への書き込みが、任意の変数への以前の書き込みに依存している場合、2つ目の書き込みを行うプロセッサが1つ目の書き込みを読んだばかりの場合である。2つの書き込みは、同じプロセッサで行われた場合もあれば、異なるプロセッサで行われた場合もある。
逐次整合性(逐次一貫性)と同様に、読み取りは変更を瞬時に反映させる必要はないが、変数へのすべての変更を逐次反映させる必要がある。
W1はW2とは因果関係がない。
W(x)1とW(x)2は、W(x)2の前にP2がxに対して行った読み取りにより、因果関係がある。
データの一貫性を保ち、各プロセッサが独自のメモリを持つスケーラブルなプロセッサシステムを実現するために、プロセッサの一貫性モデルが考案された。すべてのプロセッサは、あるプロセッサが行った書き込みを見る順序と、異なるプロセッサが同じ場所に書き込んだ書き込みを見る順序が一致している必要がある(コヒーレンスの維持)。しかし、異なるプロセッサが異なる場所に書き込んだ場合には、一貫性を保つ必要はない。
すべての書き込み操作は、すべてのメモリに対するいくつかのサブ書き込みに分割することができる。このようなメモリからの読み出しは、このメモリへの書き込みが完了する前に起こる可能性がある。そのため、読み込まれたデータが古くなってしまうことがある。したがってプロセッサ一貫性下のプロセッサは、古いストアをストールする必要があるときに、より新しいロードを実行することができる。このモデルでも、Read before write、Read after read、Write before writeの順序は守られている。
プロセッサ一貫性モデルは、PRAM一貫性モデルに似ているが、同じメモリ位置へのすべての書き込みが、他のすべてのプロセスから同じ順序で見えるようにしなければならないという、より強い条件を備えている。プロセッサ整合性は、逐次整合性よりは弱いが、PRAM整合性モデルよりは強い。
Stanford DASHマルチプロセッサシステムでは、Goodmanの定義と比較できない(弱くもなく強くもない)プロセッサ一貫性のバリエーションを実装している。すべてのプロセッサは、あるプロセッサによる書き込みを見る順序と、異なるプロセッサによる同じ場所への書き込みを見る方法が一貫している必要がある。しかし、異なるプロセッサによる異なる場所への書き込みについては、一貫性を保つ必要はない。
パイプラインRAM整合性(PRAM整合性)は、1988年にLiptonとSandbergによって最初に記述された一貫性(整合性)モデルの1つとして発表された。非公式な定義のため、実際には少なくとも2つの微妙に異なる実装があり、1つはAhamadらによるもの、もう1つはMosbergerによる。
PRAM一貫性では、すべてのプロセスが、あるプロセスの操作を、そのプロセスが発行したのと同じ順序で見るが、異なるプロセスが発行した操作は、異なるプロセスから異なる順序で見ることができる。PRAMの一貫性は、プロセッサの一貫性よりも弱い。PRAMでは、ある場所へのコヒーレンスをそのプロセッサ全体で維持する必要性が緩和されている。ここでは、任意の変数への読み出しは、プロセッサ内で書き込みの前に実行することができる。このモデルでは、書き込み前の読み取り、読み取り後の読み取り、書き込み前の書き込みの順序は維持される。
キャッシュ整合性 (一貫性) は、同じメモリ位置へのすべての書き込み操作が、ある順序で実行されることを必要とする。キャッシュの整合性は、プロセッサの整合性よりも弱く、PRAMの整合性とは比較にならない。
遅い一貫性 では、あるプロセスがメモリの場所に以前に書き込まれた値を読み取ると、その後その場所から以前の値を読み取ることはできない。また、あるプロセスが行った書き込みは、そのプロセスがすぐに見ることができる。遅い一貫性は、PRAMやキャッシュコンシステンシーよりも弱いモデルである。
例: 遅いメモリーの図は、遅い一貫性の例を描いている。最初のプロセスがメモリロケーションXに1を書き込み、次にメモリロケーションYに1を書き込む。2番目のプロセスがYから1を読み込み、XがYより先に書き込まれたにもかかわらず、Xから0を読み込む。
Hutto, Phillip W., and Mustaque Ahamad (1990)は、適切なプログラミングによって、スローメモリ(一貫性)が表現力と効率性を持つことを説明している。彼らは、スローメモリには2つの価値ある特性があると述べている。それは、局所性と不可分的メモリからの削減をサポートすることである。彼らはスローメモリの表現力を示すために2つのアルゴリズムを提案している。
以下のモデルでは、プログラマーによる特定の同期を必要とする。
プログラムの順序やアトミック性(不可分性)は、すべての読み取りと書き込みではなく、特定の操作に対してのみ維持される。これは、クリティカルセクションで行われるようなある種のメモリ操作は、クリティカルセクション内のすべての操作が完了するまで、すべてのプロセッサには見えないようにする必要があるという理解に基づいている。また、マルチプロセッサシステムで実行されるプログラムには、データレースが起こらないように必要な同期が含まれており、常に逐次一貫性の結果が得られるようになっていることを利用している。このように、弱い順序(ウィークオーダリング)では、同期操作以外の操作をデータ操作に分類することができる。
同期操作は、すべてのプロセッサによって行われた以前のすべての操作を完了して見たことを確認するために、プロセッサに信号を送信する。弱い順序を維持するために、同期操作の前の書き込み操作は、同期操作の前にグローバルに実行されなければならない。また、同期操作の後に存在する操作は、同期操作が完了した後にのみ実行されなければなりません。したがって、同期変数へのアクセスは逐次的に一貫しており、読み出しや書き込みは、以前の同期操作が完了した後にのみ実行されなければならない。このモデルでは、一貫性は緩和されない。これらの要件が満たされていれば、他のすべての「データ」操作の順序を変更することができる。
プログラム内の明示的な同期への高い依存度がある。弱い順序モデルでは、プログラマは、テスト&セット、fetch-and-op、ストア・コンディショナル、ロード・リンクなどのアトミック・ロック命令を使用するか、同期変数にラベルを付けるか、フェンスを使用しなければならない。
リリース一貫性モデルは、入口の同期操作と出口の同期操作を区別することで、弱一貫性モデルを緩和する。弱い順序では、同期操作を見ようとするとき、同期操作が完了してプロセッサが進む前に、すべてのプロセッサのすべての操作が見える必要がある。しかし、リリース一貫性モデルでは、「アクワイア(取得)」と呼ばれるクリティカルセクションへの入り口では、ローカルメモリ変数に関するすべての操作が完了している必要があります。また、「リリース(解放)」と呼ばれる終了時には、ローカルプロセッサで行われたすべての変更が他のすべてのプロセッサに伝搬される必要がある。コヒーレンスは維持される。
アクワイア(取得)操作は、クリティカルセクションにアクセスするために実行されるロード/リード(読み込み)である。リリース(解放)操作は、他のプロセッサに共有変数を使用させるために実行されるストア/ライト(書き込み)である。
同期変数の中でも、逐次(シーケンシャル)一貫性やプロセッサ一貫性を保つことができる。逐次一貫性では、競合するすべての同期変数を順番に処理する必要がある。しかし、プロセッサ一貫性では、競合する変数のペアは、この順序に従うだけでよい。新しい取得者(アクワイアする者)が古いリリース(解放)の前に起こることを許容することができる。
リリース一貫性モデルの一種である。クリティカルセクションへの進入・退出を明示するために、アクワイア(取得)命令とリリース(取得)命令を使用する必要がある。しかし、エントリ一貫性では、すべての共有変数に、その変数に固有の同期変数が割り当てられる。このようにして、変数xに対する取得のときだけ、そのプロセッサに関してxに関連するすべての操作を完了する必要がある。これにより、異なる共有変数の異なるクリティカルセクションの同時操作が可能となる。同一の共有変数に対するクリティカルな操作では同時性は見られない。このような一貫性モデルは、異なる行列要素を同時に処理できる場合に有効である。
一般的な一貫性では、すべてのプロセスの書き込みが完了した後、メモリ位置のすべてのコピーが最終的に同一になる。
ローカル一貫性(局所整合性)では、各プロセスは自分のプログラムで定義された順序で自分の操作を行う。他のプロセスの書き込み操作がどのような順序で行われるかについては、何の制約もない。ローカルコンシステンシーは、共有メモリシステムにおいて最も弱いコンシステンシーモデルである。
緩和型一貫性モデルと呼ばれる、逐次一貫性の要件を緩和した一貫性モデルがある。これらの一貫性モデルは、ハードウェアレベルでのメモリ一貫性を提供しない。実際には、プログラマーは同期技術を適用してメモリ一貫性を実装する責任がある。上記のモデルは、4つの基準に基づいて分類されており、さらに詳しく説明する。
緩和した一貫性を定義するために、4つの比較がある。
緩和された整合性を分類する一つの方法は,どの逐次整合性要件を緩和するかを定義することである。Adve and Gharachorloo, 1996で定義されたプログラム順序や書き込み原子性の要件を緩和することで、より厳密ではないモデルを作ることができる。プログラム順序は、各プロセスがプログラムによって順序付けられたメモリ要求を発行することを保証し、書き込み不可分性は、メモリ要求が単一のFIFOキューの順序に基づいて処理されることを定義している。プログラム順序の緩和では、write-after-write、read-after-write、read/write-after-readのいずれかまたはすべての操作ペアの順序を緩和することができる。緩和された書き込みの不可分性モデルでは、プロセスは他のプロセッサよりも先に自分の書き込みを見ることができる。
同期モデルとは、メモリアクセスを2つのグループに分け、それぞれのグループに異なる一貫性制約を割り当てることで定義される。これは、一方のグループには弱い一貫性モデルが必要であり、他方のグループにはより厳しい一貫性モデルが必要であることを考慮したものである。一方、非同期型モデルでは、メモリアクセスタイプに同じ一貫性モデルを割り当てる。
発行法式は、プロセスがメモリ操作を行う際の制限を定義することで、逐次的整合性シミュレーションを行う。一方、観測方式は、プロセスのイベント順序に対する可視性の制限を記述する。
一貫性モデルの中には、他のモデルよりも制限が厳しいものがある。言い換えれば、厳密な一貫性モデルは、より多くの制約を一貫性要件として強制する。モデルの強さは、プログラムの順序や不可分性の緩和によって定義され、モデルの強さを比較することもできる。あるモデルは、同じ緩和を適用しているか、それ以上の緩和を適用している場合、直接に関係している。一方、異なる要件を緩和したモデルは直接関係しない。
逐次整合性(一貫性)には、プログラム順序と書き込みの不可分性という2つの要件がある。これらの要件を緩和することで、異なる緩和された一貫性モデルを得ることができる。これは、緩和された制約とともに、性能が向上するように行われるが、プログラマは、同期技術を適用してメモリ一貫性を実装する責任があり、ハードウェアを十分に理解していなければならない。
緩和の可能性:
以下のモデルは、緩和された一貫性のいくつかのモデルです。
ハードウェアレベルで性能を向上させるためのアプローチとして、書き込みの後に読み出しを行うプログラム順序(PO)を緩和することで、書き込み操作のレイテンシーを効果的に隠すことができる。この種の緩和は、プロセッサからの以前の書き込みに対して、後続の読み取りの順序を緩和するという最適化に依存している。この緩和のために、XXXのようないくつかのプログラムは、逐次一貫性(SC)の結果を出すことができないかもしれない。一方、YYYのようなプログラムは、残りのプログラム順序制約が適用されているため、一貫した結果が得られることが期待される。
3つのモデルが該当する。IBM 370モデルは、最も厳格なモデルである。読み取りは、異なるアドレスへの先の書き込みの前に完了することができるが、すべてのプロセッサが書き込みを見ていない限り、書き込みの値を返すことは禁止されている。SPARC V8のTSO(total store ordering model)モデルは、IBM 370モデルを部分的に緩和したもので、同じ場所への他の書き込みに対して、自分のプロセッサの書き込みの値を返すことができる。前述のモデルと同様に、これもすべてのプロセッサが書き込みを見ていない限り、書き込みの値を返すことはできない。プロセッサ一貫性モデル(PC)は、3つのモデルの中で最も緩和されたモデルであり、両方の制約を緩和して、他のプロセッサから見えるようになる前であっても、以前の書き込みよりも先に読み取りが完了できるようにしている。
例Aでは、IBM 370ではwrite(A)が完了するまでread(A)が発行されないため、この結果はIBM 370でのみ可能である。一方、TSOやPCでは、1つのプロセッサでフラグの書き込みが完了する前にフラグの読み出しが可能なため、この結果が得られる。
例Bでは、P2がP3に表示される前に書き込みの値を返すことができるため、PCでのみ可能となる。これは、他の2つのモデルでは不可能である。
上記のモデルでは、順序の一貫性を確保するために、セーフティネットやフェンスを用いて手動で制約をかけています。IBM370モデルでは、操作の間に手動で配置される特殊な直列化命令があります。これらの命令は、メモリ命令や分岐などの非メモリ命令で構成される。一方、TSOモデルやプロセッサ一貫性(PC)モデルではセーフティネットが用意されてないが、プログラマはリードモディファイ・ライトオペレーションを使用して、書き込みとそれに続く読み出しの間でプログラムの順序が維持されているように見せることができる。TSOの場合、すでにR-modify-Wの一部となっているRまたはWをR-modify-Wに置き換えれば、プログラム順序(PO)が維持されているように見えるが、これにはR-modify-WのWが読み取り値を返す「ダミー」であることが必要である。PCについても同様に、リードがライトに置き換えられたり、すでにR-modify-Wの一部になっていたりしても、プログラム順序(PO)は維持される。
しかし、この緩和だけを行使しても、コンパイラ最適化はできない。コンパイラ最適化には、プログラム順序(PO)内の任意の2つの操作を並べ替えることができる完全な柔軟性が必要なので、リードに対するライトの並べ替え機能は、この場合には十分に役立たない。
モデルによっては、プログラムの順序をさらに緩和し、異なる場所への書き込み間の順序制約さえも緩和するものもある。SPARC V8のパーシャルストアオーダーモデル(PSO)は、このようなモデルの唯一の例である。同じプロセッサから異なる場所への書き込みをパイプライン化し、オーバーラップさせることができることが、PSOが可能にする重要なハードウェア最適化である。PSOは、プロセッサが自分の書き込みの値を読み取ることができ、他のプロセッサが他のプロセッサの書き込みを読み取ることができないようにするという点で、不可分性の要件についてはTSOと同様である。2つの書き込み間のプログラム順序は、PSOでは明示的なSTBAR命令を用いて維持される。このSTBAR命令は、FIFO書き込みバッファを持つ実装では、書き込みバッファに挿入される。カウンタは、STBAR命令以前のすべての書き込みが完了したかどうかを判断するために使用され、メモリシステムへの書き込みが開始されてカウンタが増加します。書き込み確認でカウンタが減少し、カウンタが0になると、それまでの書き込みがすべて完了したことになる。
例Aと例Bでは、PSOによってこれらの非連続的に一貫した結果が得られる。PSOが提供するセーフティーネットはTSOのものと同様で、書き込みから読み出しまでのプログラムの順序を決め、書き込みの不可分性を強制する。
前のモデルと同様に、PSOが許容する緩和は、コンパイラの最適化に役立つほど十分に柔軟ではなく、もっと柔軟な最適化が必要である。
いくつかのモデルでは、異なる場所へのすべての操作が緩和される。読み込みや書き込みは、別の場所にある別の読み込みや書き込みを基準にして、順序を変えることができる。弱い順序はこのカテゴリに分類され、2種類のリリース一貫性モデル(RCscとRCpc)もこのモデルに含まれる。また、この緩和のカテゴリには、Digital Alpha、SPARC V9 relaxed memory order (RMO)、IBM PowerPCの3つの商用アーキテクチャが提案されていた。これらのモデルは、Digital Alphaを除いて、同じ場所への読み出しの再順序付けが可能だった。これらのモデルでは、従来のモデルにはない追加的な緩和が認められており、読み出し操作に続くメモリ操作は、読み出しに対してオーバーラップして順序を変更することができる。RCpcとPowerPCを除くこれらのモデルでは、読み出しが他のプロセッサの初期書き込みの値を返すことができる。プログラマーの観点からすると、これらのモデルは、プロセッサが自分の書き込みを早く読むことができるにもかかわらず、書き込みの不可分性の錯覚を維持しなければならない。
これらのモデルは、提供されるセーフティネットの種類に基づいて2つのカテゴリーに分類される。ここでは、慎重に書かれたプログラムの必要性が見られる。同期の性質によって、弱い命令、RCsc、RCpcモデルに分類される。Alpha、RMO、PowerPCモデルでは、フェンス命令が用意されており、異なるメモリ操作の間にプログラムの順序を課すことができる。
上記の制約のほとんどを緩和したモデルの例として、弱い順序(Weak Ordering)がある(他のプロセッサの書き込みを早く読むことを除く)。このモデルでは、メモリ操作をデータ操作と同期操作の2つのカテゴリに分類します。プログラムの順序を強制するために、プログラマはプログラムの中に少なくとも1つの同期操作を見つける必要がある。これが機能する前提は、同期操作の間にデータ領域へのメモリ操作を並べ替えても、プログラムの結果には影響しないということである。同期操作は、プログラムの順序を守るためのセーフティネットとして機能する。この仕組みは、データ操作の回数をカウンターで追跡し、このカウンターがゼロになるまで、同期操作は発行されないというものである。さらに、以前の同期操作がすべて完了しない限り、それ以上のデータ操作は発行されない。2つの同期変数の間にあるメモリ操作は、プログラムの正しさに影響を与えることなく、オーバーラップしたり、順序を変えたりすることができる。このモデルでは、書き込みの不可分性が常に維持されるため、弱い順序のための追加のセーフティネットは必要ありません。
リリース一貫性には、RCsc (release consistency with sequential consistency)とRCpc (release consistency with processor consistency)の2種類がある。後者のタイプは、以下で特殊とされる操作に適用される一貫性のタイプを示す。
特殊な(通常の)メモリ操作があり、それは同期操作と非同期操作の2つのクラスの操作からなる。後者は同期に使用されない操作で、前者はアクイジション(取得)とリリース(解放)の操作で構成される。アクイジションとは、ある特定の共有ロケーションへのアクセスを得るための、事実上のリードメモリ操作のことである。一方、リリースとは、共有されている場所へのアクセス許可を与えるために行われる書き込み操作である。
リリース一貫性と逐次一貫性(RCsc)の場合、制約条件は次のようになる。
アクワイア(取得) → その他の操作
その他の操作 → リリース(解放)
特殊操作 → 特殊操作
プロセッサ一貫性とリリース一貫性(RCpc)では、プログラムの書き込みから読み出しまでの順序が緩和され、次のような制約となる。
アクワイア(取得) → その他の操作
その他の操作 → リリース(解放)
特殊操作 → 特殊操作 (特殊な書き込みの後に特殊な読み込みが続く場合を想定)。
注:上記のA→Bという表記は、プログラム順でAの操作がBよりも先に行われた場合、プログラム順が強制されることを意味する。