5

ポインターを使用する場合、パフォーマンスに影響はありますか?

ポインタの使用を避ける方が良いですか?使用する場合、どのような状況でですか?明らかに、それらは参照とともに、データのコピーを減らすのに役立ちます。指し示すデータ型が小さければ、ポインターの必要性は少ないと思います。対照的に、ポインタのオーバーヘッドはオブジェクトのコピーのオーバーヘッドよりも小さいため、ポインタを介して大きなオブジェクトを渡す方が適切です。

引数/パラメータ以外の領域のポインタについても疑問に思っていましたか?

このパフォーマンスのコンテキストでは、参照は一般的にポインターよりも優れていますか?

私はマイクロ最適化のSO「汚い」トピックに接していることに感謝していますが、非常にレイテンシーに焦点を当てたアプリを書いています。

4

6 に答える 6

7

パフォーマンスが重要になる可能性があることは知っていますが、セマンティクスのが重要です。高速で間違っていることは無意味です。

ポインターまたは参照の使用には、共有などのセマンティクスの影響があります。

void foo(A& a) {
    a.a = 1;
    if (a.b != 0) { throw ... }
    a.b = 0;
}

この場合a.b == 0、の最初のフィールドはa変更されていますが、2番目のフィールドは変更されていません。

また、そのような共有は潜在的なエイリアシングを作成する可能性があります。

void foo(struct A a, struct A b);

void foo(struct A* a, struct A* b);

前者の場合、2つの構造は必然的に異なりますが、後者の場合は異なります。この可能性のあるエイリアシングにより、最適化が妨げられる可能性があります。

最初にセマンティクスに焦点を合わせます。それらを正しく理解したら、特定の状況での効果を微調整して測定できます。

于 2012-05-28T13:53:39.093 に答える
4

このパフォーマンスのコンテキストでは、参照は一般的にポインターよりも優れていますか?

はい、可能な場合は参照を使用し、必要な場合はポインタを使用してください。パフォーマンス面では、それらは同じです。

はい、余分なコピーを防ぐために、通常は参照またはポインタで大きな構造体を渡す方が良いでしょう。

ポインタまたは参照を介して変数またはオブジェクトにアクセスするのは、直接アクセスするのと同じくらい速い場合があります。この効率の理由は、マイクロプロセッサの構築方法にあります。関数内で宣言されたすべての非静的変数とオブジェクトはスタックに格納され、実際にはスタックポインターを基準にしてアドレス指定されます。同様に、クラスで宣言されたすべての非静的変数とオブジェクトは、C++では「this」として知られている暗黙のポインターを介してアクセスされます。したがって、適切に構造化されたC ++プログラムのほとんどの変数は、実際には何らかの方法でポインターを介してアクセスされていると結論付けることができます。したがって、マイクロプロセッサはポインタを効率的にするように設計する必要があり、それがマイクロプロセッサです。

ただし、いくつかの欠点がありますが、それらはポインタと参照の両方に適用されます。

ただし、ポインタと参照を使用することには欠点があります。最も重要なことは、ポインタまたは参照の値を保持するために追加のレジスタが必要です。特に32ビットモードでは、レジスタは不足しているリソースです。十分なレジスタがない場合は、ポインタを使用するたびにメモリからポインタをロードする必要があり、これによりプログラムの速度が低下します。もう1つの欠点は、ポインターの値が、ポイントされた変数にアクセスできる時間の数クロックサイクル前に必要になることです。

そして、ここにソースがあります。あなたがこの質問をしたなら、私はあなたがそれを良い読み物だと思うだろうと思います。

いくつかのコードを見てみましょう:

   int x = 0;
00412E0E  mov         dword ptr [x],0 
   foo(x);
00412E15  lea         eax,[x] 
00412E18  push        eax  
00412E19  call        foo (4111C2h) 
00412E1E  add         esp,4 
   foo(&x);
00412E21  lea         eax,[x] 
00412E24  push        eax  
00412E25  call        foo (4111BDh) 
00412E2A  add         esp,4 

関数を呼び出すときに違いはありません。

void foo (int& x)
{
00411370  push        ebp  
00411371  mov         ebp,esp 
00411373  sub         esp,0C0h 
00411379  push        ebx  
0041137A  push        esi  
0041137B  push        edi  
0041137C  lea         edi,[ebp-0C0h] 
00411382  mov         ecx,30h 
00411387  mov         eax,0CCCCCCCCh 
0041138C  rep stos    dword ptr es:[edi] 
   x = 3;
0041138E  mov         eax,dword ptr [x] 
00411391  mov         dword ptr [eax],3 
}

void foo (int* x)
{
004117A0  push        ebp  
004117A1  mov         ebp,esp 
004117A3  sub         esp,0C0h 
004117A9  push        ebx  
004117AA  push        esi  
004117AB  push        edi  
004117AC  lea         edi,[ebp-0C0h] 
004117B2  mov         ecx,30h 
004117B7  mov         eax,0CCCCCCCCh 
004117BC  rep stos    dword ptr es:[edi] 
   *x = 3;
004117BE  mov         eax,dword ptr [x] 
004117C1  mov         dword ptr [eax],3 
}

関数内に違いはありません。

于 2012-05-28T13:36:55.990 に答える
1

ポインタを介したデータへのアクセスは、直接行うよりも少し遅くなりますが、間接参照操作は非常に高速であり、非常に特定の反復数処理タスクを実行しない限り、これは通常大したことではありません。

大きなオブジェクトと小さなオブジェクトのポインタを渡すことについては、正確に正しいです。たとえば、int *とintのサイズは、実装によっては同じになる場合があります。

参照とポインタは通常、パフォーマンスに関して同じです。ただし、Foo*ではなくconstFoo&を実行する習慣がある場合、コンパイラーはコードを最適化するためのより良い仕事を頻繁に行うことができます。

于 2012-05-28T13:36:35.990 に答える
1

ヒープベースのオブジェクトの最大の問題は、必要なときにそれらが一緒に配置されないことが多いことです。つまり、オブジェクトを処理するためにそれらのオブジェクトをCPUキャッシュに読み込むには、より多くの時間が必要になります(つまり、オブジェクトが10個ある場合)。それらがすべて連続して割り当てられている場合、1回のメモリ読み取りですべてが一度にキャッシュに入れられ、ヒープ全体に分散している場合は、それぞれの読み取りが必要になります)。

公平を期すために、これは、ガベージ圧縮後でもガベージコレクションされたものを含むすべてのヒープシステムに適用されます。答えは(明らかに)これらのオブジェクトを一緒に割り当てるか、時間の経過とともにそれらを割り当てるための空き領域を備えたバッファーを提供することです。

ポインタに関する他の問題は、ポインタへのオブジェクトにアクセスするために間接参照が必要なことです。オブジェクトの配列がある場合は問題ありません。通常、オブジェクトは連続しています。ただし、ポインタの配列がある場合は、オブジェクトを読み取る場所を見つけるために、それらのポインタを読み取る必要があります。

ポインタ演算を使用して隣接するオブジェクトにアクセスする場合(すべてのオブジェクトがどこにあるかを見つけるために1つのポインタを読み取るだけでよいため)、ポインタも高速になります。

つまり、問題はポインター自体にあるのではなく、ポインターが指すデータをどのように編成するかにあります。

于 2012-05-28T13:40:14.783 に答える
1

ポインタがどこを指しているかによって異なります。オブジェクトがスタックに割り当てられ、ポインタパラメータを渡すと、多くの場合、ヒープ上のオブジェクトを指すポインタよりも高速になります。渡したオブジェクトが、ほとんどの場合、CPUによってすでにキャッシュされている前に関数でアクティブに使用されていた場合、パフォーマンスはほぼ同じになります。

コピーについて...ほとんどのコンパイラは、CPUレジスタを使用して、CPUで使用可能な最速のメモリであるポインタを渡します。ただし、値として渡す場合、コンパイラはオブジェクト全体をコピーし、ctor/dtorを呼び出す必要があります。

だから私のアドバイスは、物事をスタックに保ち、ポインタ/参照を渡すようにしてください。

パフォーマンスについては一定ではなく、ハードウェア/ OS /コンパイラによって異なる可能性があるとは言い難いので、プロファイラーツールを使用してコードをプロファイリングし、キャッシュミス、メモリの断片化、CPU使用率などを分析することをお勧めします。

http://en.wikipedia.org/wiki/VTune

http://developer.amd.com/tools/CodeAnalyst/Pages/default.aspx

于 2012-05-28T14:08:27.287 に答える
-2

ポインタを使用しても、パフォーマンスに影響はありません。ポインタの使用は非常に高速です。ポインタは、ほとんどの(すべて?)専門的なプロジェクトで一般的なツールとして使用されます。これらは、リスト、キュー、マップ、ツリー、データベースなど、あらゆる場所で構築するために使用されます。それらに慣れてください!

于 2012-05-28T13:37:14.527 に答える