2009-08-25 8 views
9

Sagen, ich habe eine Struktur wie die folgende ...C Zeiger vs direktes Mitglied Zugang für structs

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

... und ich habe eine globale Variable dieses Typs (ich bin mir wohl bewusst, welche Gefahren von Globals, dies ist für ein eingebettetes System, das ich nicht entworfen habe und für das sie ein unglückliches, aber notwendiges Übel sind.) Ist es schneller, direkt oder durch einen Zeiger auf die Mitglieder der Struktur zuzugreifen? dh

double LocalSpeed = MyGlobal.MaxSpeed; 

oder

double LocalSpeed = pMyGlobal->MaxSpeed; 

Eine meiner Aufgaben ist es, ein vor kurzem geerbt Embedded-System zu vereinfachen und zu beheben.

+1

Was bietet ein Zeiger Ihnen mit diesem direkten Zugriff nicht? Und ein Zeiger ist langsamer. –

+3

-1 für die Frage nach Mikro-Optimierung, ohne es zuerst zu benchmarken. – bk1e

Antwort

19

Im Allgemeinen sagt ich mit der ersten Option gehen würde:

double LocalSpeed = MyGlobal.MaxSpeed; 

Dies hat zu einer weniger dereferenzieren (Sie nicht den Zeiger zu finden, dann dereferencing es zu bekommen, um seine Lage). Es ist außerdem einfacher und einfacher zu lesen und zu warten, da Sie die Zeigervariable nicht zusätzlich zur Struktur erstellen müssen.

Das heißt, ich glaube nicht, dass Leistungsunterschiede, die Sie sehen würden, sogar auf einem eingebetteten System bemerkbar wären. Beides wird sehr, sehr schnell erreichbar sein.

+0

Warum die Downvotes? Ich wäre neugierig, warum die Leute nicht übereinstimmen. –

+1

Ich bin mit Reed hier. Die direkte Referenzierung ist sicherer, erfordert keine NULL-Prüfung und speichert eine Dereferenzierungsoperation. Auf den Speicherinhalt der Variablen muss sowieso zugegriffen werden, also warum eine zusätzliche Suche durchführen. Ein Zeiger erhält nichts, wenn Sie bereits direkten Zugriff auf die Variable haben. –

+0

Was die Zeiger-Dereferenzierung angeht, das habe ich mir gedacht, es ist nur eine lange Zeit her, seit ich mit C oder C++ umgehen musste. Danke für Ihren Rat. –

1

Ich nehme an, dass, wenn dies überhaupt einen Unterschied macht, das architekturabhängig wäre.

1

In C sollte kein Unterschied oder ein unbedeutender Leistungseinbruch auftreten.

C Schüler unterrichtet:

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

sollten Sie in der Lage sein, die Demontage von ihnen vergleichen sowohl sich selbst davon zu überzeugen, dass sie im Wesentlichen gleich sind, auch wenn Sie Programmierer keine Assembly-Code sind.

Wenn Sie nach einer Leistungsoptimierung suchen, würde ich woanders suchen. Mit dieser Art der Mikrooptimierung können Sie nicht genügend CPU-Zyklen speichern.

Aus stilistischen Gründen bevorzuge ich die Struktur-Punkt-Notation, besonders im Umgang mit Singleton-Globals. Ich finde es viel sauberer zu lesen.

+1

Die Frage geht nicht um -> vs *, es geht darum, einen Zeiger an erster Stelle zu verwenden. –

+1

Die Frage ist: "Ist es schneller, auf die Mitglieder der Struktur direkt oder über einen Zeiger zuzugreifen?" Ich glaube, ich habe die Frage angesprochen. – abelenky

8

Die erste sollte schneller sein, da keine Pointer-Dereferenzierung erforderlich ist. Andererseits gilt das für x86-basierte Systeme, für andere nicht sicher.

auf x86 der erste, der etwas übersetzen würde wie diese

mov eax, [address of MyGlobal.MaxSpeed] 

und die zweite wäre so etwas wie dieses

mov ebx, [address of pMyGlobal] 
mov eax, [ebx+sizeof(int)] 
1

Im Allgemeinen sein, den Zugriff auf die Struktur direkt schneller sein würde, als es wird keine zusätzliche Zeigerdereferenzierung benötigt. Die Zeiger-Dereferenzierung bedeutet, dass sie den Zeiger (das Ding in der Variablen) nehmen muss, was auch immer darauf zeigt, und dann darauf operieren.

3

Auf Ihrer Embedded-Plattform ist es wahrscheinlich, dass die Architektur so optimiert ist, dass es im Grunde genommen ein Wash ist, und selbst wenn dies nicht der Fall wäre, würden Sie nur einen Leistungseinbruch bemerken, wenn dies in einer sehr engen Schleife ausgeführt würde .

Es gibt wahrscheinlich viel offensichtlichere Leistungsbereiche Ihres Systems.

3
struct dataStruct 
{ 
    double first; 
    double second; 
} data; 

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

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

Dies ist die Verwendung von VS2008 Freigabeanordnung Ausgabemodus:

data.first = 9.0; 
008D1000 fld   qword ptr [[email protected] (8D20F0h)] 

    pData->second = 10.0; 
008D1006 xor   eax,eax 
008D1008 fstp  qword ptr [data (8D3378h)] 
008D100E fld   qword ptr [[email protected] (8D20E8h)] 
008D1014 fstp  qword ptr [data+8 (8D3380h)] 
+1

Zielen Sie jetzt auf seine eingebettete Plattform. – Alan

+0

Was waren die Optimierungseinstellungen? –

+0

Das ist falsch. Ihr erstes Mitglied wird immer nach Definition einer Struktur (der Zeiger einer Struktur ist auch ein Zeiger eines ersten Mitglieds) gewinnen. Sie sollten Sekunde gegen Sekunde vergleichen. –

2

zerlegen, zerlegen, zerlegen ...

auf den Codezeilen Je Sie nicht uns zeigen werden, ist es möglich, Wenn Ihr Zeiger etwas statisch ist, wird ein guter Compiler das wissen und die Adresse für beide vorberechnen. Wenn Sie keine Optimierungen haben, ist diese ganze Diskussion stumm. Es hängt auch von dem Prozessor ab, den Sie verwenden, beide können je nach Prozessor mit einem einzigen Befehl ausgeführt werden. Also ich folgen die grundlegenden Optimierungsschritte:

1) zerlegen und untersuchen 2) Zeit, um die Ausführung

Wie oben erwähnt, obwohl das Endergebnis ist, kann es ein Fall von zwei Befehle werden statt einer einen einzigen Takt Kalkulation Zyklus, den Sie wahrscheinlich nie sehen würden. Die Qualität Ihrer Compiler- und Optimizer-Entscheidungen wird deutlich dramatischere Leistungsunterschiede bewirken als der Versuch, eine Codezeile zu optimieren, um die Leistung zu verbessern. Wechselnde Compiler können 10-20% in beide Richtungen geben, manchmal mehr. Wie Sie Ihre Optimierungsflags ändern können, wenn Sie alles einschalten, wird nicht der schnellste Code erstellt, manchmal ist -O1 besser als -O3.

Verstehen, was diese beiden Codezeilen produzieren und wie die Leistung aus der Hochsprache maximiert wird, ergibt sich aus dem Kompilieren für verschiedene Prozessoren und dem Zerlegen mit verschiedenen Compilern. Und noch wichtiger ist, dass der Code um die fraglichen Zeilen eine große Rolle dabei spielt, wie der Compiler dieses Segment optimiert.

Mit jemand anderes Beispiel auf diese Frage:

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

dataStruct data; 

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

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

    return(0); 
} 

Mit gcc (nicht so toll einen Compiler) erhalten Sie:

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

So beide Zeilen C-Code in einem Speicher verbunden ist, Das Problem hier ist das Beispiel, das als Test verwendet wird. Zwei separate Funktionen wären ein wenig besser gewesen, aber es braucht viel mehr Code und der Zeiger muss auf irgendeinen anderen Speicher zeigen, so dass der Optimierer nicht erkennt, dass es eine statische globale Adresse ist. Um dies zu testen, müssen Sie die Adresse übergeben Der Compiler (also gcc) kann also nicht herausfinden, dass es sich um eine statische Adresse handelt.

Oder ohne Optimierungen, gleichen Code, gleichen Compiler, kein Unterschied zwischen Zeiger und direkt.

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

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

Dies ist, was Sie je nach Compiler und Prozessor erwarten würden, gibt es keinen Unterschied. Für diesen Prozessor würde, selbst wenn der Testcode die statische Adresse für den Zeiger von der Funktion verdeckte, er immer noch auf zwei Befehle herunterkochen. Wenn der Wert, der in dem Strukturelement gespeichert wird, bereits in einem Register geladen ist, dann wäre es ein Befehl, entweder der Zeiger oder direkt.

Also die Antwort auf Ihre Frage ist nicht absolut ...es kommt darauf an. zerlegen und testen.

+0

mit Doppel anstelle von Ints bedeutet nur mehr Register der Speicher selbst ist immer noch eine einzige Anweisung für den Zeiger oder direkten Zugriff, so dass es immer noch keinen Unterschied für diesen Compiler für diesen Prozessor für diese Kompiliersitzung. –

+0

Das ist falsch. Ihr erstes Mitglied wird immer nach Definition einer Struktur (der Zeiger einer Struktur ist auch ein Zeiger eines ersten Mitglieds) gewinnen. Sie sollten Sekunde gegen Sekunde vergleichen. –

0

Direkter Mitgliederzugriff ist schneller (für Zeiger würde man normalerweise eine Zeigerdereferenzierungsoperation erhalten). Obwohl es mir schwer fällt, mir das in einer Situation vorzustellen, in der es ein Problem, eine Leistung oder anders ist.