最も単純なスピンロックアルゴリズムを除いて、ミューテックスコードは非常に複雑です。優れた最適化されたミューテックスロック/ロック解除コードには、優れたプログラマーでさえ理解するのに苦労している種類のコードが含まれています。特別な比較および設定命令を使用し、ロック解除/ロック状態だけでなく待機キューも管理し、オプションでシステムコールを使用して待機状態(ロックの場合)に移行するか、他のスレッドをウェイクアップ(ロック解除の場合)します。
平均的なコンパイラーが(単純なスピンロックを除いて)すべての複雑なコードをデコードして「理解」する方法はありません。したがって、コンパイラーでさえ、ミューテックスが何であるか、そしてそれがどのように関連するかを認識していません。同期するために、実際には、コンパイラがそのようなコードの周りで何かを最適化する方法はありません。
これは、コードが「インライン」であるか、クロスモジュール最適化の目的で分析に使用できる場合、またはグローバル最適化が使用できる場合です。
コンパイラはpthread_mutex_lock()が特殊関数であることを実際には理解していないと思いますが、シーケンスポイントによって保護されているだけですか?
コンパイラーはそれが何をするかを知らないので、それを中心に最適化しようとはしません。
どのように「特別」ですか?不透明で、そのように扱われます。不透明な関数の中でも特別なことではありません。
他のオブジェクトにアクセスできる任意の不透明な関数と意味上の違いはありません。
私の懸念はキャッシングです。コンパイラは_protectedのコピーをスタックまたはレジスタに配置し、その古い値を割り当てに使用できますか?
はい、コンパイラが従うことができる方法で変数名またはポインタを使用することにより、オブジェクトに透過的かつ直接作用するコードでは。変数を間接的に使用するために任意のポインターを使用する可能性のあるコードではありません。
したがって、不透明な関数の呼び出しの合間にはそうです。渡らない。
また、関数でのみ使用できる変数の場合、名前で:アドレスまたは参照がバインドされていないローカル変数の場合(コンパイラがそれ以降のすべての使用を追跡できないようにするため)。これらは実際、ロック/ロック解除を含む任意の呼び出しにわたって「キャッシュ」することができます。
そうでない場合、それが起こらないようにするものは何ですか?このパターンのバリエーションは脆弱ですか?
関数の不透明度。インライン化されていません。アセンブリコード。システムコール。コードの複雑さ。コンパイラを救済し、「それは複雑なことはただそれを呼び出すだけだ」と考えるすべてのもの。
コンパイラのデフォルトの位置は、 「それを最適化する/私がよく知っているアルゴリズムを書き直そう」ではなく、常に「とにかく何が行われているのかわからない愚かな実行をしよう」です。ほとんどのコードは、複雑な非ローカルな方法で最適化されていません。
ここで、絶対的に悪いと仮定しましょう(コンパイラーが諦めるべきであるという観点から、それは最適化アルゴリズムの観点から絶対的に最良です):
- 関数は「インライン」(=インライン化に使用可能)です(またはグローバル最適化が開始されるか、すべての関数が道徳的に「インライン」になります)。
- その同期プリミティブ(ロックまたはロック解除)では、(モノプロセッサのタイムシェアリングシステムやマルチプロセッサの強力に順序付けられたシステムのように)メモリバリアは必要ないため、そのようなものは含まれていません。
- 特別な命令(比較や設定など)は使用されません(たとえば、スピンロックの場合、ロック解除操作は単純な書き込みです)。
- スレッドを一時停止またはウェイクアップするためのシステムコールはありません(スピンロックでは必要ありません)。
次に、コンパイラが関数呼び出しを中心に最適化できるため、問題が発生する可能性があります。これは、他のアクセス可能な変数の「クローバー」を含む空のasmステートメントなどのコンパイラバリアを挿入することで簡単に修正されます。つまり、コンパイラは、呼び出された関数にアクセスできる可能性のあるものはすべて「クローバー」されていると想定しているだけです。
または、保護された変数を揮発性にする必要があるかどうか。
物事を揮発性にする通常の理由で、それを揮発性にすることができます。デバッガーで変数にアクセスできるようにするため、浮動小数点変数が実行時に間違ったデータ型を持つのを防ぐためなどです。
揮発性は本質的に抽象マシンのメモリ操作であり、I / O操作のセマンティクスを持ち、それ自体は
- iostreamのような実際のI/O
- システムコール
- その他の不安定な操作
- asmメモリクローバー(ただし、メモリの副作用はそれらの周りに並べ替えられません)
- 外部関数の呼び出し(上記のいずれかを実行する可能性があるため)
揮発性メモリの副作用に関しては、揮発性は順序付けられていません。これにより、volatileが先験的に役立つ最も具体的なケース、つまりメモリフェンスが不要な場合でも、スレッドセーフコードを記述するためにvolatileは実質的に役に立たなくなります(実際の使用には役に立たなくなります) 。シングルCPU。(これは、CまたはC ++のいずれかの最も理解されていない側面の1つである可能性があります。)
したがって、volatileは「キャッシング」を防ぎますが、すべての共有変数がvolatileでない限り、volatileはコンパイラによるロック/ロック解除操作の並べ替えを防ぎません。