webentwicklung-frage-antwort-db.com.de

Schnellste C++ - Karte?

Korrigieren Sie mich, ich irre mich, aber std :: map ist eine geordnete Map. Jedes Mal, wenn ich einen Wert eingebe, verwendet die Map einen Algorithmus, um ihre Elemente intern zu sortieren, was einige Zeit in Anspruch nimmt.

Meine Anwendung erhält Informationen zu einigen Elementen in einem konstanten Intervall.

Diese App hält eine Karte, die wie folgt definiert ist:

::std::map<DWORD, myItem*>

Zunächst werden alle Artikel in der App als "neu" eingestuft. Ein "Item" -Objekt wird dieser Map zugewiesen und hinzugefügt, wobei seine ID und ein Zeiger darauf zugeordnet werden.

Wenn es sich nicht um ein "neues" Objekt handelt (nur um eine Aktualisierung dieses Objekts), sollte meine App das Objekt unter Verwendung der angegebenen ID auf der Karte finden und aktualisieren.

Meistens bekomme ich Updates.

Meine Frage ist:
Gibt es eine schnellere Kartenimplementierung oder sollte ich diese weiterhin verwenden?
Benutze ich besser eine ungeordnete Karte?

22
Poni

Kann ich besser unordered_map verwenden?

Möglicherweise.

std:map bietet eine konsistente Leistung bei O (log n), da es als ausgeglichener Baum implementiert werden muss. Aber std:unordered_map wird als Hash-Tabelle implementiert, die Ihnen O(1) Performance (gute Hash-Funktion und Verteilung der Schlüssel auf Hash-Buckets) verleiht, aber es könnte auch O(n)sein. _ (alles in einem Hash-Bucket und wird in eine Liste umgewandelt). Normalerweise würde man zwischen diesen Extremen etwas erwarten.

So können Sie immer eine vernünftige Leistung (O (log n)) haben, oder Sie müssen sicherstellen, dass alles stimmt, um eine gute Leistung mit einem Hash zu erzielen.

Wie bei jeder solchen Frage: Sie müssen messen, bevor Sie sich für einen Ansatz entscheiden. Wenn Ihre Datensätze nicht groß sind, stellen Sie möglicherweise keinen signifikanten Unterschied fest.

39
Richard

Wichtiger Hinweis: Wenn Sie nicht gemessen haben (und Ihre Frage deutet darauf hin, dass Sie dies nicht getan haben), beeinflusst die Kartenleistung die Anwendungsleistung erheblich (ein großer Prozentsatz der Zeit für das Durchsuchen und Aktualisieren der Karte) stört nicht beim Erstellen es schneller. Halten Sie sich an std::map (oder std::unordered_map oder eine beliebige verfügbare hash_map-Implementierung). Wenn Sie Ihre Anwendung um 1% beschleunigen, lohnt sich der Aufwand wahrscheinlich nicht. Machen Sie es fehlerfrei stattdessen.

Echoing Richards Antwort: Messen Leistung mit unterschiedlicher Kartenimplementierung unter Verwendung Ihrer realen Klassen und realen Daten.

Einige zusätzliche Hinweise:

  • Verstehen Sie den Unterschied zwischen den erwarteten Kosten (Hash-Karten haben normalerweise niedrigere Werte), Worst-Case-Kosten (O (logn) für symmetrischen Binärbaum, aber viel höher für Hash-Karten, wenn Insert eine Neuzuordnung des Hash-Arrays auslöst) und amortisierten Kosten (Gesamtkosten dividiert durch Anzahl) von Operationen oder Elementen; hängt von Dingen wie Verhältnis von neuen und vorhandenen Elementen ab). Sie müssen herausfinden, was in Ihrem Fall mehr Zwang gibt. Zum Beispiel kann die Neuzuweisung von Hash-Maps zu viel sein, wenn Sie eine sehr niedrige Latenzgrenze einhalten müssen.

  • Finden Sie heraus, wo echte Engpässe liegen. Es kann sein, dass die Kosten für die Suche in einer Karte im Vergleich zu z. IO Kosten.

  • Versuchen Sie eine speziellere Kartenimplementierung. Zum Beispiel kann viel gewonnen werden, wenn Sie etwas mehr über den Schlüssel der Karte wissen. Autoren von generischen Kartenimplementierungen verfügen nicht über solche Kenntnisse.

In Ihrem Beispiel (32-Bit-Ganzzahlschlüssel ohne Vorzeichen, die stark gebündelt sind, z. B. sequentiell zugewiesen werden), können Sie einen radix-basierten Ansatz verwenden. Sehr einfaches Beispiel (bedrohen Sie es als Illustration, nicht gebrauchsfertiges Rezept):

Item *sentinel[65536];  // sentinel page, initialized to NULLs.
Item (*pages[65536])[65536];  // list of pages,
                              // initialized so every element points to sentinel

Dann ist die Suche so einfach wie:

Item *value = pages[index >> 16][index & 0xFFFF];

Wenn Sie einen neuen Wert einstellen müssen:

if (pages[index >> 16] == sentinel) {
  pages[index >> 16] = allocate_new_null_filled_page();
}
pages[index >> 16][index & 0xFFFF] = value;
  • Optimieren Sie Ihre Kartenimplementierung.

    • Z.B. Jeder hash_map möchte die ungefähre Anzahl von Elementen im Voraus kennen. Es hilft, unnötige Neuzuweisungen von Hashtabellen und (möglicherweise) das Wiedereinspielen aller Schlüssel zu vermeiden. 

    • Mit meinem speziellen Beispiel oben würden Sie sicherlich andere Seitengrößen oder eine dreistufige Version ausprobieren.

    • Die allgemeine Optimierung bietet einen speziellen Speicherzuweiser, um die Mehrfachzuweisung kleiner Objekte zu vermeiden.

9

Wann immer Sie Artikel einfügen oder löschen, kostet die Speicherzuordnung freigabe viel. Stattdessen können Sie einen Allokator wie diesen verwenden:/- https://github.com/moya-lang/Allocator , der std :: map zweimal beschleunigt, wie der Autor sagt, aber ich fand es besonders für andere STL-Container noch schneller .

0
no one special