Wie stark beeinflusst die Zeiger-Indirektion die Effizienz?

7

Dereferenziert ein Zeiger merklich langsamer als nur direkt auf diesen Wert zuzugreifen? Ich nehme an, meine Frage ist - wie schnell ist der Dereferenzierungsoperator?

    
user965369 02.11.2011, 16:15
quelle

5 Antworten

20

Das Durchlaufen einer Zeigerindirektion kann viel langsamer sein, weil eine moderne CPU funktioniert. Aber es hat nicht viel mit Laufzeitspeicher zu tun.

Stattdessen wird die Geschwindigkeit durch Vorhersage und Cache beeinflusst.

Die Vorhersage ist einfach, wenn der Zeiger nicht geändert wurde oder wenn er auf vorhersehbare Weise geändert wurde (z. B. um vier in einer Schleife inkrementieren oder dekrementieren). Dies ermöglicht es der CPU, der tatsächlichen Codeausführung im Wesentlichen voraus zu laufen, herauszufinden, was der Zeigerwert sein wird, und diese Adresse in den Cache zu laden. Vorhersage wird unmöglich, wenn der Zeigerwert durch einen komplexen Ausdruck wie eine Hash-Funktion gebildet wird.

Cache kommt ins Spiel, weil der Zeiger in Speicher zeigen kann, der nicht im Cache ist, und er muss abgerufen werden. Dies wird minimiert, wenn die Vorhersage funktioniert, aber wenn eine Vorhersage unmöglich ist, dann können Sie im schlimmsten Fall eine doppelte Auswirkung haben: Der Zeiger befindet sich nicht im Cache und das Zeigerziel befindet sich auch nicht im Cache. In diesem schlimmsten Fall würde die CPU zweimal blockieren.

Wenn der Zeiger für einen Funktionszeiger verwendet wird, kommt der Verzweigungsprädiktor der CPU ins Spiel. In virtuellen C ++ - Tabellen sind die Funktionswerte alle konstant und der Prädiktor hat es einfach. Die CPU wird den Code bereit zum Ausführen und in der Pipeline haben, wenn die Ausführung den indirekten Sprung durchläuft. Wenn es sich jedoch um einen unvorhersagbaren Funktionszeiger handelt, kann die Auswirkung auf die Leistung schwerwiegend sein, da die Pipeline gelöscht werden muss, wodurch bei jedem Sprung 20 bis 40 CPU-Zyklen verschwendet werden.

    
Zan Lynx 02.11.2011, 16:25
quelle
3

Hängt von Dingen wie:

ab
  • ob der Wert "direct accessed" bereits in einem Register ist oder auf dem Stack (das ist auch eine Zeiger-Indirektion)
  • ob die Zieladresse bereits im Cache ist
  • die Cache-Architektur, Busarchitektur usw.

dh zu viele Variablen, über die sinnvollerweise spekuliert werden kann, ohne sie zu verengen.

Wenn Sie es wirklich wissen wollen, vergleichen Sie es mit Ihrer spezifischen Hardware.

    
Useless 02.11.2011 16:20
quelle
2

es erfordert einen Speicherzugriff mehr:

  1. lese die in der Zeigervariablen
  2. gespeicherte Adresse
  3. lies den Wert an der Adresse lesen

Dies könnte nicht gleich zwei einfachen Operationen sein, da es möglicherweise auch mehr Zeit benötigt, um auf eine Adresse zuzugreifen, die nicht bereits im Cache geladen ist.

    
Heisenbug 02.11.2011 16:19
quelle
2

Wenn Sie davon ausgehen, dass es sich um einen echten Zeiger handelt (nicht um einen intelligenten Zeiger irgendeiner Art), verbraucht die Dereferenzierungsoperation überhaupt keinen (Daten-) Speicher. Es enthält jedoch (möglicherweise) eine zusätzliche Speicherreferenz: eine, um den Zeiger selbst zu laden, die andere, um auf die Daten zuzugreifen, auf die der Zeiger zeigt.

Wenn Sie einen Zeiger in einer engen Schleife verwenden, wird er normalerweise für die Dauer in ein Register geladen. In diesem Fall liegen die Kosten hauptsächlich in dem zusätzlichen Registerdruck (d. H. Wenn Sie ein Register zum Speichern dieses Zeigers verwenden, können Sie es nicht verwenden, um etwas anderes zur gleichen Zeit zu speichern). Wenn Sie einen Algorithmus haben, der andernfalls genau die Register füllen würde, aber mit der Registrierung eines Zeigers würde der Speicher überlaufen, kann das einen Unterschied machen. Zu einer Zeit war das ein ziemlich großer Verlust, aber mit den meisten modernen CPUs (mit mehr Registern und On-Board-Cache) ist das selten ein großes Problem. Die offensichtliche Ausnahme wäre eine eingebettete CPU mit weniger Registern und ohne Cache (und ohne On-Chip-Speicher).

Die Quintessenz ist, dass es normalerweise ziemlich vernachlässigbar ist, oft unterhalb der Schwelle, wo Sie es sogar zuverlässig messen können.

    
Jerry Coffin 02.11.2011 16:24
quelle
2

Es tut es. Es kostet einen extra fetch.
Beim Zugriff auf eine Variable nach Wert wird die Variable direkt von ihrem Speicherort gelesen.
Der Zugriff auf denselben Durchgangszeiger fügt einen zusätzlichen Aufwand hinzu, um die Adresse der Variablen aus dem Zeiger zu holen und dann den Wert von diesem Speicherort zu lesen.

Natürlich vorausgesetzt, dass die Variable nicht in einem Register platziert wird, was in einigen Szenarien wie enge Schleifen wäre. Ich glaube, die Frage sucht die Antwort auf einen Overhead, der keine solchen Szenarien voraussetzt.

    
Alok Save 02.11.2011 16:19
quelle

Tags und Links