2017-01-18 18 views
2

私はCコードに従うことについて質問があります。Cコード最適化クエリ

struct pqr { 
    int b; 
}; 

struct abc { 
    int a; 
    struct pqr * ptr2; 
}; 

struct abc *ptr1; 

ptr1->ptr2->b = 10; 

今、私はptr1-で数行のコードを持っている場合>機能でデリファレンスPTR2、 は、次のコードの変更はCPUのcylesを減らすのに役立ちますか?

struct pqr *ptr = ptr1->ptr2; 

    ptr->b = 10; 
+3

両方のバージョンで生成されたアセンブリを見てみませんか?違いは見えますか? –

+0

'pqr * ptr2'のメモリをどこに割り当てるのですか? – Lundin

+1

これは、これが回答とどのくらい関係がありますか? – Gerhardh

答えて

-1

CPUサイクルを減らすのに役立つと思います。 ptrはポインタ変数であり、コンパイル時にptr1->ptr2の値を計算することはできません。少なくとも可読性は良いです(ptr)。

0

最適化されていないコンパイラでは、それが役に立ちます。しかし、現代のコンパイラでは、最適化フラグを使用して、2つの構文の違いを見てはいけません。

+0

単一行 'ptr-> b = 10;'に当てはまるかもしれません。しかし、2つ以上の 'ptr-> XXX'がある場合、その違いは異なります。 – i486

+2

コンパイラがエイリアシングの不在を証明できるかどうかによって異なります。 – njuffa

+0

@ i486いいえ、最適化コンパイラは、必要に応じて一時変数を導入するのに十分スマートです。 – Lundin

0

いいえ、そのようなコードはパフォーマンスを向上させません。いずれの半分のコンパイラはその一時変数を最適化します。お使いのバージョンのいずれかが、次のようなマシンコードにコンパイルされます:インデックス・レジスタの

店ptr1-> PTR2 X
は内容でものを行う
...インデックス・レジスタによって指し示さX
...
あなたは、いくつかの式の中でたくさんのstructそのネストされたを使用しようとしている場合は、同じ変数の//その後の使用は、しかし、読みやすさを向上させることができ、X


あなたの一時変数インデックス・レジスタを使用します。

something_t* s = something->something->something; 
s->x = 5; 
if(s->x == s->y) 
    s->y = 42; 

something->something->something->x = 5; 
if(something->something->something->x == something->something->something->y) 
    something->something->something->y = 42; 

のようなコードを交換すると、可読性のはかなり改善されています。しかし、これはまったく同じマシンコードを生成します。

+0

(厳密なエイリアシングに関して)より保守的なコンパイラフラグを使用すると、各アクセスのアドレスを読み込みやすくなります(少なくとも埋め込み/マイクロコントローラアセンブリでの私の経験)。 – Lou

+0

@Lousy厳密なエイリアシングはこれとは関係ありません。例のすべてのポインタ型は互換性があります。 – Lundin

+0

エイリアシングは問題になります。スコープ内の同じオブジェクトへの2つのポインタが簡単に存在する可能性があります –

1

表示するコードが十分ではありません。

すべてが1つの機能にある場合は、違いはありません。 struct pqrのうちの2つを関数に渡す場合は、ポインタのキャッシュがプログラムのセマンティクスを保持するかどうかを判断するために、関数/モジュールの別名解析が必要になります。

+0

私は、コード断片を小さく保つためにメモリ割り当て部分を削除しました。私が最初の記事で述べたように、ptr1→ptr2→b = 10はちょうど1つのサンプル行で、ptr1→ptr2の参照を使用するいくつかの行があります。 – LML

1

これはコンパイラに依存しますが、いくつかの既知のコンパイラのコードでこの明示的な最適化の恩恵を受けます。 -O3と

int getValue() { 
    return rand(); 
} 

void testA() { 
    ptr1->ptr2->b = getValue(); 
    ptr1->ptr2->b = getValue(); 
    ptr1->ptr2->b = getValue(); 
} 

void testB() { 
    struct pqr *ptr2 = ptr1->ptr2; 
    ptr2->b = getValue(); 
    ptr2->b = getValue(); 
    ptr2->b = getValue(); 
} 
両方 GCC 6.3

Clang 3.9.1はこれにアセンブリ似て生成されます:このコード例については

testA(): 
     mov  rax, QWORD PTR ptr1[rip] 
     mov  rbx, QWORD PTR [rax+8] // load 
     call rand    
     mov  DWORD PTR [rbx], eax // store 

     mov  rax, QWORD PTR ptr1[rip] 
     mov  rbx, QWORD PTR [rax+8] // load 
     call rand    
     mov  DWORD PTR [rbx], eax // store 

     mov  rax, QWORD PTR ptr1[rip] 
     mov  rbx, QWORD PTR [rax+8] // load 
     call rand    
     mov  DWORD PTR [rbx], eax // store 


testB(): 
     mov  rax, QWORD PTR ptr1[rip] 
     mov  rbx, QWORD PTR [rax+8] // load 

     call rand 
     mov  DWORD PTR [rbx], eax // store 

     call rand 
     mov  DWORD PTR [rbx], eax // store 

     call rand 
     mov  DWORD PTR [rbx], eax // store 
+0

私はupvoted、最適化なしでアセンブリを見て良いでしょう。 – cdcdcd

+0

@cdcdcd:答え(コンパイラエクスプローラ:[GCC](https://godbolt.org/g/9h8Ho2)、[Clang](https://godbolt.org/g/e92cVu))からのリンクを確認し、クリアしてくださいコンパイラのドロップダウンボックスの横にあるテキストボックス。すべての場合、私は 'testA'が' ptr1'に3回、 'testB'に1回しかアクセスしていないことを確認しました。 – Lou

+0

ごめんなさいLousyは怠惰だ。ありがとう、 – cdcdcd

0

どのように多くの命令をコンパイラが出力、ケースのためのあなた」最適化の程度(ただしここでは何も想定していません)、コンパイラがどのくらいスマートなのか、プロセッサの種類(CISCやRISCなど)によって異なります。ここで答えを得るにはあまりにも多くの変数があることが分かります。逆アセンブルして、違うバージョンのコードで、より多くの指示を含むコンパイルが生成されるかどうかを確認する必要があります(コードの違反行のみ)。

しかし、一般的には、ループ内またはループ内から呼び出された関数内で実行する場合は、「間接指定」という形式は避けてください。その理由は、間接参照を解決するためにコンパイラがいくつかの操作を実装する可能性があるためです。たとえCコード内でポインタを厳密に逆参照するように見えなくても、コンパイラはアセンブリを生成します。構造メンバーのいずれか。したがって、あなたの場合は、1つではなく2つのレベルの間接指定を解決する必要があります(代替:ptr-> b)。