2016-09-24 7 views
-2

ケースI性能比較は、

while (!channels_.empty()) 
      { 
       for (auto it = channels_.begin(); it != channels_.end(); ++it) 
       { 
        time_t stop_time; 
        if (it->second->active_playlist()->status_stop_time(it->second->on_air_row(), stop_time)) 
        { 

        } 
       } 
      } 

ケースII

while (!channels_.empty()) 
     { 
      time_t stop_time; 
      for (auto it = channels_.begin(); it != channels_.end(); ++it) 
      { 

       if (it->second->active_playlist()->status_stop_time(it->second->on_air_row(), stop_time)) 
       { 

       } 
      } 
     } 

可変STOP_TIMEは、ケースIおよびIIでネストされたループの外又は内で宣言されています。どちらがパフォーマンス面で優れていますか?どうして?

+2

あなたはそれをタイムアウトして見つけませんか?私はあなたがどんな違いに気付くか疑問に思う。 – smac89

+1

私はそれらがまったく同じコードにコンパイルされると確信している。 – melpomene

+0

ケースIIの方が速いですか?変数は一度だけ初期化されないからです。 – seccpur

答えて

2

標準についてはパフォーマンスについてほとんど言及していませんが、明確なルールが1つあります。その最適化では、観察可能な副作用を変更してはいけません。

標準では、スタックまたはヒープの使用法は記述されていないため、使用前の任意の時点でスタック上の変数の領域をコンパイラが割り当てることは完全に正当です。

ただし、最適値はさまざまな要因によって異なります。最も一般的なアーキテクチャでは、スタックポインタの調整を入力と終了の2つの場所で行うのが最も理にかなっています。 x86では、スタックポインタを8ではなく640で変更するのにコストの差はありません。

また、コンパイラが値が変更されないことが確認できれば、オプティマイザはループも。

実際には、x86およびアームベースのプラットフォームのメインストリームコンパイラ(gcc、clang、msvc)は、スタック割り当てを1つのアップ/ダウンに集約し、十分なオプティマイザ設定/引数を与えられたループインバリアントをホイストします。

疑わしい場合は、アセンブリまたはベンチマークを調べます。

我々は非常に迅速にgodboltでこれを証明することができます:GCC 5で

#include <vector> 

struct Channel 
{ 
    void test(int&); 
}; 

std::vector<Channel> channels; 

void test1() 
{ 
    while (!channels.empty()) 
    { 
    for (auto&& channel : channels) 
    { 
     int stop_time; 
     channel.test(stop_time); 
    } 
    } 
} 

void test2() 
{ 
    while (!channels.empty()) 
    { 
    int stop_time; 
    for (auto&& channel : channels) 
    { 
     channel.test(stop_time); 
    } 
    } 
} 

void test3() 
{ 
    int stop_time; 
    while (!channels.empty()) 
    { 
    for (auto&& channel : channels) 
    { 
     channel.test(stop_time); 
    } 
    } 
} 

。1と-O3 this generates three identical pieces of assembly

test1(): 
      pushq %rbp 
      pushq %rbx 
      subq $24, %rsp 
    .L8: 
      movq channels+8(%rip), %rbp 
      movq channels(%rip), %rbx 
      cmpq %rbp, %rbx 
      je  .L10 
    .L7: 
      leaq 12(%rsp), %rsi 
      movq %rbx, %rdi 
      addq $1, %rbx 
      call Channel::test(int&) 
      cmpq %rbx, %rbp 
      jne  .L7 
      jmp  .L8 
    .L10: 
      addq $24, %rsp 
      popq %rbx 
      popq %rbp 
      ret 

    test2(): 
      pushq %rbp 
      pushq %rbx 
      subq $24, %rsp 
    .L22: 
      movq channels+8(%rip), %rbp 
      movq channels(%rip), %rbx 
      cmpq %rbp, %rbx 
      je  .L20 
    .L14: 
      leaq 12(%rsp), %rsi 
      movq %rbx, %rdi 
      addq $1, %rbx 
      call Channel::test(int&) 
      cmpq %rbx, %rbp 
      jne  .L14 
      jmp  .L22 
    .L20: 
      addq $24, %rsp 
      popq %rbx 
      popq %rbp 
      ret 

    test3(): 
      pushq %rbp 
      pushq %rbx 
      subq $24, %rsp 
    .L26: 
      movq channels+8(%rip), %rbp 
      movq channels(%rip), %rbx 
      cmpq %rbp, %rbx 
      je  .L28 
    .L25: 
      leaq 12(%rsp), %rsi 
      movq %rbx, %rdi 
      addq $1, %rbx 
      call Channel::test(int&) 
      cmpq %rbx, %rbp 
      jne  .L25 
      jmp  .L26 
    .L28: 
      addq $24, %rsp 
      popq %rbx 
      popq %rbp 
      ret 
-5

ループ外で変数を宣言することは、常により高速です。どうして?単にメモリが何度も代わりに1回割り当てられるからです。

これらの例では同じことはしません。最初の1つでは、各反復ごとに新しい変数を取得します。 2番目の変数では常に同じ変数を使用していますので、反復によっていくつかのデータを渡すことができます。

編集:コメントには、両方のコードが同じにコンパイルされます。私は同意できません。私はそのコードを持っているとしましょう:

int a = 0; 
for (int i=0; i<10; i++) 
{ 
    a++; 
    //do something with a 
} 

for (int i=0; i<10; i++) 
{ 
    int a = 0; 
    a++; 
    //do something with a 
} 

私はそれらが同じにコンパイルされないと思います。さらに、コンストラクタでいくつかの重要なことを行うことができる単純な変数ではなく、何らかのクラスオブジェクトにすることができます。おそらくいくつかの例では、コードは最適化されますが、必ずしもそうではありません。

+1

私が知っているすべてのコンパイラは、関数入力時にすべてのローカル変数を一度に割り当てます。変数が宣言されている場所は関係ありません。 – melpomene

+0

私の経験から言えば、このような小さなコードの違いは重要ではありません。メモリ割り当てには0時間かかる。 – anatolyg

+1

コンストラクタの呼び出しはメモリ割り当てとは無関係です。また、問題の変数は 'time_t'型です。これはクラスではありません。 – melpomene

0

多くのパフォーマンス関連の質問の一般的な答えは、「測定して自分で確認する」ことです。それが簡単なら、それをやりなさい。この場合、が簡単です。

場合によっては、アセンブリコードを調べることをお勧めします。コードが2つのケースで同じであれば、パフォーマンスを測定する必要はありません。

+0

'@anatolyg':私は考えていない "look at assembly"コメントありがとうございます。タイマーで測定するのは明らかに優れていますが、時間差はここでは非常に小さくなります。私はPCクロックの解像度が来ているのを見てきました。 – seccpur

+0

@seccputタイマーで測定するのは明らかに優れていません。もしアセンブリコードが同じであれば、測定するものはありません。 – EJP