commentこれまでも話してきたとおり、スレッド間でオブジェクトを共有することはハイコストなので、可能な限りオブジェクトを共有しない設計にすることが性能においても品質においても重要です。してもオブジェクトを共有したい場合は、昨今の処理系にはそれを保証する機能が用意されているのでそれを使って安全を担保します。Cではvolatile修飾子をこの用途に使ってはいけません。なんの効果も得られないか、特定の環境下で効果があったとしてもそれは移植性がないコードで、より複雑なバグを埋め込むことになります。Cでの第一選択肢はPOSIXスレッド(pthreads)のMUTEXです。MUTEXは他のスレッドからの割り込みに対しては安全ですが、非同期シグナル安全ではありませんので、これも同時に考慮して設計する必要があります。それではスレッド間で共有する変数のカウントアップをMUTEXで排他制御したコードに、timeコマンドを噛ませて実行してみましょう。0;}このとき、主とする処理が単なる数値演算のようなOSの機能を必要としないものにも関わらず、やたらとシステムCPU時間を消費している場合には、排他制御のオーバーヘッドが大きいことを疑ってみてください。プロファイラにかけて重い処理を探ってみるのもよいですが、timeコマンドの出力するCPU時間は取得が簡単でとても参考になります。10000000real0m2.117suser0m1.272ssys0m14.747sこのようにMUTEXでは役不足な処理の場合、排他制御の実現にOSが介在する上位のAPIではなく、メモリバリアを抽象化した下位のAPIを使う選択肢もあります。このライブラリを使ってカウントアップ処理を保護するMUTEXを外し、インクリメントをatomic_fetch_add()に置き換えてみましょう。10000000real0m0.252suser0m1.776ssys0m0.000sシステムCPU時間を消費していないことがわかります。この例ではMUTEXよりも高速です。_Atomic修飾子です。10000000real0m1.292suser0m9.514ssys0m0.000sこちらもシステムCPU時間が消費されていません。これらは、OSが提供する機能ではなく、アプリケーションの機械語レベルで非同期スレッド安全な処理にコンパイルされています。最後に、完全にシングルで書いたものを実行してみます。10000000real0m0.028suser0m0.028ssys0m0.000sこれまでで最速の処理時間です。シングルの場合、非同期スレッド安全のために保証しなくてはならない、次に上げる事柄を一切考慮する必要がありません。不可分性クリティカルセクションに割り込ませないか、割り込まれても問題がないことの保証。可視性あるスレッドが行った操作の結果が、他のスレッドからも観測できることの保証。コンパイラがある処理をレジスタ上で完結するように最適化してしまったり、CPUキャッシュの中で完結してメモリに書き込まれていなかったりすると、他のスレッドからはその値を参照できない場合がある。逐次性あるスレッドが行った処理の順序が、他のスレッドでも同じ順序で観測できることの保証。コンパイラが不要と判断した処理を省略してしまったり、CPUのアウトオブオーダー実行によって命令の実行順序が入れ替わってしまうと、期待する条件が成立しない場合がある。これらを考慮する必要が無い場合、極端に言えばコンパイラは最適化によってただ「10000000」と標準出力に書き込むバイナリを生成しても許されるわけで、実際そのような最適化が行われます。一貫性のある設計のためには、なにを保証し、なにを保証しないのかを明確にしておくべきです。ときとして、これらの設計と実装はとても複雑になり、シングルで作ったほうがよい場合があります。そのことに早い段階で気づくための作業が設計フェーズです。コメントをキャンセルメールアドレスが公開されることはありません。*サイト次回のコメントで使用するためブラウザーに自分の名前、メールアドレス、サイトを保存する。