76

I want to write a C code firmware for Atmel AVR microcontrollers. I will compile it using GCC. Also, I want to enable compiler optimizations (-Os or -O2), as I see no reason to not enable them, and they will probably generate a better assembly way faster than writing assembly manually.

But I want a small piece of code not optimized. I want to delay the execution of a function by some time, and thus I wanted to write a do-nothing loop just to waste some time. No need to be precise, just wait some time.

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

Since memory access in AVR is a lot slower, I want i and j to be kept in CPU registers.


Update: I just found util/delay.h and util/delay_basic.h from AVR Libc. Although most times it might be a better idea to use those functions, this question remains valid and interesting.


Related questions:

4

9 に答える 9

89

dmckee's answerからのリンクをたどってこの回答を作成しましたが、彼/彼女の回答とは異なるアプローチをとっています。

GCCの言及からの関数属性のドキュメント:

noinline この関数属性は、関数がインライン化の対象になることを防ぎます。関数に副作用がない場合は、インライン化以外の最適化により関数呼び出しが最適化されますが、関数呼び出しはライブです。そのような呼び出しが最適化されないようにするには、asm ("");

これは私に興味深いアイデアを与えました...nop内側のループに命令を追加する代わりに、次のように空のアセンブリコードをそこに追加しようとしました:

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

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

さらに、 を使用するvolatileと、gcc はこれらの変数を RAM に格納し、それらを一時レジスタにコピーするためにldd大量に追加します。std一方、このアプローチは、そのvolatileようなオーバーヘッドを使用せず、生成しません。


更新:-ansiまたはを使用してコードをコンパイルする場合は、GCC ドキュメント で説明されているように、キーワードをに-std置き換える必要があります。asm__asm__

さらに、アセンブリ ステートメントを指定した場所で実行する必要がある場合 (つまり、最適化のためにループの外に移動してはならない) を__asm__ __volatile__("")使用することもできます。

于 2011-08-16T19:55:34.360 に答える
29

ij変数を として宣言しますvolatile。これにより、コンパイラはこれらの変数を含むコードを最適化できなくなります。

unsigned volatile char i, j;
于 2011-08-16T19:30:00.560 に答える
7

このアプローチが完全に見当違いであり、コンパイラのアップグレードなどによって簡単に壊れてしまうことがまだ言及されていない理由はわかりません。待機する時間の値を決定し、現在の目標値を超えるまでの時間。rdtscx86 ではこの目的に使用できますが、より移植性の高い方法はclock_gettime、(または非 POSIX OS のバリアント) を呼び出して時間を取得することです。現在の x86_64 Linux は、syscall を回避して内部的clock_gettimeに使用することさえあります。rdtscまたは、システムコールのコストを処理できる場合は、最初から使用clock_nanosleepしてください...

于 2011-09-01T20:31:06.933 に答える
3

コンパイラの avr バージョンがs の完全なセット#pragma(リンク内の興味深いものはすべて gcc バージョン 4.4 からのもの) をサポートしているかどうかは頭の中でわかりませんが、通常はそこから始めます。

于 2011-08-16T19:30:32.823 に答える
3

私にとって、GCC 4.7.0では、空のasmは-O3でとにかく最適化されていました(-O2では試しませんでした)。また、レジスタまたは揮発性で i++ を使用すると、パフォーマンスが大幅に低下します (私の場合)。

私がしたことは、「メインプログラム」をコンパイルするときにコンパイラが見ることができなかった別の空の関数とリンクすることでした

基本的にこれ:

この関数を宣言した状態で「helper.c」を作成 (空の関数)

void donotoptimize(){}

次にコンパイルgcc helper.c -c -o helper.o してから

while (...) { donotoptimize();}

を介してリンクしgcc my_benchmark.cc helper.oます。

これにより、最良の結果が得られました(私の考えでは、オーバーヘッドはまったくありませんが、プログラムはそれなしでは機能しないため、テストできません:))

私はそれがiccでも動作するはずだと思います。リンクの最適化を有効にしている場合はそうではないかもしれませんが、gcc では可能です。

于 2014-01-18T20:23:38.097 に答える
1

volatile asm を置くと役立ちます。詳細については、こちらをご覧ください:-

http://www.nongnu.org/avr-libc/user-manual/optimization.html

Windows で作業している場合は、以下で詳しく説明するように、プラグマの下にコードを配置することもできます。

https://www.securecoding.cert.org/confluence/display/cplusplus/MSC06-CPP.+Be+aware+of+compiler+optimization+when+dealing+with+sensitive+data

お役に立てれば。

于 2011-08-17T19:18:34.267 に答える
0

そのループを別の.cファイルに入れ、その1つのファイルを最適化しないでください。そのルーチンをアセンブラーで記述し、Cから呼び出すと、オプティマイザーが関与しなくなります。

私は時々揮発性のことをしますが、通常はその関数への呼び出しを返すasm関数を作成します。オプティマイザーはfor / whileループをタイトにしますが、ダミー関数へのすべての呼び出しを行わなければならないため、最適化されません。DenilsonSáからのnopの答えは同じことをしますが、さらにきついです...

于 2011-08-16T22:16:06.283 に答える
-1

registerキーワードを使用することもできます。レジスタで宣言された変数は、CPUレジスタに格納されます。

あなたの場合:

register unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}
于 2012-09-06T13:14:19.510 に答える