<volatileの必要性1>

| | コメント(2)

 皆様のおかげさまを持ちまして、今年もD-CLUE社員旅行に行くことができました。
 今年も恒例の沖縄です。

20110530_1_tobe.jpg

 

 沖縄にきてしばらくして感じたことは、なんとなく自分がのんびりしているなと感じたことです。それは海があるからなのでしょうか?自然があるからなのでしょうか?昔は全然考えなかったのですが、ふと今回そんなことを感じました。
 「海岸線を走っているとき」、「山の中を走っているとき」。それらの全てを含めてここでは時間がゆっくりと流れている感じがしています。もちろん仕事で来ているわけではありませんので、なにかに急かさせることもないのですが、この明るい日差しと波の音がゆっくりとした時間の流れを感じさせてくれるんだなと思い、自分の心をリフレッシュさせることができました。
 また、沖縄と言えば「沖縄そば」が有名ですね。私は「ソーキそば」よりもこのもやしがたっぷりと乗った「牛肉そば」が好きです。

20110530_2_tobe.jpg

 

 

 

 

 

 

 

 

 

 

 

 

 しばらく食べ続けないと、その下にある「そば」に出会えません。
 これを全て食べ尽くしたときに、なんとも言えぬ達成感があります。。。

 

<volatileの必要性って1>

 さて、沖縄の時間はゆっくりと流れていますが、プログラムのコードは出来るだけ早く動いてもらいたいものです。そこで、皆さんはコンパイルをするときに最適化のオプションを付けたりすると思います。

 たとえば、以下のような処理を作ったとします。

20110530_3_tobe.png

 testFunc()という関数を考えますと、変数[initialFlag]を"1"に初期化した後、[initialwait()]という関数を呼び出します。
 [initialWait()]では[initialFlag]が"0"にクリアされるのを待っていて、"0"になったらwhile文を抜けて[testFunc()]に戻ってきます。
 自分で変数を"1"に初期化したのに、"0"を待っているなんて、一見あり得ないようなプログラムですが、以下のような場合が考えられます。

 

20110530_4_tobe.png 事前にデバイスドライバー等を初期化しておいて、外部からのイベント(電源ボタンの押下等)をひたすら待っている場合等がその例だと思います。
 さて、今回これらのプログラムがどう動くかを見てみます。と言うわけで、恒例のアセンブラです。
 

20110530_5_tobe.png

 上記アセンブラコードは、コンパイラが出力したListファイルを貼り付けたものです。
 ここで重要なのは、whileのLoopがどのように変換されているかです。
 コードの一番左側に行ラベルがありますので、そちらを基準に説明しますと、
   65c-660 :r3レジスタにinitialFlagのアドレスをLoadしています。
   664     :r3レジスタにinitialFlagの中身をLoadします。(最初は"1")
   666     :r3レジスタの値を"0"と比較します。
   668     :違っていたら65c番地に移動します。

 

ここで見る限り、特に問題はありませんね。
さて、上記Listは最適化オプションを[-O0]を使ってコンパイルしたものです。
実際にデバッグが完了し、製品上で最終試験をする場合は[-O2]等のオプションを付けるか、Toolによっては[Release]側を選択して最終コンパイルをすると思われます。
そこで、[-O2]を付けてビルドしたコードを見てみることにします。
 

20110530_6_tobe.png    490-494 :r3レジスタにinitialFlagのアドレスをLoadしています。
   498     :r3レジスタにinitialFlagの中身をLoadします。(最初は"1")
   49a     :r3レジスタの値を"0"と比較します。
   49c     :違っていたら49c番地に移動します。

 

ここで、問題となるのは49c番地の動作です。
先ほどのソースと大きく違うのは、49c番地の動作では、再度[initialFlag]の中身をLoadせずに、CPU内部の演算結果だけをみていることです。
従って、この場合は、割り込みハンドラ側でイベントを受け付けたことにより"0"を設定しても、while()のLoopは抜けなくなってしまいます。
「デバッグしている時は、動いていたのに、最終試験をすると動かない。。」ということになります。

さて、どうするか。。。そこで[volatile]宣言の活躍です。
先ほどのプログラムから変数の宣言だけを変更します。

 20110530_7_tobe.png

そこで、再度アセンブラコードをみますと。

20110530_8_tobe.png

     490-494 :r2レジスタにinitialFlagのアドレスをLoadしています。
   498     :r3レジスタにinitialFlagの中身をLoadします。(最初は"1")
   49a     :r3レジスタの値を"0"と比較します。
   49c     :違っていたら498番地に移動します。

 

今度の場合は、[498番地]の比較結果がNGの場合は [498番地]に戻っています。
[498番地]では再度r2(initialFlag)の中身をLoadしますので、もしも誰かが変更している場合は、新しく"0"という値が取得出来ることになります。

割り込み処理等が無くシングルタスクの場合、あるいは最適化オプションが無い場合は問題ないかもしれませんが、そのようなプログラムはあまり一般的ではないと思いますので、改めてきちんと付けることにしましょう。

 

 さて、心がリフレッシュできたところで、体はと言いますと。
 今年もD-CLUE社員旅行恒例の、「大イベント大会」を行いました。
 海岸で体をつかったイベントや、ゲームをやって盛り上がるのですが、今年は急遽「大水泳大会」も追加されました。
 急遽はじまった水泳大会ですが、たまにプールに行っていることもあり若干余裕かと思ったのですが、急に泳いだら足がつってしまい。。σ(^◇^;)
 ちょっと40歳を超えてなにか、今までのように行かない自分に。。。そんなことも気がつかせてくれるすてきな沖縄でした。(~ヘ~)ウーン。。改めて準備(運動)が必要です。
 最後に、沖縄の注意点ですが。。
 遊びすぎに注意しましょう。。
 あまり遅くに空港に行きますと。

20110530_9_tobe.jpg

 

 

 

 

 

 

 

 

 

 

 

 

 「ぐぶり~さび~しが・・・・、ちゅうやうわいやいび~ん」と。。
 お腹が空いても、何も食べれません(笑)

コメント(2)

anonymouse :

そのvolatileの使い方は、規格上はマズイです。
volatileは揮発修飾子。つまり外部入出力を主目的とした修飾子です。
volatileは、I/Oのために演算の順番保証はします。
ですが、volatile変数自体がメモリー上に配置されていることは
一切保証されません。また、規格上はvolatile修飾子を
つけた変数は、1つ前の代入結果が保持されることを
保証しません。1度代入した後、次の読み出しで値が
変わっていても構わないことになっています。これに従えば、
volatile変数自体の実体はどのようになっていても構わないため、
1つの関数を実行している間中volatile変数の実体を常に
レジスタに割り当てても良いことになります。

呼び出し元に戻ってくる割り込みはともかく、このレジスタ割り当てが
もしマルチコア、マルチスレッド環境で行われた場合、スレッドごとに
変数値が異なることになります。これにより、ループの脱出指示は
正しく記述しているにも関わらず、いつまで立っても別スレッドの
ループを抜けられないという事が発生します。

まぁ、あくまで現実は実装依存ですが。

参考元:
http://www.open-std.org/jtc1/sc22/WG14/www/docs/C99RationaleV5.10.pdf

dclueblog :

コメントありがとうございます。

今回、シングルコアでの動作+関数の外部に宣言することでグロー
バル化を図らせていただいております。

その条件において、volatileの宣言と処理の最適化ということにつ
いての記載をさせて頂きました。

コンパイラでの出力結果においても、特に問題なく動作していまし
たので今回記事として記載させて頂きました。

開発の際は、コンパイラーに頼るところもございます。
正規の規格および、コンパイラーの実装状態等を理解し利用すること
は大事ですね。

あらためて、ありがとうございました。

コメントする

このブログ記事について

このページは、dclueblogが2011年5月30日 13:36に書いたブログ記事です。

ひとつ前のブログ記事は「<WindowsMobile2>」です。

次のブログ記事は「<volatileの必要性2>」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Powered by Movable Type 4.01