2017-01-18 3 views
2

Ich habe eine Frage zu folgenden C-Code.C-Code-Optimierung Abfrage

struct pqr { 
    int b; 
}; 

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

struct abc *ptr1; 

ptr1->ptr2->b = 10; 

Nun, wenn ich mehrere Zeilen Code mit ptr1-> ptr2 in einer Funktion dereferencing wird der folgende Code Änderung CPU cyles reduzieren helfen?

+3

Warum betrachten Sie nicht die generierte Assembly für beide Versionen? Siehst du einen Unterschied? –

+0

Wohin weisen Sie Speicher zu, auf den 'pqr * ptr2' zeigen soll? – Lundin

+1

Inwieweit könnte dies für die Antwort relevant sein? – Gerhardh

Antwort

-1

Ich denke, es wird dazu beitragen, CPU-Zyklen zu reduzieren. ptr ist Zeigervariable und ptr1->ptr2 Wert kann nicht zur Kompilierzeit berechnet werden. Zumindest ist die Lesbarkeit besser (mit ptr).

0

Bei nicht optimierten Compilern hilft das. Aber in modernen Compilern, mit Optimierungsflags, sollten Sie den Unterschied zwischen den 2 Syntaxen nicht sehen.

+0

Es kann für eine einzelne Zeile 'ptr-> b = 10;' gelten. Aber wenn es 2+ 'ptr-> XXX 'darunter gibt, wird es anders sein. – i486

+2

Das hängt vielleicht davon ab, ob der Compiler das Fehlen von Aliasing beweisen kann. – njuffa

+0

@ i486 Nein, ein optimierender Compiler ist schlau genug, um temporäre Variablen bei Bedarf einzuführen. – Lundin

0

Nein, ein solcher Code verbessert die Leistung nicht. Jeder halbwegs ordentliche Compiler optimiert diese temporäre Variable. Jede Ihrer Versionen in Maschinencode kompiliert wird wie folgt aus:

store ptr1-> ptr2 in Indexregister x
...
tun Sachen mit Inhalt deutete auf einen Index-Register x
...
// anschließende Verwendung der gleichen Variable Indexregister x


Ihre temporäre Variable könnte Lesbarkeit jedoch verbessern verwenden, wenn Sie die verschachtelte Struktur eine Menge in mehreren Ausdrücke verwenden wollen. Ersetzen Code wie

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

mit

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

ist schon eine Verbesserung der Lesbarkeit. Aber es wird den gleichen Maschinencode ergeben.

+0

Mit etwas konservativeren Compiler-Flags (in Bezug auf striktes Aliasing) könnte es leicht dazu führen, dass die Adresse bei jedem Zugriff gelesen wird (zumindest meine Erfahrung mit Embedded/Microcontroller Assembly). – Lou

+0

@Lousy Strenges Aliasing hat damit nichts zu tun. Alle Zeigertypen in den Beispielen sind kompatibel. – Lundin

+0

Aliasing könnte jedoch eine Rolle spielen. Es könnte leicht zwei Zeiger auf das gleiche Objekt im Bereich sein –

1

Es ist nicht genug Code gezeigt, um zu erzählen.

Wenn es alles in einer Funktion ist, sollte es keinen Unterschied machen. Wenn Sie zwei der struct pqr in eine Funktion übergeben, dann wird die Quer-/Modul-Alias-Analyse notwendig, um zu bestimmen, ob das Zwischenspeichern des Zeigers die Semantik des Programms beibehält.

+0

Ich habe den Speicherzuordnungsteil weggelassen, um das Codefragment klein zu halten. Wie ich im ursprünglichen Post erwähnte, gibt es mehrere Zeilen, die ptr1-> ptr2-Deferenzierung verwenden, wobei ptr1-> ptr2-> b = 10 nur eine Beispielzeile ist. – LML

1

Dies hängt vom Compiler ab, aber mehrere bekannte Compiler werden von dieser expliziten Optimierung in Ihrem Code profitieren.

Zu diesem Beispielcode:

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(); 
} 

Sowohl GCC 6.3 und Clang 3.9.1 mit O3 wird diese Anordnung ähnlich produzieren:

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

I upvoted, aber es wäre gut, die Baugruppe ohne Optimierung zu sehen. – cdcdcd

+0

@cdcdcd: Überprüfen Sie die Links von der Antwort (Compiler Explorer: [GCC] (https://godbolt.org/g/9h8Ho2), [Clang] (https://godbolt.org/g/e92cVu)) und deaktivieren Sie das Textfeld neben dem Dropdown-Feld des Compilers. In allen Fällen habe ich überprüft, dass "testA" dreimal auf "ptr1" und "testB" nur einmal zugreift. – Lou

+0

Sorry Lousy ist faul. Vielen Dank, – cdcdcd

0

Wie viele Befehle ein Compiler ausgibt, für den Fall, dass Sie‘ Wie hoch ist der Compiler und die Art des Prozessors (zum Beispiel CISC oder RISC), hängt vom Grad der Optimierung ab (aber hier nehmen Sie keine an?). Wie Sie sehen können, gibt es einfach zu viele Variablen, um hier eine Antwort zu erhalten. Sie müssen disassemblieren und feststellen, ob eine andere Version Ihres Codes Zusammenstellungen mit mehr oder weniger Anweisungen (und nur für die fehlerhaften Codezeilen) erzeugt.

Aber allgemein in der Vergangenheit war der Rat immer, dass jede Form von "Indirection" vermieden werden sollte, wenn Sie es innerhalb einer Schleife oder innerhalb einer Funktion, die aus einer Schleife aufgerufen wurde, tun. Der Grund dafür ist, dass der Compiler eine Anzahl von Operationen implementieren kann, um die Indirektion zu lösen. Selbst wenn es nicht so aussieht, als ob Sie einen Zeiger im C-Code streng deneferenzieren würde, würde der Compiler eine Assemblierung erzeugen, die es tun würde eines der Mitglieder einer Struktur, das ist. Daher müssen Sie in Ihrem Fall zwei Ebenen der Indirektion statt einer (die Alternative: ptr-> b) auflösen.