質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

6回答

17376閲覧

volatileが必要な場面を見つけ出す

strike1217

総合スコア651

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

2グッド

6クリップ

投稿2018/02/19 06:04

例えば、以下のコード

C

1#include<signal.h> 2 3sig_atomic_t interrupted; 4 5void sigint_handler(int signum){ 6 interrupted = 1; 7} 8 9int main(){ 10 signal(SIGINT, sigint_handler); 11 while(!interrupted){ 12 [何かの処理] 13 } 14 return 0; 15}

このコードは sig_atmoic_t interrupted にvolatileが付いていないので最適化するとバグります。

main()からの読み取りは、コンパイラの最適化によって削除されてしまう可能性がある。

真っ先に思ったこと:「知らねぇぇぇ・・・ & なんで??」
最適化によって削除されたり、変更されたりする変数を把握することができません。
**つまり、volatileが必要、不必要な場面がわからない。**ということです。

変数宣言にvolatile修飾子を追加すると、intterruptedはwhileループの繰り返しやシグナルハンドラからアクセスされる際に、必ず元のアドレスからアクセスされることが保証される。

キャッシュできないデータにはvolatileを使う

(キャッシュ?これは・・・CPUに備わっているキャッシュのことですかね?
元のアドレスとはなんのこと?)

皆さんは、どうやってvolatileが必要な部分を見つけているんでしょうか?

プログラムを作る -> 最適化するとバグる -> volatileが必要な部分を探す。

しかし、これだとバグを前提にvolatileを探すことになるので、良い方法とは言えませんが・・・

あるパターンが存在するはずです。
volatile修飾の規則を教えてください。
しかし、全パターンを覚えるんですか??
それは流石に無理ですよね?

_[ここから私の妄想]___________
volatileによる最適化抑止機能は、変数に対してに加えて、関数ごとに抑止できると便利ではないでしょうか?

C

1volatile int buffer_ready; 2char buffer[BUF_SIZE]; 3 4void buffer_init(){ 5 for(size_t i = 0; i < BUF_SIZE; i++) 6 buffer[i] = 0; 7 buffer_ready = 1; 8}

簡単な関数の例なのですが、

volatile宣言がされている場合、コンパイラはそのデータが置かれたメモリに対する読み書きの順序を変更できない。しかし、これらの読み書きと、他のメモリに対する読み書きとの順序を変更してもよい。

したがって、コンパイラはこのループをbuffer_readyへの書き込みの後に移動することができる。これはプログラマの意図に反する。

今回の場合は、buffer_readyが入れ替わっても問題無さそうですが、複雑な関数になると問題になってくるはずです。
そうすると、bufferの方にもvolatileを付ける羽目になり、volatileがどんどん増えていきます。

buffer_init()そのものを最適化抑止させる機能があると便利ではないでしょうか?

ラムダ式って何が便利なのです??
以前の質問で、main()を内をさらに細かい関数単位で分割できる・・・と言ったんですが、すると関数の中もラムダ式を使って細かく最適化抑止を制御できるのではないか?(ラムダ関数を最適化抑止する)
という想像ができます。
ラムダ式の利便性が向上しそうです。
__________________________

まぁ、私の妄想はどうでもいいんですけど・・・関数全体に対して最適化抑止機能が付いているのはどうかなぁ??と思っただけの話なのです。

ここでの疑問は「どうやって、volatile修飾が必要な箇所を見つければ良いんでしょうか??」という疑問です。
パターンがあるなら、是非教えてください。

umyu, KSwordOfHaste👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

ベストアンサー

(この回答はC言語またはC++言語を前提とします)

どうやって、volatile修飾が必要な箇所を見つければ良いんでしょうか??

普通のPC/サーバ上で動作する一般的なアプリケーションを開発しているときは、volatile修飾のことを気にする必要はありません。

真にvolatile修飾が必要となるのは、以下のパターンいずれかと思います:

  • シグナルハンドラ(signal handler)を記述するとき。
  • デバイスドライバなど外部ハードウェアとのI/O処理を直接記述するとき。
  • 組込機器/マイコン/DSP上で動作するプログラムを記述するとき。
  • オペレーティング・システム(OS)本体を記述するとき。

補足:「マルチスレッドアプリ開発ではvolatileが必要」という主張は、C/C++言語においては全くの誤りです。これらは昔のC/C++開発環境を前提としているか、他言語の話題と混同されているケースが大半です。


プログラムを作る -> 最適化するとバグる -> volatileが必要な部分を探す。
しかし、これだとバグを前提にvolatileを探すことになるので、良い方法とは言えませんが・・・

volatile修飾を「正しく」利用する(付けるべきところで修飾し、余計なところには使わない)のはとても難しい問題です。機械的に正誤判定を行う方法は私も知りません。最終的にはプログラマ自身が正しい知識を身につけることと、レビュープロセスなどで人的にチェックするしかないと思います。

投稿2018/02/19 07:24

yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/02/19 07:29

「volatile修飾を「正しく」利用するのはとても難しい問題です。」 そ、そうなんですね >< アセンブリ言語を読んだり、コンパイラのソースを読んだりする必要が出てくるんですかね・・・ かなり高度ですが・・・ デバイスドライバやOSの記述など・・・結構高度なもので出現するんですね。
Chironian

2018/02/19 07:34

シグナルハンドラで変更する変数にvolatileを付けるのと全く同じ理由で、別スレッドでも変更する変数もvolatileを付ける必要が有る場合があるのでは?
strike1217

2018/02/19 07:38

時々、volatileがロック機能を提供しているという誤った理解をしている人がいるようですが・・・ そのことでは??
yohhoy

2018/02/19 07:38

普通のアプリ開発では、シグナルハンドラ利用時を除いて、「低レイヤー/ハードウェア寄りのプログラムでしかvolatile修飾を使わない」程度の認識でも困らないと思います。 volatile=最適化抑止 という認識でも問題ないですが、もう少し掘り下げると、volatile変数は「コンパイラからは検知できない任意タイミングで更新される可能性のある変数」という意味を持ちます。それゆえに、コンパイラはvolatile変数の読込/書出を保守的に扱う、つまり(コンパイラの制御下にある)通常変数のような最適化対象にはしません。
yohhoy

2018/02/19 07:47 編集

Chironian さん C11以降、C++11以降はマルチスレッド処理でもvolatile修飾は不要ですし、全く役に立ちません。(それより古い環境ではコンパイラベンダ依存でvolatile修飾が必要なこともあります。) https://www.jpcert.or.jp/sc-rules/c-pos03-c.htmlhttps://ja.stackoverflow.com/a/1554/49 などもご参考に。 # VisualC++の独自拡張の例:https://msdn.microsoft.com/ja-jp/library/12a04hfd.aspx こちらも既に非推奨仕様ですけど。
strike1217

2018/02/19 07:42

> volatile変数は「コンパイラからは検知できない任意タイミングで更新される可能性のある変数」 スレッドには当てはまらない・・・ということですか??
yohhoy

2018/02/19 07:44 編集

>> volatile変数は「コンパイラからは検知できない任意タイミングで更新される可能性のある変数」 > スレッドには当てはまらない・・・ということですか?? はい。マルチスレッド処理については、C/C++コンパイラの制御下にあるとみなして良いです。スレッド間の同期や通信には、アトミック変数やミューテックス/ロック/排他制御を使います。
strike1217

2018/02/19 07:47 編集

あへ! 左様でございますか!紛らわしい!!
Chironian

2018/02/19 07:47

yohhoyさん。データ競合の回避ではなく、別スレッドでの変更をコンパイラが把握できないので最適化されるとヤバイという問題です。 もしかして、データ競合を回避したら必然的に最適化も抑止されるでしょうか? ちょっとここは自信がないです。
strike1217

2018/02/19 07:51

あああ、そうですよね。 データ競合と最適化抑止は関係ないですよね。
yohhoy

2018/02/19 08:56 編集

Chironianさん > 別スレッドでの変更をコンパイラが把握できないので最適化されるとヤバイという問題です。 別「スレッド」での変更を(間接的に)コンパイラに把握させるための仕組みが、アトミック変数やミューテックスです。volatile修飾にはこの役目はありません。https://www.jpcert.or.jp/sc-rules/c-pos03-c.html 参照ください。 > データ競合を回避したら必然的に最適化も抑止されるでしょうか? 例えば「正しく排他制御された変数アクセス」ではデータ競合を引き起こしません。このときコンパイラはロック操作を正しく認識するため、lock/unlock操作を超えるような「望ましくない変数アクセスの移動」(=誤った最適化)は行いません。 下記コードでは、変数b,cへの代入は最適化により並び替えられる可能性がありますが、lockより前やunlockより後ろ(=排他制御の外側)には移動しないことをコンパイラが保証します。このとき、排他制御で保護される変数b,cにvolatile修飾は不要です。 a = 1; lock(); b = 2; c = 3; unlock(); d = 4;
KSwordOfHaste

2018/02/19 08:01 編集

多分自分の回答は「昔のC/C++ないし他の言語と混同」に該当するのではないかと思います。Cでマルチスレッドプログラミングする際にintのような単純なフラグを用いて制御すること自体が古い考え方で、Windows/Linuxなどでの汎用OS上での通常プロセスでのマルチスレッドアプリケーションでは同期用のしかるべき関数なりクラスなりを使えばvolatileを意識しなくてはならないような場面を無くすことができる(というよりそういう場面をなくすような設計をすべきである)ということではないかと想像します。 しかしyohhoyさんのご指摘がそういう意味かどうか悲しいことに確信がもてませんでした>< --- 追記:自分がコメント書いている間に多数のコメントがついてました!どういう話かyohhoyさんコメントからうかがい知ることができそうです。
Chironian

2018/02/19 08:01 編集

yohhoyさん。 なるほど。そう言えば、関数呼び出しを挟めば最適化が抑止されたことがありました。そのように規定されているのですね!(lock()が同期用のロックかどうかコンパイラは把握できない筈ですし) アトミック変数はどうでしょう? 必要に応じて暗黙的にvolatileになるのでしょうか? ところで、シグナルハンドラも全く同じ話が成立するように思います。シグナルハンドラはスレッドの劣化版のようなもの(専用のスタックがないだけ)ですから。
strike1217

2018/02/19 08:07

「一般的なアプリケーションを開発しているときは、volatile修飾のことを気にする必要はありません。 」 ちょちょーーい待ってください。 私の妄想のところにも書いたのですが、「volatile宣言がされている場合、コンパイラはそのデータが置かれたメモリに対する読み書きの順序を変更できない。しかし、これらの読み書きと、他のメモリに対する読み書きとの順序を変更してもよい。」 メモリの読み書きの順序が変更されるマズイですよね?? これは低レイヤの話ではなく、通常のアプリケーションを開発する上では問題になるのではないでしょうか??
yohhoy

2018/02/19 08:40 編集

Chironian さん > 関数呼び出しを挟めば最適化が抑止されたことがありました。そのように規定されているのですね!(lock()が同期用のロックかどうかコンパイラは把握できない筈ですし) C11/C++11準拠コンパイラやPOSIX Threads(PTherads)準拠コンパイラは、ある関数がロック操作であることを正しく認識しています。一種のコンパイラマジックですね(大抵は関数属性/attributeが利用されています)。 そうでないコンパイラでも、関数の中身まで把握していない限りは積極的な最適化を行えませんから、(適当な)関数呼出を挟むと意図通りのアクセス順になることは良くありますw > アトミック変数はどうでしょう? 必要に応じて暗黙的にvolatileになるのでしょうか? ざっくり言えば、アトミック変数アクセスもロック操作と同じ意味を持つため、アトミック変数アクセスをまたぐ通常変数の移動は禁止されます。 # アトミック変数アクセスには厳密にはmemory_orderという概念があり、片方向には移動可能/両方向にも移動可能などを細かく制御できますが、複雑すぎるので無視してよいと思います... # 興味があれば C++ memory ordering や C++ memory model などで検索してみてください。 > シグナルハンドラも全く同じ話が成立するように思います。 C/C++言語仕様上は、シグナルハンドラとスレッドは別物として扱っており、必然的にC/C++コンパイラもそれにならいます。で、シグナルハンドラではvolatile修飾が必要という仕様です。別の表現では、volatile修飾とアトミック変数は直行した概念になっており、volatile修飾されたアトミック変数というものも存在しえます。(いつ使うんでしょう?)
yohhoy

2018/02/19 08:16

strike1217 さん ギモン自体は最もですが、端的に言えば「問題ない」という回答になります。下記スライドで説明されていますので参照ください。そもそもこの話題は非常に難解なので、"そういうもの"という理解でも大丈夫かもしれません(私見)。探求自体は止めませんが :D http://www.slideboom.com/presentations/83479/%E3%81%9D%E3%82%8D%E3%81%9D%E3%82%8Dvolatile%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E4%B8%80%E8%A8%80%E3%81%84%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8F%E3%81%8B
yohhoy

2018/02/19 08:29 編集

KSwordOfHaste さん Windows/VisualC++では比較的最近(Visual C++2010)までvolatile変数=スレッド同期用という独自拡張路線でしたし、Linux等でもPOSIX Threads準拠以前は似たような状況だったようです。(後者はいまでもGCCに-pthreadオプションつけないと挙動が怪しいといった話があるような無いような) volatile修飾やスレッド間同期の話題は「C11以降/C++11以降」、つまり 2011年 を境に大きく状況が変化しています。直ぐに新しい仕様や開発環境が普及するわけではないのでタイムラグはありますが、一つの目安にどうぞ。もう2018年ですしね!
Chironian

2018/02/19 08:36 編集

yohhoyさん。しつこくてすいません。 なるほど。アトミック変数は使い方的に最適化抑止が必要な場面がほとんどでしょうから、セットになっているのですね。ありがとうございます。 > C/C++言語仕様上は、シグナルハンドラとスレッドは別物として扱っており シグナルハンドラとスレッドの両方から呼ばれる関数も当然ありえるので、その時何が起こるのでしょう? スレッドから呼ばれた時はvolatile無しでも最適化抑止できてシグナルハンドラから呼ばれた時はvolatileがないと最適化抑止できないって、凄く不思議な感じを受けます。 単に規格策定に間に合わなかっただけとか?
yohhoy

2018/02/19 08:45 編集

Chironian さん > スレッドから呼ばれた時はvolatile無しでも最適化抑止できてシグナルハンドラから呼ばれた時はvolatileがないと最適化抑止できない この一文、思考順が逆なのかなという印象を受けました。プログラマ目線の方が分かりやすい気がします: - スレッドを考慮:アトミック変数を使う - シグナルハンドラを考慮:volatile変数を使う - 両方を同時に考慮:volatile修飾されたアトミック変数を使う http://d.hatena.ne.jp/yohhoy/20120701 にちょこっとだけまとめてあります。
Chironian

2018/02/19 09:18

yohhoyさん。ありがとうございます。 リンク先の「volatile変数とatomic変数は直交する概念であり」はたいへん納得できますが、対スレッドと対シグナルハンドラーでvolatileの必要性が変わってしまうのはどうにも納得できません。 しかし、ちょっと長くなりすぎたと思います。もっと噛み砕いてみて、それでも私の中で消化できなかったら別途質問を立てさせて頂こうと思います。その時は、またよろしくお願い致します。
yohhoy

2018/02/19 12:55 編集

(コレを書いてしまうと余計混乱を招くリスクがあるのですが、参考用にコメント残します。無視して貰っても構いません。) volatile修飾やスレッド同期に関するつっこんだ議論は、まず抽象的かつ形式的に記述された言語仕様による「保証範囲」からスタートします。そこで保証されない振る舞いは、たとえ現実世界では考えづらいものであっても「それは定義上は無保証。はいおしまい。」という論法になってしまいます。とりわけ順序性保証に関する議論では、言語仕様は非現実的な結果(たとえば因果律に反して見えるような)さえも許容したりします。 コンパイラによる最適化では、言語仕様が保証しない範囲を「自由度」と解釈します。その自由度を利用した最適化が実際に行われるか否かは、コンパイラの判断です。近年はよりアグレッシヴな最適化を行うコンパイラが増えおり、従来ならば厳密には仕様保証範囲外であった振る舞いが、新しいコンパイラでは壊れたように見える/実際にはプログラムの問題というケースがあります。
Chironian

2018/02/19 12:23

yohhoyさん。追記ありがとうございます。少し噛み砕いてみます。
strike1217

2018/02/19 13:36

では、「コンパイラからは検知できない任意タイミングで更新される可能性のある変数」の場合のみ考慮すれば良いのですね。 ん〜〜私の妄想の部分は問題にはならないんですね。 そうなると、volatileが必要となる場面は結構少ない・・・んですね。 殆どの場合は最適化を施しても問題になるケースは少ない・・・と考えて良いんですね。
maisumakun

2019/11/08 08:30

> 真にvolatile修飾が必要となるのは、以下のパターンいずれかと思います: どれでもないパターンでvolatileが必要となったことがありまして、ガベージコレクタの存在するRuby用のC言語拡張を書いているときに、ガベージコレクタによる回収を防いで「ここまでは変数を生き残らせなければならない」ということを表現するためにvolatileが登場したことがあります。
guest

0

こんにちは。

最適化は基本的に関数内で行われますから、関数内での変更だけであれば、おかしな動作にならないように最適化されます。(当然ですね。)
しかし、関数の外(割り込みや他のスレッド)で変更されるとコンパイラはその状況を把握できませんので、プログラマが教えて上げる必要があります。
つまり、関数が動作中に、割り込みや他のスレッドにて変更される変数にvolatileを付けます。


【yohhoyさんとの議論にて追記】
データ競合が発生する場合は、一般にロックやアトミック変数でデータ競合を回避します。
対スレッドの場合はその機構により、最適かも抑止されるのでvolatileは付けなくて良いと標準規格にて決まっているそうです。
ただし、対シグナルハンドラーについては標準規格でvolatileを付ける必要があるそうです。

シグナルハンドラーを実装するケースは稀でしょうから、ちゃんとデータ競合を回避しておけば、一般にはvolatileは付けなくても大丈夫と考えて良さそうです。
質問のサンプルはシグナルハンドラーですので、volatileが必要なようです。

投稿2018/02/19 06:29

編集2018/02/19 09:33
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/02/19 06:46

ああ!なるほど! シグナルは割り込みと見なせるので、シグナルによって変更される変数にvolatileを付ける。 と考えるわけですね。 つまり、主にはグローバル変数でしょうかね? ローカル変数にはvolatileは付けないということですよね?(割り込みやスレッドによって変更されないから)
Chironian

2018/02/19 07:16

確かに事実上それでOKと思います。 ↓で議論したのと同様、スレッドや割り込みハンドラーで共有する変数については同様に注意が必要です。 https://teratail.com/questions/113653 排他制御の問題と最適化抑止の問題は一緒に出てくるので混同しやすいですね。
strike1217

2018/02/19 07:20

「排他制御の問題と最適化抑止の問題は一緒に出てくるので混同しやすいですね。」 ああ!そうですね。 とりあえず、関数外の要因に関わる変数にはvolatileを!・・・は理解しました。
guest

0

訂正:

すみませんが、自分の回答はvolatileを付けないとどうなるかの一例を挙げたにすぎずvolatileをどう使うかというトピックにはあまりに不十分であると思います。yohhoyさん回答からそう気づきました。

例えばコンパイラーが生成するコードのみならずプロセッサーでもメモリーオーダリングが行われており、それを制御するのにはメモリーバリアを意識しなくてはならず、かつC/C++のvolatileはプロセッサによるメモリーオーダリングを制御するようなメモリーバリア機能を持たないということがyohhoyさん回答からわかりました。

そうした点だけとっても「volatileはそんな簡単に直接使えるようなものではない」と思いました。


自分の認識では「複数のスレッド(より厳密に言えば非同期に実行されるもの=割り込みなども含む)から並行して更新・参照される制御情報」にvolatileを付けるというのが判断基準です。

gccのコンパイルの一例ですが

C

1int flag = 1; 2main() { 3 while (flag) { 4 } 5}

これを-Ofastでコンパイルすると

text

1main: 2.LFB23: 3 .cfi_startproc 4 movl flag(%rip), %eax 5 testl %eax, %eax ; (A) 6 je .L2 7.L4: 8 jmp .L4 9 .p2align 4,,10 10 .p2align 3 11.L2: 12 xorl %eax, %eax 13 ret

となりました。このコードを見るとvolatileがないとなぜ困るかは自明と思います。プログラム開始直後に一度だけ行われる(A)の判断結果により「無限ループするか」「一度もループしないか」が決められてしまい、ループしながらflagの変化を待つという動作になりません。

複数のスレッドが更新・参照する可能性がある情報を扱う場合、特定のスレッドで過去のある時点にアクセスした結果得られた値を後で再び用いること、例えばレジスターなどへコピーした値を再度用いることはもちろんNGですよね?

キャッシュとは「プロセッサーから遠い場所にある情報のコピーをより近い場所に置いてそれを参照する」ということを表します。プロセッサーのL1/L2キャッシュとは限りません。最初に示したコンパイル結果が表しているように、レジスターやスタックも本件においてはキャッシュの一種として捉えるべと思います。

そのような**「オリジナルとは別の場所にキャッシュされた情報を参照することを許さない」のがvolatileの効果**と考えるのが第一歩で、そうされては困る情報にvolatileを付けるのが(少なくとも基本的な)判断基準だと思います。

投稿2018/02/19 06:50

編集2018/02/19 08:52
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/02/19 07:03 編集

むむ・・・ 「レジスターやスタックも本件においてはキャッシュの一種として捉えるべと思います。」 これについては納得いきました。 「オリジナルとは別の場所にキャッシュされた情報を参照することを許さない」 「オリジナル」とは、例えば、KSwordOfHasteさんのコードに載っているflagグローバル変数のようなものの事ですよね?
KSwordOfHaste

2018/02/19 08:26

> flagグローバル変数のようなもの 自分の回答意図はそのとおりです。 ただ、回答してはみたもののyohhoyさんコメントを拝見するに自分の回答はかなり不十分な回答だと思います。volatileをどのように使うかは簡単なトピックではなく、むしろ「それを使わない方法を学ぶ」方が一般アプリケーションプログラマーには優先すべきトピックじゃないかと思います。
guest

0

パターンというか私ならここでvolatileを使うというケースです。

  • ポーリング待ち用の変数にはつける。
  • I/Oにアクセスするためのポインタ変数にはつける。

最近のCPUはOutOfOrder実行が普通なので最適化の有無に関係なく変数にアクセスする順番が守られない可能性があります。
その場合はメモリフェンス命令によって次に実行する命令を待たせたりするのですが、マルチコアCPUをターゲットにした環境の場合はミューテックスやロックの関数にその機構が実装されてるのかもしれませんね。

投稿2018/02/19 08:12

TaroToyotomi

総合スコア1430

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

(キャッシュ?これは・・・CPUに備わっているキャッシュのことですかね?
元のアドレスとはなんのこと?)

この文脈におけるキャッシュとは、レジスタに値を保持して使い回すことを言います。元のアドレスとは、変数が格納されているアドレスのことです。
つまり、ある変数が関数の中で何度も繰り返し使用されるとき、最適化しない場合はその都度メモリ領域から変数の値を読み取りますが、最適化が有効な場合は、最初に一度メモリ領域から読み出してその値をレジスタに覚えておき、後はそれを使い回す、ということをします。

皆さんは、どうやってvolatileが必要な部分を見つけているんでしょうか?
あるパターンが存在するはずです。
volatile修飾の規則を教えてください。

volatileは探して見つけるようなものではありませんし、パターン化された規則によって付けるかどうかを決めるものでもありません。付けるかどうかは「設計上の必要性」に応じて決めます。
その変数がどのタイミングで誰がアクセスするかは、ちゃんと設計しているなら「当然把握している」はずなので、裏(別スレッドとか割り込みとか)で変更される可能性のあるもので、前述のような最適化をしてほしくない変数ににはvolatileを付けることになります。


一応、「_[ここから私の妄想]___________」について追記

buffer_init()そのものを最適化抑止させる機能があると便利ではないでしょうか?

問題の解決を誤った方法に求めると、より問題が深刻化します(あるいは潜在的な問題に気づけなくなります)。その手の制御は、最適化の有無などという不確かなもので行うのではなく、排他制御で行うのが普通です。buffer_readyとbufferへのアクセスのアトミック性を確保しさえすれば、メモリのアクセス順がどうなろうと問題にはなりません。


追記

私の経験的な見解としては、一般的なアプリを作る上ではvolatileはほとんど必要性を感じません。パフォーマンスのチューニングをしたいときに作る検証用コードで、最適化によって変数のアクセス回数が減って正しく計測できなくなるのは困る、というときに使う程度です。少なくとも、製品向けのプログラムでvolatileを使ったことはありません。

volatileの話題でマルチスレッドの話がよく出てきますが、同期目的で使えるのは、せいぜいフラグ変数程度でしょう。実際には、スレッド間/プロセス間の同期は、プラットフォームが提供する専用のAPIを使うのが最も確実です。製品を作る上では不確実な要素は排除しないといけませんから。
C++11でスレッドが導入されてからは、プラットフォーム依存のAPIを直接呼び出さなくても済むようになりましたね(標準ライブラリーにない便利な同期オブジェクトは直接API呼び出しすることになりますが)。

投稿2018/02/19 07:21

編集2018/02/20 02:05
catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/02/19 07:33

「ある変数が関数の中で何度も繰り返し使用されるとき、最適化しない場合・・・」 この「最適化しない場合」とは、コンパイル時に最適化オプションを付けていない・・・という意味でしょうか?それともvolatileによって最適化が抑止されている・・・という意味でしょうか??
strike1217

2018/02/19 07:37

あ、つまり、「最初に一度メモリ領域から読み出してその値をレジスタに覚えておき、後はそれを使い回す」 これをされると困る時に、volatileを付けるわけですね。
strike1217

2018/02/19 07:40

なるほど!理解いたしましたわ。
guest

0

ちょっとまとめます。他の人の回答に出てくるものだと・・・

・コンパイラからは検知できない任意タイミングで更新される可能性のある変数
・レジスタなどにコピーしたものを使いまわされては困る
・「volatile宣言がされている場合、コンパイラはそのデータが置かれたメモリに対する読み書きの順序を変更できない。しかし、これらの読み書きと、他のメモリに対する読み書きとの順序を変更してもよい」という規則

スレッドセーフな関数について
以前の質問では「安全」を定義する必要がありましたが、今回では「問題」を定義する必要がありますね。

・プログラム自体のバグが問題である場合、確かにvolatileが必要となる場面は意外に少ない・・・かと思います。
・プログラマが意図しない動作が問題である場合、volatileが必要になる場面は結構ある・・・と思います。

例えば、以前の質問・・・
64bit整数型が遅い理由
で、上げているプログラムなのですが・・・
これに最適化を施すと割り算がループの外に出てしまい(ループが不要なので消える)、この実験プログラムには意味がなくなってしまいます。
正常に動作はしますが、そういう意味においては、「問題」になるため、volatileが必要になってきます。

いやぁぁ・・・**む・ず・か・し・い・!**ですね。

これはまさにcatsforepawさんの仰る通り、

付けるかどうかは「設計上の必要性」に応じて決めます。

しかし、実際にどうやってvolatileを見つけるかなのですが・・・
最適化によって変更される規則は、おそらくコンパイラのオプティマイザーの開発者レベルじゃないと完璧に把握するのは無理なんでしょう。

yohhoyさんの仰る通りかもしれません。

最終的にはプログラマ自身が正しい知識を身につけることと、レビュープロセスなどで人的にチェックするしかないと思います。

よく出てくるパターンのみを把握しておけばある程度は「ここでvolatileが必要だな・・・」という感覚が身につくかも・・・しれないです。

投稿2018/02/19 14:38

strike1217

総合スコア651

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/02/19 15:03 編集

よく出てくると言ってもここでは、最適化による変更のパターンを4つしか上げていませんね。 むぅぅぅ・・ 手強いですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問