Welche container zum speichern von eindeutigen Werten?
Ich habe Folgendes problem. Ich habe ein Spiel läuft durchschnittlich 60 frames pro Sekunde. Jeder frame muss ich zum speichern von Werten in einen container und es dürfen keine Duplikate.
Es wahrscheinlich zu speichern hat weniger als 100 Elemente pro Bild, aber die Anzahl von insert-Aufrufe werden sehr viel mehr (und vielen abgelehnt, weil es muss eindeutig sein). Erst am Ende des Rahmens brauche ich, um die traverse container. So etwa 60 Iterationen der container pro Rahmen, aber viel mehr Einfügungen.
Beachten Sie die Elemente, die zu speichern sind einfache integer.
Gibt es eine Reihe von Containern, die ich nutzen kann, aber ich kann nicht machen meiner Meinung nach was zu Holen. Leistung ist die zentrale Frage für diese.
Einige pros/cons, die ich gesammelt habe:
Vektor
- (PRO): Contigous-Speicher, eine große Rolle.
- (PRO): Speicher reserviert werden kann zunächst, nur sehr wenige Zuweisungen/deallocations danach
- (CON): Keine alternative, als um die traverse container (std::find) jedes insert() zu finden, eindeutige Schlüssel? Der Vergleich ist einfach, aber (ganze zahlen) und der ganze container können wahrscheinlich passen die cache
set
- (PRO): Einfach, klar bedeutete für diese
- (CON): Nicht-Konstante einfügen-Zeit
- (CON): eine Menge von Zuweisungen/deallocations pro frame
- (CON): Nicht contigous-Speicher. Traversieren einer Reihe von Hunderten von Objekten bedeutet springen um eine Menge im Speicher.
unordered_set
- (PRO): Einfach, klar bedeutete für diese
- (PRO): Mittlerer Fall konstanter Zeit einfügen
- (CON): Sehen, wie ich es speichern Ganzzahlen, hash-operation wird wahrscheinlich viel teurer als alles andere
- (CON): eine Menge von Zuweisungen/deallocations pro frame
- (CON): Nicht contigous-Speicher. Traversieren einer Reihe von Hunderten von Objekten bedeutet springen um eine Menge im Speicher.
Ich bin stützte sich auf, geht die vector-route, da der memory-access-patterns, obwohl eingestellt ist eindeutig gemeint für dieses Problem. Das große Problem ist, dass mir unklar ist, ob das Durchlaufen des Vektors, der für jedes insert ist teurer als die Zuweisungen/deallocations (vor allem wenn man bedenkt, wie oft dies getan werden muss) und die Speicher-lookups von set.
Ich weiß, letztendlich kommt es darauf an das profiling jeden Fall, aber wenn es nichts anderes als ein Vorsprung oder nur theoretisch, was würde wohl am besten in diesem Szenario? Gibt es irgendwelche vor - /Nachteile, die ich vielleicht übersehen habe aswell?
EDIT: Wie ich nicht erwähnen, wird der container gelöscht wird() am Ende von jedem Rahmen
unordered_set
ist die - Klassiker "set" - container, mit ungeordneten,-keine-doppelten Semantik und beste asymptotische Komplexität, ich würde give it a shot, aber die Chancen sind vector
schlagen wird es für kleine Gebindegrößen, da es weit bessere cache-Lokalität Eigenschaften.Was über die Bereitstellung Ihrer eigenen allocator, dass in der Lage ist, zu überwinden, die Ineffizienz in der Speicherverwaltung? (z.B. Bereitstellung einer Objekt-pool)
Was auch immer Sie tun, versuchen Sie, richtig zu Kapseln Sie den code und verwenden
auto
zu track-Typen, so können Sie leicht ändern Sie Ihre Wahl der Behälter in die Zukunft. Dann Messen.Wenn Sie wissen, welchen Bereich der Werte werden gespeichert in dem Vektor-Sie könnten Sie dort eigene Vektor und verwenden Sie dann random_suffle auf den container und nehmen Sie nur die ersten 100 Elemente aus.
Ein
set
wird wohl dominiert von Zweig mispredictions. Die kompletten Daten passt in L1 und Zuordnungen vermieden werden können (block-Zuweisung). Allerdings set
ist eine Baum-Struktur, so dass mehrere Filialen mit eine ~50% chance misprediction pro legen Sie unvermeidbar sind.InformationsquelleAutor KaiserJohaan | 2015-02-27
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich werde meinen Hals auf den block hier und vorschlagen, dass die Vektor-route ist wohl am effizientesten, wenn die Größe 100 und die Objekte, die gespeichert werden Integrale Werte. Der einfache Grund HIERFÜR ist, dass set und unordered_set Speicher für jedes insert in der Erwägung, dass der Vektor braucht nicht mehr als einmal.
Können Sie erhöhen suchen performance dramatisch halten Sie die vector bestellt, da dann alle durchsucht werden können, die binäre Suche und daher vollständig in log2N Zeit.
Der Nachteil ist, dass die Einsätze nehmen einen winzigen Bruchteil mehr durch die Erinnerung bewegt, aber es klingt so, als ob es viel mehr sucht als Beilagen, und bewegen (Durchschnitt) 50 zusammenhängenden Speicher Worten ist eine fast sofortige operation.
Letzte Wort:
Schreiben Sie die richtige Logik. Sorgen über die performance, wenn sich die Benutzer beschweren.
BEARBEITEN:
Denn ich konnte mir nicht helfen, hier ist eine einigermaßen vollständige Umsetzung:
flat_set
. Es hat auch eineflat_map
.Guter Punkt! Ich bin überrascht, es hat mir nicht vorkommen, zu sehen im ersten Schub. Da c++14 scheine ich vergessen zu haben, wie die Verwendung von boost...
InformationsquelleAutor Richard Hodges
Habe ich das timing mit ein paar verschiedene Methoden, die ich dachte, waren wahrscheinlich Kandidaten. Mit
std::unordered_set
war der Gewinner.Hier sind meine Ergebnisse:
Timing basiert auf dem median der fünf Läufe, für jeden Fall.
Code:
InformationsquelleAutor Vaughn Cato
Als Sie sagten, Sie haben viele Einfüge-und nur eine-traversal, würde ich empfehlen ein Vektor, und drücken Sie auf die Elemente, unabhängig davon, ob Sie sind einzigartig in dem Vektor. Dies geschieht in
O(1)
.Nur, wenn Sie müssen gehen Sie durch den Vektor, dann Sortieren Sie und entfernen Sie doppelte Elemente. Ich glaube, dass dies getan werden kann, in
O(n)
wie Sie sind begrenzt Ganzzahlen.BEARBEITEN: Sortieren in linearer Zeit durch zählen, Sortieren präsentiert in dieses video. Wenn nicht machbar, dann sind Sie wieder zu
O(n lg(n))
.Haben Sie sehr wenig cache-miss, weil die Kontiguität des Vektors im Speicher, und nur sehr wenige Zuweisungen (vor allem, wenn Sie reservieren, genügend Arbeitsspeicher in den Vektor).
InformationsquelleAutor qdii