Top Page [◀◀]  2   3   4   5   6   7   8   9   ... [▶▶Last Page

ループの最適化

いつもお世話になっております。

以下のようなプログラムで、loop1()はwhileを抜けられず復帰しないのですが、 loop2()は、正しく復帰します。

loop1と、loop2の違いは、Delay()を呼ぶか、呼ばないかの違いしか有りません。

タイマー割り込みによりTimerは、0になります。 (Flagは0のままとします。)

多分?ループ最適化による影響ではないかと思うのですが、コンパイルオプションを替えても状況は変わりません。

どの様なオプションが影響するか お教え願えないでしょうか?

 

int Flag ;
int Timer;

void Delay( void )
  {
    asm("NOP");
  }

void Interrupt_1ms( void )
  {
    if ( Timer > 0 ) Timer--;
  }

int loop1( void )
  {
    Flag = 0;
    Timer = 100;
    while(1)
      {
        if ( Flag ) break;
        if ( Timer == 0 )
         {
           return( 1 );
         }
     }
    return( 0 );
 }

int loop2( void )
  {
    Flag = 0;
    Timer = 100;
    while(1)
      {
        Delay(); // 重要
        if ( Flag ) break;
        if ( Timer == 0 )
         {
           return( 1 );
        }
     }
    return( 0 );
  }

  • にもち さん、こんにちは。NoMaYです。お久しぶりです。

    コンパイラは何をお使いですか? RZに関して質問されていることが多いようですので、GNUARMかIAR ARM C/C++コンパイラでしょうか?

    [追記]

    あと、割り込みルーチンで更新される変数Timerにvolatileが付いていませんが、今回の質問用に付けなかったものですか? それとも、普段から付けないでいたりしますか?

  • In reply to NoMaY:

    NoMaY様 reply有難うございます。

    e2studio(GNUARM)を使用しております。(CPUは、RZ-A1l:R7S721020VCFP)

    SH4で動いていたソースをARMに移植したのですが、各所に細工が必要で手を焼いています。
    (SH4のコンパイラは、GNUとは別物です。)
  • In reply to にもち:

    にもち さん、こんにちは。NoMaYです。

    私の追記が行き違ったかな、という予感がするので自己レスです。これは移植元のSH4用のソースに付いていなかったから、かな、、、という気がしてきました、、、

    > [追記]
    > あと、割り込みルーチンで更新される変数Timerにvolatileが付いていませんが、今回の質問用に付けなかったものですか? それとも、普段から付けないでいたりしますか?

  • In reply to NoMaY:

    NoMaY様 有難うございました。

    volatileが抜けていました。正常に動作するようになりました。

    私は、
    普段SH4,SH2等のソフトを作っているのですが、自前のコンパイラを使用しているためvolatileと言う概念が有りませんでした。
    一昨年よりARMを手がけるようになりGNUを使用するようになったのですが、volatile未指定によると思われる現象を思い出します。
    それらも、Delay等を挟む事で解決しましたがこれが原因だったのでしょう。

    本当に有難うございました。
  • In reply to にもち:

    わわいです
    コンパイラは、volatileがついていない変数は、外部から変更されることがない、という前提で最適化を行います。だもんで、提示のコードではFlagが0という前提で最適化を行い、ループの途中で無駄なFlag変数の参照を省略して、高速なコードを生成します
    Delay関数を挟んだら動く、というのは、単にそのコンパイラの最適化が足りない結果そうなったということができます。他のコンパイラだとまた結果は変わるでしょうね。

    #まあ、最適化が足りない、というより、組み込み向けコンパイラなのでそういう配慮があった、のかもしれません
  • In reply to わわい:

    わわい様。 いつも有難うございます。

    変数Timerは、別ソース上にあるタイマー割り込みで更新しておりloop1/2のソース上には外部参照(volatile無し)で行っています。
    従って、loop1()では外部コールも発生しないためTime、Flagrの更新は無いとし、 loop2()では、外部コール(Delay)で制御が切れるためTimerや、Flagの更新を考慮したコードが出たのですね。

    e2studio(gnu)でvolatileを指定しないでも、volatile属性を有効にするオプションはあるでしょうか?
    それとも、最適化レベル(none)で行うのでしょうか? .....これは、試してみます。

    いろいろと有難うございました。
  • In reply to にもち:

    GCCでは変数のオプションとしては以下のものがありますね
    -fvolatile
    -fvolatile-global
    -fvolatile-static
    詳細は検索してみてください
  • In reply to わわい:

    > GCCでは変数のオプションとしては以下のものがありますね

    大昔に廃止になってるので今使える状況は稀でしょう。

    https://www.gnu.org/software/gcc/gcc-3.4/changes.html
    > GCC no longer accepts the options -fvolatile, -fvolatile-global and -fvolatile-static. It is unlikely that they worked correctly in any 3.x release.

  • > 多分?ループ最適化による影響ではないかと思うのですが、

    Cコンパイラは「ひとつの関数の中では volatile 指定されていないオブジェクトの内容は勝手に変化しない」前提でコード生成するので loop1() の中の処理では Flag は 0、Timer は 100 に初期化して以降、その状態が続く(ループ処理が終わらない)コードが生成されると思います。https://godbolt.org/z/X3NRVB

    対して loop2() では、ループ処理の中で呼んでる関数 Delay() の中でインラインアセンブラが使用されており、コンパイラはインラインアセンブラの処理内容を理解しないため、そのわからない処理の中で Flag や Timer の内容が書き換えられる可能性を考慮してループ中に Flag や Timer のアクセスの度に内容をメモリから読み出します。https://godbolt.org/z/6-VZlk
    loop2() が復帰する理由はそれでしょう。
    Delay() の中のインラインアセンブラを無効化すれば Delay() の処理で Flag や Timer の内容が書き変わらないことをコンパイラは理解するのでループ処理は終わらなくなるかもしれません。https://godbolt.org/z/PIVpif
    同様に、Delay() を外部関数化して処理の内容をコンパイラに悟られないようすれば Loop2() は復帰するコードになるかもしれません。https://godbolt.org/z/5y2Mwb

    > コンパイルオプションを替えても状況は変わりません。

    大昔の gcc では volatile 指定されていないオブジェクトを volatile 指定されているよう扱うコンパイルオプションが存在しましたが既に廃止されています。最適化レベル0を指定(`-O0')すれば一見うまくいくようなコードは生成されるかもしれませんが https://godbolt.org/z/TMilv8 「ひとつの関数の中では volatile 指定されていないオブジェクトの内容は勝手に変化しない」前提は変わらないので期待したコードが生成されるかは何の保証もありません。割り込み処理で変化をさせるオブジェクトについては素直に volatile 指定をする以外方法はないでしょう。

  • In reply to fujita nozomu:

    fujita nozomu様

    詳細な解説有難うございました。
    上位のコンピュータ(昔の表現?)用のソフトでは意識しなかったり、以前使用していたコンパイラがループの最適化を行わない仕様であったりしたため 今回の不具合をvolatile未指定によるものと考えませんでした。
    今後は、使用するコンパイラが一般的なものであると意識して使用したいと思います

    ありがとうございました。
  • にもちさん、
    かふぇルネ管理人です。
    初心者向けフォーラムにふさわしい、楽しい話題で盛り上がってますね。
    職人は道具を使いこなしてなんぼの世界ですので、師匠さんたちのノウハウを大いに吸収してください。
    ご師匠さんたち、みなさん優しいですね。

Top Page [◀◀]  2   3   4   5   6   7   8   9   ... [▶▶Last Page