2017-01-28 15 views
0

私は、マイクロコントローラの "forループ"の実行時間を測定する実験を行っていました。この '' forループ ''は、整数とポインタ操作を含んでいます。ループのためにコンパイラ最適化

ケース1:コンパイラoptimizaionフラグを「なし」(最適化なし)に設定すると、アセンブリコードが生成され、実行時間を測定できます。

ケース2:コンパイラ最適化を「速度」(速度に最適化)に設定すると、 このループのために生成されたアセンブリコードはありません。コンパイラが、私はスピードにコンパイラフラグを設定し、このループを、捨てる理由を誰が説明してもらえ

/* the basic concept behind this code is data manipulation in an array.Therefore I created an array then with the help of loops, tried to manipulate data*/ 

    int abc[1000]; 
    for(n=0; n<1000; n++) 
      { 
       abc[n]= 0xaa;       
      } 
    for(n=2; n<1000; n=n+2) 
      { 
       abc[n]= 0xbb;       
      } 
    for(n=5; n<1000; n=n+2) 
      { 
    for(i=(n+n); i<1000; i++) 
       { 
        abc[i]= i;       
       } 
      } 

「「ループのために」」コンパイラはこれを捨てる、のように思えます。

+0

'for'ループとその中で呼び出される関数の内容を表示できますか? –

+0

参考のためにサンプルコードを追加しました。 – user7439633

+0

コードを適切に書式化して読みやすくしてください。 –

答えて

1

その後、abcを使用しないと、オプティマイザがそれを認識して(それにすべての書き込みを)「死んだ」とみなし、完全に削除した可能性があります。

1

コンパイラはコードを調べ、abcが設定され、使用されていないことを確認します。コンパイラの中にはこれに関する警告を出すものがあります。 abcは決して使用されないので、コンパイラはそれを最適化します。なぜなら、変数を使用しないと変数を設定する点です。

abcを揮発性にすることはできますが、それはおそらくテストの目的を破るでしょう。変数をvolatileにすると、コンパイラはその使用について何も仮定することができません。変数をvolatileにすると、コンパイラは最適化を行うことができない可能性があるため、タイミングは最適化の有無にかかわらず同じになります。

0

ここにいくつかの完全な例がありますが、あなたの質問に正確に答えることができない点が不完全です。

unsigned int fun (void) 
{ 
    unsigned int x[8]; 
    unsigned int ra; 
    unsigned int rb; 

    for(ra=0;ra<5;ra++) x[ra]=ra; 
    rb=0; for(ra=0;ra<5;ra++) rb+=x[ra]; 
    return(rb); 
} 

void more_fun (void) 
{ 
    unsigned int x[8]; 
    unsigned int ra; 

    for(ra=0;ra<5;ra++) x[ra]=ra; 
} 

と最適化されたコンパイル済みの出力第二の機能最初

00000000 <fun>: 
    0: e3a0000a mov r0, #10 
    4: e12fff1e bx lr 

00000008 <more_fun>: 
    8: e12fff1e bx lr 

の例は、RAもXのどちらも関数の外で使用されていることを確認するために、彼らが生産されているのは何も非常に簡単ではありません本当の価値のあるものは、それは未使用の変数とデッドコードなので、それはすべて消えると、全機能がこれに最適化されています。それがあるべきよう

void more_fun (void) 
{ 
} 

これをさらに踏襲すると、ランダマイザや他のアルゴリズムを使用してこのようなことをさらに進めています。この場合はかなり簡単でした。

fun()関数内の回転のどれもランタイム値を持たず、すべてがデッドコードであり、結果は入力やグローバルなものに基づいて変化しません。答えを事前に計算することができます。コンパイラはコンパイル時(0 + 1 + 2 + 3 + 4 = 10)に答えを計算し、すべてのデッドコードを削除します。あなたがなど、ループを使用して時刻を燃やすか、おそらくループが実装されている方法を見たいと思っている場合は基本的に正しい答え

unsigned int fun (void) 
{ 
    return(10); 
} 

を考え出す、あなたは物事のカップルを試すことができます。

あなたが見ることができるように揮発性の解決策は、私はあなたが実際にあなたが使用するたびに出かけ、タッチラムしたいと言っているかなり醜いです(コンパイラは異なります)

00000000 <fun>: 
    0: e3a03000 mov r3, #0 
    4: e24dd008 sub sp, sp, #8 
    8: e58d3004 str r3, [sp, #4] 
    c: e59d3004 ldr r3, [sp, #4] 
    10: e58d3004 str r3, [sp, #4] 
    14: e59d3004 ldr r3, [sp, #4] 
    18: e2833001 add r3, r3, #1 
    1c: e58d3004 str r3, [sp, #4] 
    20: e59d3004 ldr r3, [sp, #4] 
    24: e2833002 add r3, r3, #2 
    28: e58d3004 str r3, [sp, #4] 
    2c: e59d3004 ldr r3, [sp, #4] 
    30: e2833003 add r3, r3, #3 
    34: e58d3004 str r3, [sp, #4] 
    38: e59d3004 ldr r3, [sp, #4] 
    3c: e2833004 add r3, r3, #4 
    40: e58d3004 str r3, [sp, #4] 
    44: e59d0004 ldr r0, [sp, #4] 
    48: e28dd008 add sp, sp, #8 
    4c: e12fff1e bx lr 

00000050 <more_fun>: 
    50: e92d4010 push {r4, lr} 
    54: e3a04000 mov r4, #0 
    58: e1a00004 mov r0, r4 
    5c: e2844001 add r4, r4, #1 
    60: ebfffffe bl 0 <dummy> 
    64: e3540005 cmp r4, #5 
    68: 1afffffa bne 58 <more_fun+0x8> 
    6c: e8bd4010 pop {r4, lr} 
    70: e12fff1e bx lr 

のようなものを与えることができます

void dummy (unsigned int); 
unsigned int fun (void) 
{ 
    unsigned int ra; 
    volatile unsigned int rb; 

    rb=0; for(ra=0;ra<5;ra++) rb+=ra; 
    return(rb); 
} 

void more_fun (void) 
{ 
    unsigned int ra; 
    for(ra=0;ra<5;ra++) dummy(ra); 
} 

この変数は、何かをしたいときにRAMから読み込み、各ステップの後に書き戻します。 more_fun()ソリューションは、コンパイラが外部関数を呼び出すように強制した場合、コンパイラが期待しているようにvolatileを期待することには依存しません(コンパイラはローカル、これは最適化ドメインにはなく、結果としてインライン化することはできません。たとえば、dummy()が入力変数を使用しない場合は、デッドコードが表示される可能性があります。そのような小さなループであるため、コンパイラはループを実装することができます。または、アンロールすることができます。アンロールすると、それが意味をなさない場合にはループとして実装されるか、一連の呼び出しとして実装されます。

void more_fun (void) 
{ 
    dummy(0); 
    dummy(1); 
    dummy(2); 
    dummy(3); 
    dummy(4); 
} 

gnuのような無料のツールでは、最強のコンパイラではありませんが、最もタイトな/最速のコード(1つのサイズは、誰にも当てはまりません)まで、オブジェクトやバイナリにコンパイルしますオブジェクトとバイナリの両方を逆アセンブルするディスアセンブラを持っているので、これらのような単純な関数で遊ぶことができ、コンパイルオプションが何をしているのかを調べ、デッドコードが何であるかを理解することができます。あなたは時間以外何も費用はかかりません。

ほとんどの人は、揮発性のソリューションを手に入れようとしていますが、手作業の最適化を試みてゆっくりとビルドすると、結果が不安定になり、あなたがそれを持っていなかった場合よりも不自然な方法で、最終的には、変数の1つに揮発性の有無にかかわらずパフォーマンスが大きく変化するループ内の他の関数を呼び出す実際のコードを思いつくことができます。

同じコンパイラを使用して同じ正確なコンピュータで同じ正確なソースコードを使用しても、単語のベンチマークは、とにかく偽です。重要なのは、実際のタスクを実行する実際のプログラムで、このパフォーマンスがあります。それを別の方法で実装し、可能であればそれを測定し、いくらかのマージンを追加し、どちらがより速く、そしてそれと一緒に行くか、あるいは同じことが読んだり、維持したりする方が簡単なのかを判断します。