2012-03-09 21 views
4

私はこの事故に気づき、広範囲にテストすることにしました。なぜconst intはconst intより高速ですか?

#define Type int 
#define Prm const Type & 
Type testfunc1(Prm v1, Prm v2, Prm v3, Prm v4, Prm v5, Prm v6, Prm v7, Prm v8, Prm v9, Prm v10){ 
    return (v1|v2|v3|v4|v5|v6|v7|v8|v9|v10); 
} 

億回:私は関数を呼び出すとき

ので、種類intconst intconst int &

 for(Type y = 0; y < 10000; y++){ 
      for(Type x = 0; x < 10000; x++){ 
       out |= testfunc1(x,y,x,x,y,y,x,y,x,y); 
      } 
     } 

は、私はconst intconst int &よりも高速であることに気づきます。 (注:戻り値を使用して関数が最適化されないようにする)

なぜそうですか?私はいつも&を追加すると実際には速くなると思っていましたが、テストではそれとは逆のことが言えます。より大きなデータ型の場合は、結果が異なる可能性があることを知っています。結果についてはかなり確信しているので、テストしませんでした。

私のテスト:

const int: 7.95s 
const int &: 10.2s 

編集:私はそれが原因で私のアーキテクチャで確かだと思います。私はSint64タイプでテストし、その結果は以下の通りであった。

const Sint64: 17.5s 
const Sint64 &: 16.2s 

EDIT2:それともそれはありますか? doubleタイプ(?64ビットである)でテストされ、結果は私が困惑します:

const double: 11.28s 
const double &: 12.34s 

EDIT3:64ビットタイプで、私の最新のテストを一致させるためにループコードを更新しました。

+2

戻り値を使用しても、最適化されないことはありません。つまり、コンパイル時に計算全体を行うことができるため、コンパイラはすべてを最適化してループを単に「0x3FFF」に置き換えることができます。 –

+0

私はこれに対する答えに興味があるでしょう。 const intは、const int&よりも関数プロローグコード(コンパイラによって入れられる)によって異なって扱われるかもしれません。私は教育的な推測をしています。 – octopusgrabbus

+0

@ R.MartinhoFernandes、もしそれが最適化されていれば、7.95秒間それを実行しません;)私のコンパイラはそれほどスマートではないことは言うまでもありません(パラメータの定数を与えた場合にのみ最適化できました) 。 – Rookie

答えて

9

引数に&を入れることで、プログラムにさらにコードを追加します。&がなければ、シーケンスは次のとおりです。

push values 
call Function 
pop values <- usually an update to stack pointer 

と機能で:

push address of value1 
push address of value2 
etc 
call Function 
pop values <- usually an update to stack pointer 

と機能で:

return_value = 0; 
address = sp[arg1] 
or return_value, [address] 
address = sp[arg2] 
or return_value, [address] 
etc 
return return_value 

return sp[arg1] | sp[arg2] | etc <- value read direct from stack. 

は '&' はこれを行い追加します

ご覧のとおり、&が多く追加されています。それでなぜそれを使うのですか?非常に大きなオブジェクトがある場合は、オブジェクトをスタックにコピーするよりも、ポインタを渡すほうが最適です。

+3

また、上記は一般的な記述であると付け加えておきます。オプティマイザは、スタックではなくレジスタに値を入れることができます。 IA64コンパイラは引数をレジスタに入れることもできます。 – Skizz

+0

には、レジスタに言及するために1が加算されます。 –

7

この結果は、システムによって大きく異なります。あなたの特定のシステムでは、参照の値(ポインタとして実装されている可能性が高い)の値をコピーするのは、整数の値をコピーするよりもコストが高いことを示します。その違いの最も一般的な理由は、整数には32ビットの表現が必要で、ポインタ/参照表現には64ビットが必要です。 EDITあなたの整数をにアクセスするコストはもちろんですが、値を取得するには間接的な追加が必要です。 2つのアイテムしか渡していないので、キャッシュを使用するとその追加コストは大幅に隠されますが、コストはそこにあります。

しかし、大文字のstructvector<...>などの参照を渡すには、構造体のアイテム数に関係なく、64ビット(またはそのサイズはシステム上のもの)でなければなりません。あなたのvector<...>が保持するアイテムがいくつありますか?構造が大きくなればなるほど、それを値で渡すためのコストが高くなります。したがって、それを参照することで実現する節約額です。

+3

ポインタが32ビットしか必要ない場合でも、実際のint値にアクセスするには1つの間接指定が必要です。 –

+2

'int'をコピーするのがアドレスをコピーするよりも高価であったとしても、リファレンスバージョンは間接メモリフェッチ操作も含みます。これは、intをコピーするコストと同等である可能性があります。 –

+1

@ R.MartinhoFernandesあなたは正しいです、間接費があります。 OPでのテストは、10ではなく2つの変数のアドレスを渡すので、そのコストをいくらか隠すように構成されています。したがって、8つの間接アクセスがキャッシュによって処理されますが、そこ。 – dasblinkenlight

1

値の代わりにアドレスを渡すと、アドレスがエスケープされます(エスケープ解析やお気に入りのコンパイラの教科書のポイントツー分析を参照)。

はい、インライン化やリンク時の最適化などは、これらの問題を軽減できます。

関連する問題