Gemeinsame Zeiger und die Leistung

7

Ich habe jetzt geteilte Zeiger für soem Zeit verwendet, und ich habe Leistungsprobleme in meinem Programm ... Also würde ich gerne wissen, ob gemeinsame Zeiger zu Leistungseinbußen führen. Wenn ja, wie schwer? Vielen Dank.

Mein Programm ist multi-threaded mit std :: tr1 :: shared_ptr

    
Guest 12.10.2009, 17:57
quelle

7 Antworten

12

Wenn Ihre App 700-Byte-XML-Nachrichten übergibt, die in 65-Byte-Google-Protokollnachrichten oder 85-Byte-ASN.1-Nachrichten enthalten sein könnten, spielt das wahrscheinlich keine Rolle. Aber wenn es eine Million etwas pro Sekunde verarbeitet, dann würde ich die Kosten für das Hinzufügen von 2 vollen Lese-Änderungsschreib- (RMW-) Zyklen zum Übergeben eines Zeigers nicht verwerfen.

Ein voller Lese-Modifikations-Schreibvorgang liegt in der Größenordnung von 50 ns, so dass zwei 100 ns sind. Diese Kosten sind die Kosten eines Lock-Inc und eines Lock-Dec - das gleiche wie 2 CAS's. Dies ist die Hälfte eines Windows-kritischen Abschnitts Reserve und Release. Dies wird verglichen mit einem einzigen Maschinenzyklus-Push (400 PICO Sekunden auf einer 2,5 GHz-Maschine)

Und das schließt nicht einmal die anderen Kosten für das Ungültigmachen der Cachezeile ein, die tatsächlich die Zählung enthält, die Auswirkungen der BUS-Sperre auf andere Prozessoren usw. usw.

Das Übergeben von intelligenten Zeigern durch const-Referenz ist fast IMMER zu bevorzugen. Wenn der Angerufene keinen neuen geteilten Zeiger erstellt, wenn er die Lebensdauer des Pointees garantieren oder kontrollieren möchte, ist dies ein Fehler im aufgerufenen . Willy-Nilly Passing Thread sichere Referenz Zählen intelligente Zeiger um Wert läuft nur nach Leistung Treffer.

Die Verwendung von Referenzzählern vereinfacht die Lebensdauer ohne Zweifel, aber Shared Pointer nach Wert zu übergeben, um sich gegen Defekte im Angerufenen zu schützen, ist reiner Unsinn.

Übermäßige Verwendung der Referenzzählung kann in Kürze ein schlankes Programm, das 1 mm Nachrichten pro Sekunde (mps) verarbeiten kann, in ein fettes umwandeln, das 150 k mps auf derselben Hardware verarbeitet. Auf einmal brauchen Sie ein halbes Rack Server und $ 10000 / Jahr an Strom.

Es geht Ihnen immer besser, wenn Sie die Lebensdauer Ihrer Objekte ohne Referenzzählung verwalten können.

Ein Beispiel für eine einfache Verbesserung ist, sagen wir, wenn Sie ein Objekt auffächern und Sie die Breite des Fanouts (sagen wir n) um n erhöhen, statt sie einzeln bei jedem Fanout zu erhöhen.

BTW, wenn die CPU ein Lock-Präfix sieht, sagt es wirklich "Oh nein, das wird weh tun".

Alles was gesagt wird, ich stimme allen zu, dass Sie den Hot Spot überprüfen sollten.

    
pgast 13.10.2009 05:02
quelle
10

Angesichts der Daten ist es praktisch unmöglich, diese Frage richtig zu beantworten. Die einzige Möglichkeit, wirklich festzustellen, was zu einem Leistungsproblem in Ihrer Anwendung führt, besteht darin, einen Profiler auf dem Programm auszuführen und die Ausgabe zu überprüfen.

Es ist sehr unwahrscheinlich, dass shared_ptr die Verlangsamung verursacht. Der shared_ptr-Typ und viele frühe Eigenentwicklungsvarianten werden in einer ständig wachsenden Anzahl von C ++ - Programmen verwendet. Ich selbst benutze sie in meiner Arbeit (professionell zu Hause). Ich habe viel Zeit damit verbracht, meine Arbeitsanwendungen zu profilieren, und shared_ptr war noch nie in der Nähe eines Problems in meinem Code oder anderem Code, der in der Anwendung ausgeführt wird. Es ist viel wahrscheinlicher, dass der Fehler anderswo ist.

    
JaredPar 12.10.2009 18:02
quelle
5

Wenn Ihr Programm ein Performance-Problem zu haben scheint, ist es ganz natürlich, zu raten, was das Problem sein könnte, aber wenn Sie eine Wette platzieren möchten, ist es fast 100% wahrscheinlich, dass es etwas ganz anderes ist. Profiling kann das Problem finden. Dies ist die Methode, die ich verwende.

    
Mike Dunlavey 12.10.2009 20:13
quelle
4

Gemeinsame Zeiger sind Referenzzähler. Insbesondere wenn Sie Multi-Threading verwenden, kann das Inkrementieren und Dekrementieren der Referenzzählung sehr viel Zeit in Anspruch nehmen. Der Grund dafür, dass Multithreading hier schmerzt, ist, dass if Sie einen gemeinsamen Zeiger zwischen Threads übergeben haben, der Verweiszähler würde zwischen diesen Threads geteilt werden, so dass jede Manipulation zwischen den Threads synchronisiert werden muss. Das kann die Dinge ziemlich verlangsamen.

Edit: Für diejenigen, die daran interessiert sind, wie viel langsamer Thread-Interlocking einige ziemlich einfache Operationen machen kann, sehen Sie Herb Sutter Tests mit ein paar Implementierungen von CoW Strings . Obwohl seine Tests nicht perfekt sind (z. B. er hat nur unter Windows getestet), gibt es dennoch eine Vorstellung davon, welche Art von Verlangsamung Sie erwarten können. Für die meisten praktischen Zwecke kann / könnte man sich eine CoW-Zeichenfolge als etwas wie shared_ptr<charT> vorstellen, wobei viele (irrelevante) Elementfunktionen hinzugefügt werden.

    
Jerry Coffin 12.10.2009 18:01
quelle
3

Sehr unwahrscheinlich - Sie würden die meiste Zeit damit verbringen müssen, Zeiger herumzureichen.

Der Effekt von shared ptrs ist normalerweise gering und es ist schwierig, einen Kantenfall zu konstruieren, in dem sie zu einem Problem werden (vorausgesetzt, eine geeignete Implementierung und ein richtig optimierender Compiler).

Auswirkungen von shared ptr:

  • erhöhte Zuordnungsgröße.
    Das würde nur dann eine Rolle spielen, wenn Sie viele geteilte Zeiger auf sehr kleine Objekte haben (z. B. Dutzende Millionen von shared_ptr<int> ) und / oder in der Nähe eines Speicherlimits arbeiten. Es gibt ein geringes Potenzial für eine bemerkenswerte Leistungsreduzierung, wenn die zusätzlichen Zuweisungen eine Cache / NUMA-Ebene innerhalb einer inneren Schleife überschreiten.

  • erhöhte Anzahl von Zuordnungen
    shared_ptr allcoates ein Tracking-Objekt (Referenzzählung, schwache Zählung und Deleter). Dies setzt den Heap unter Druck und kann zu einer allgemeinen Verlangsamung führen, wenn Sie eine hohe Gesamtzahl von Zuweisungen und Deallokationen haben.
    Kann vermieden werden, indem make_shared verwendet wird, indem Referent und trackng-Objekt in eine einzige Zuweisung eingefügt werden

  • Referenzzählung
    erhöht die Kosten einer Kopie eines Zeigers. In einer Single-Thread-Anwendung würden Sie bemerken, dass nur Sie die meiste Zeit Zeit damit verbringen, Zeiger zu kopieren. In einer Multithread-Anwendung müssen Sie immer noch hohe Konflikte mit den gleichen Zeigern haben.
    Kopierkosten können an vielen Stellen vermieden werden, indem ein shared_ptr<T> const & z. als Funktionsargument.

  • Dereferenzierung
    Die zusätzlichen Dereferenzierungskosten sind Null in einem Release-Build eines optimierenden Compilers. Debug-Builds entsprechen oft Funktionsaufrufen und zusätzlichen NULL-Prüfungen. Vor allem in Debug-Builds müssen Sie jedoch die meiste Zeit Dereferenzierungszeiger verwenden, um einen Unterschied zu machen.

Ohne zusätzliche Informationen können wir Ihnen nicht helfen. Sie müssen beschreiben, was die "Leistungsprobleme" sind (allgemeine Schwerfälligkeit, bestimmte Vorgänge, die lange dauern, viel Austausch) und einige Schlüsselzahlen - was Ihre App macht, wie viele intelligente Zeiger vorhanden sind, wie oft sie kopiert werden, und welche anderen Operationen Sie ausführen, besindse mit intelligenten Zeigern jonglieren.

Oder Sie lernen, Leistungsmonitor und / oder einen Profiler zu verwenden, um herauszufinden, was die Verlangsamungen verursacht und ob es bestimmte Engpässe gibt.

    
peterchen 12.10.2009 18:36
quelle
0

Erraten Sie nicht die Leistung: Profil Ihren Code.

    
T.E.D. 12.10.2009 21:05
quelle
0

Eine Sache, die die Leistung beeinträchtigen könnte, ist die übermäßige Weitergabe von shared_ptr als Funktionsparameter. Eine Lösung dafür wäre, Referenzen auf shared_ptr zu übergeben. Dies ist jedoch Mikro-Optimierung, also tun Sie es nur, wenn es wirklich benötigt wird

bearbeiten: Wenn man darüber nachdenkt, gibt es bessere Möglichkeiten zur Optimierung:

  • Wenn Sie den Zeiger übermäßig bewegen, sollten Sie das Objekt wahrscheinlich etwas tun lassen, anstatt es zu ziehen.
  • Sie können anstelle des Zeigers
  • eine (const) Referenz auf das Objekt übergeben
  • Übergeben Sie einen Verweis auf den Zeiger, wenn der Zeiger geändert werden muss
stefaanv 12.10.2009 20:25
quelle