1. ホーム
  2. c

GCCがビジーな待機ループを最適化するのを防ぐにはどうしたらよいか?

2023-08-28 20:56:06

質問

Atmel AVRマイクロコントローラ用のCコードファームウェアを書きたいと思っています。GCCを使用してコンパイルするつもりです。また、私はコンパイラの最適化を有効にしたいと思います ( -Os または -O2 ) を有効にしない理由はありませんし、おそらく手動でアセンブリを記述するよりもずっと速く、より良いアセンブリを生成するでしょうから。

しかし、私は最適化されないコードの小さなピースが欲しいのです。私はある関数の実行をある時間だけ遅らせたいので、時間を無駄にするために何もしないループを書きたいと思いました。正確である必要はなく、ただ時間を待つだけです。

/* How to NOT optimize this, while optimizing other code? */
unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}

AVRのメモリアクセスはかなり遅いので、私は ij をCPUのレジスタに保持するようにします。


更新: 私はちょうど util/delay.h util/delay_basic.h です。 から AVR ライブラリ . ほとんどの場合、これらの関数を使用する方が良いアイデアかもしれませんが、この質問は有効で興味深いままです。


関連する質問

どのように解決するには?

私は、以下のリンクをたどって、この答えを開発しました。 dmckeeの回答 からのリンクをたどって作成したものですが、彼の回答とは異なるアプローチをとっています。

関数の属性 のドキュメントを GCC のメンションから引用しています。

noinline この関数属性は、関数がインライン化の対象として考慮されないようにします。 関数が副作用を持たない場合、インライン化以外の最適化により、関数呼び出しは生きているにもかかわらず、最適化されなくなることがあります。このような関数呼び出しが最適化されないようにするためには asm ("");

これは私に面白いアイデアを与えてくれました... を追加する代わりに nop 命令を追加する代わりに、次のように空のアセンブリコードを追加してみました。

unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i)
        asm("");
}

そして、うまくいきました。このループは最適化されておらず、また余分な nop 命令は挿入されていません。

さらに、もしあなたが volatile を使うと、gcc はこれらの変数を RAM に保存し、さらに lddstd を使って一時的なレジスタにコピーします。一方、この方法では volatile を使用せず、そのようなオーバーヘッドを発生させません。


更新しました。 を使用してコードをコンパイルしている場合 -ansi または -std の場合は asm キーワードを __asm__ のように GCC ドキュメントに記述されている .

さらに __asm__ __volatile__("") を使うこともできます。 アセンブリ文は置いた場所で実行されなければなりません (つまり、最適化のためにループの外に出してはいけません)。 .