11

次のような構造体があるとします...

typedef struct {
  int WheelCount;
  double MaxSpeed;
} Vehicle;

...そして、私はこのタイプのグローバル変数を持っています(私はグローバルの落とし穴をよく知っています. ) 構造体のメンバーに直接アクセスするか、ポインターを介してアクセスする方が高速ですか? すなわち

double LocalSpeed = MyGlobal.MaxSpeed;

また

double LocalSpeed = pMyGlobal->MaxSpeed;

私の仕事の 1 つは、最近継承された組み込みシステムを単純化して修正することです。

4

9 に答える 9

21

一般に、最初のオプションを使用すると思います。

double LocalSpeed = MyGlobal.MaxSpeed;

これにより、逆参照が1つ少なくなります(ポインターが見つからず、逆参照してその場所に到達することはありません)。また、構造体に加えてポインター変数を作成する必要がないため、読みやすく、維持しやすくなります。

そうは言っても、組み込みシステムであっても、目に見えるパフォーマンスの違いは目立たないと思います。どちらも非常に高速なアクセス時間になります。

于 2009-08-25T15:40:07.117 に答える
3
struct dataStruct
{
    double first;
    double second;
} data;

int main()
{
    dataStruct* pData = &data;

    data.first = 9.0;
    pData->second = 10.0;
}

これは、VS2008 リリース モードを使用したアセンブリ出力です。

    data.first = 9.0;
008D1000  fld         qword ptr [__real@4022000000000000 (8D20F0h)] 

    pData->second = 10.0;
008D1006  xor         eax,eax 
008D1008  fstp        qword ptr [data (8D3378h)] 
008D100E  fld         qword ptr [__real@4024000000000000 (8D20E8h)] 
008D1014  fstp        qword ptr [data+8 (8D3380h)] 
于 2009-08-25T15:48:36.973 に答える
2

分解、分解、分解…

表示されていないコード行によっては、ポインターがある程度静的である場合、優れたコンパイラーがそれを認識し、両方のアドレスを事前に計算する可能性があります。最適化を行っていない場合、この議論全体は無言です。また、使用しているプロセッサにも依存します。プロセッサによっては、両方を 1 つの命令で実行できます。したがって、基本的な最適化手順に従います。

1) 分解して調べる 2) 実行時間を計る

上で述べたように、肝心なのは、1 つのクロック サイクルがかかるのではなく、2 つの命令の場合である可能性があるということです。コンパイラーとオプティマイザーの選択の質によって、パフォーマンスの向上を期待して 1 行のコードを微調整するよりもはるかに劇的なパフォーマンスの違いが生まれます。コンパイラを切り替えると、どちらの方向でも 10 ~ 20%、場合によってはそれ以上の結果が得られます。最適化フラグを変更できるように、すべてをオンにしてもコードが最速になるわけではなく、-O1 が -O3 よりもパフォーマンスが優れている場合があります。

これら 2 行のコードが生成するものと、高水準言語からパフォーマンスを最大化する方法を理解するには、さまざまなプロセッサ用にコンパイルし、さまざまなコンパイラを使用して逆アセンブルします。さらに重要なことに、問題の行の周りのコードは、コンパイラがそのセグメントを最適化する方法に大きな役割を果たします。

この質問で他の人の例を使用する:

typedef struct
{
    unsigned int first;
    unsigned int second;
} dataStruct;

dataStruct data;

int main()
{
    dataStruct *pData = &data;

    data.first = 9;
    pData->second = 10;

    return(0);
}

gcc (それほど優れたコンパイラではない) を使用すると、次のようになります。

mov r2, #10
mov r1, #9
stmia   r3, {r1, r2}

したがって、C コードの両方の行が 1 つのストアに結合されます。ここでの問題は、テストとして使用される例です。2 つの別個の関数の方が少しはましでしたが、その周りにはさらに多くのコードが必要であり、ポインタは他のメモリを指す必要があるため、オプティマイザはそれが静的グローバル アドレスであることを認識しません。これをテストするには、アドレスを渡す必要があります。そのため、コンパイラ (まあ gcc) はそれが静的アドレスであることを認識できません。

または、最適化なし、同じコード、同じコンパイラ、ポインターと直接の違いなし。

mov r3, #9
str r3, [r2, #0]

mov r3, #10
str r3, [r2, #4]

これは、コンパイラとプロセッサに応じて表示されると予想されるものであり、違いがない場合があります。このプロセッサでは、テスト コードがポインタの静的アドレスを関数から隠していたとしても、2 つの命令に要約されます。構造要素に格納されている値が既にレジスタにロードされている場合、ポインターまたは直接のいずれかの方法で 1 つの命令になります。

したがって、あなたの質問への答えは絶対的なものではありません...それは依存します. 分解してテスト。

于 2009-08-27T14:06:24.947 に答える
1

これがまったく違いを生む場合、それはアーキテクチャに依存すると思います。

于 2009-08-25T15:38:36.947 に答える
1

C では、違いがないか、わずかなパフォーマンス ヒットがあるはずです。

C の学生は次のように教えられます。

pMyGlobal->MaxSpeed == (*pMyGlobal).MaxSpeed

アセンブリ コードのプログラマーでなくても、両方の逆アセンブリを比較して、本質的に同じであることを確信できるはずです。

パフォーマンスの最適化を探しているなら、他の場所を探すでしょう。この種のマイクロ最適化では、十分な CPU サイクルを節約することはできません。

文体上の理由から、特にシングルトン グローバルを扱う場合は、Structure-Dot 表記を好みます。私はそれを読むのがずっときれいだと思います。

于 2009-08-25T15:40:29.597 に答える
0

メンバーへの直接アクセスの方が高速です (通常、ポインターの逆参照操作が 1 回増えるポインターの場合)。問題、パフォーマンス、またはその他の状況でそれを想像するのに苦労していますが。

于 2009-08-27T14:12:30.040 に答える