ループ内で変数を宣言する際にオーバーヘッドはありますか?(C++) [重複]
-
13-09-2019 - |
質問
この質問にはすでに答えがあります:
- 変数をループ前とループ内で宣言する場合の違いは? 25 件の回答
次のようなことをした場合、速度や効率が失われるかどうか疑問に思っています。
int i = 0;
while(i < 100)
{
int var = 4;
i++;
}
宣言するもの int var
百回。あるような気がしますが、わかりません。代わりにこれを実行する方が実用的/高速ですか:
int i = 0;
int var;
while(i < 100)
{
var = 4;
i++;
}
それとも速度や効率の点で同じなのでしょうか?
解決
ローカル変数のスタック空間は通常、関数スコープに割り当てられています。そうないスタック・ポインタの調整はわずかvar
に4を割り当て、ループ内で起こりません。したがって、これらの二つのスニペットが同じオーバーヘッドを持っています。
他のヒント
は、プリミティブ型とPOD型の場合、それは違いはありません。コンパイラは、関数の先頭で変数のスタック領域を割り当てる機能は両方のケースで返すときにそれを解放します。
非自明なコンストラクタを有する非PODクラスタイプの場合、それが違いを生むだろう - その場合には、ループの外変数を置くだけ置くのに対し、各反復コンストラクタとデストラクタ一度、代入演算子を呼び出しますそれは、ループ内のループの各反復のコンストラクタとデストラクタを呼び出します。クラスのコンストラクタ、デストラクタ、代入演算子は何をすべきかに応じて、これはしてもしなくてもよいことが望ましいことがある。
これらは両方とも同じであり、ここではあなたが(でも高く設定最適化なし)コンパイラが何をするかを見ることで、見つけることができます方法は次のとおりです。
コンパイラ(GCC 4.0)は、あなたの簡単な例に何をするかを見てください
1.Cます:
main(){ int var; while(int i < 100) { var = 4; } }
はgcc -S 1.C
1.sます:
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $0, -16(%ebp)
jmp L2
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
leave
ret
2.C
main() { while(int i < 100) { int var = 4; } }
はgcc -S 2.C
2.sます:
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $0, -16(%ebp)
jmp L2
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
leave
ret
これらのことから、次の2つのものを見ることができます:まず、コードは両方で同じである。
。次に、VAR用ストレージは、ループ外に割り当てられています
subl $24, %esp
そして最後に、ループ内の唯一のことは、割り当てと条件のチェックです。
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
あなたが完全にループを削除せずにできると同じくらい効率的である。
これらの日、ループ内に宣言することをお勧めします。
編集:この答えは今ほとんどが廃止されました。ポスト・クラシカルコンパイラの上昇に伴って、コンパイラはそれを把握することができないケースが稀になっています。私はまだそれらを構築することができますが、ほとんどの人は、不正なコードとして建設を分類する。
最近のほとんどのコンパイラはあなたのためにこれを最適化します。ことで、私はそれがより読みやすい見つけるように私はあなたの最初の例を使用すると述べています。
内蔵型についてそう2つのスタイル(おそらく右下に生成されたコードへ)の間には差がないであろう。
変数が非自明なコンストラクタ/デストラクタを持つクラスである場合は、しかし、うまく実行時のコストに大きな違いがある可能性があります。私は一般的にスコープループ内の変数は、(可能な限り小さな範囲を維持する)だろうが、それはPERFの影響を持つことが判明した場合、私は、ループの範囲外のクラス変数を動かすに見えると思います。しかし、それを行うことは、パスが変更されることがありODEの意味など、いくつかの追加的な分析を必要とするので、sematicsがそれを許可する場合にのみ行うことができます。
RAIIクラスは、この動作が必要になる場合があります。例えば、ファイルアクセスの寿命を管理するクラスが正しくファイルへのアクセスを管理するために、各ループの反復で作成および破棄することが必要になる場合があります。
破壊されたとき、それはそれを構築し、リリースだときに、クリティカルセクションを取得LockMgr
クラスがあるとします:
while (i< 100) {
LockMgr lock( myCriticalSection); // acquires a critical section at start of
// each loop iteration
// do stuff...
} // critical section is released at end of each loop iteration
とは全く異なるます:
LockMgr lock( myCriticalSection);
while (i< 100) {
// do stuff...
}
両方のループは、同じ効率を有します。ループ内で私をインクリメントするのは良い考えかもしれ:)彼らは無限の時間がかかります両方ます。
私はかつて、いくつかのもパフォーマンス・テストを実行し、私の驚きに、そのケース1は、実際に速かった発見しました!私は、ループ内の変数を宣言すると、その範囲を減少させるので、これはかもしれ仮定ので、早くfree'dます。しかし、それは非常に古いコンパイラで、長い時間前でした。イム必ず最新のコンパイラはdiferencesを離れて最適化のよりよい仕事をするが、それはまだ可能な限り短く、あなたの変数のスコープを維持するために傷つけることはありません。
#include <stdio.h>
int main()
{
for(int i = 0; i < 10; i++)
{
int test;
if(i == 0)
test = 100;
printf("%d\n", test);
}
}
コード常に上記ループ内のローカル変数のみ各関数呼び出しごとに一度割り当てられることを意味する100 10回印刷します。
を確認してくださいする唯一の方法は、それらを時間を計ることです。しかし、違いは、1がある場合は、微視的になりますので、あなたは、強大な大きなタイミングループが必要になります。
それは変数varを初期化しているため、他の1が初期化されていないそれを残しながら、ポイントにより、最初のものは、より良いスタイルです。これは、1つは、可能な限りそれらの使用点に近いように変数を定義する必要がありガイドラインは、最初の形式は、通常好まれるべきであることを意味する。
2つの変数のみを使用すると、コンパイラは、おそらく両方のためのレジスタを割り当てることになります。これらのレジスタはとにかくありますので、これは時間がかかることはありません。 2レジスタ書き込みと1個のレジスタがあり、いずれの場合に命令を読みます。
私は、ほとんどの答えがある検討する主要なポイントを逃していると思います。実際は、すべての議論によって明らかにし、「それは明らかです」。いいえそうではありません。 この場合には、私は宣言をお勧めしたい - (あなたが火星着陸船のために計算していない限り)私はそうは本当に唯一の問題は、より賢明かつ読みやすい&メンテナンス性に見えるもので、効率はかなり非問題であり、ほとんどのループコードにお勧めしたいですフロントアップ&ループ外の変数 - これは単にそれがより明確になります。次に、あなた&Iのような人々も、それが有効だかいないかどうかを確認するために、オンラインチェックの時間を無駄にする気にしないでしょう。
は真実ではないthatsの オーバーヘッドしかしその無視できるオーバーヘッドがあります。
おそらく彼らはそれはまだそれを割り当てスタック上の同じ場所で終わるだろうにもかかわらず。 これは、そのint型のため、スタック上のメモリ位置を割り当て、その後}の最後でそれを解放します。意味でのヒープの自由の意味でそれは1で(スタックポインタ)SPを移動するわけではありません。 そして、あなたの場合にはそれだけで、それは単にFP(フレームポインタ)とSPを同一視しますつのローカル変数を持って考えると
短い答えは次のようになります。DONT CAREいずれかの方法では、ほぼ同じWORKS
。しかし、スタックの編成方法の詳細読んでみてください。私の学部の学校は、その上かなり良い講義を持っていました あなたが読みたいと思う場合は、よりここでチェック のhttp://www.cs .utk.edu /〜プランク/プランク/クラス/ cs360 / 360 /ノート/ Assembler1 / lecture.htmlする