std::map und performance, schneidende Sätze
Ich bin kreuzenden einige Sätze von zahlen, und tun dies durch das speichern einer Anzahl von jedes mal, wenn ich eine Zahl sehen, die in einer Karte.
Ich finde die Leistung sehr langsam.
Details:
- Eines der sets hat 150.000 zahlen
- Der Schnittpunkt der dass-Satz und ein weiterer Satz dauert etwa 300ms ersten mal, und über 5000ms zweiten mal
- Ich habe nicht getan, alle Profilerstellung noch, aber jedes mal, wenn ich Pause den debugger während der Kreuzung seine in malloc.c!
So, wie kann ich diese verbessern die Leistung? Schalten Sie auf eine andere Datenstruktur? Einige, wie die Verbesserung der Zuweisung von Speicher-performance der map?
Update:
- Gibt es eine Möglichkeit, zu Fragen, std::map oder
boost::unordered_map zu pre-allocate
etwas Platz? - Oder gibt es irgendwelche Tipps, wie Sie mit diesen effizient?
Update2:
Sehen Schnelle C++ - container wie die C# HashSet<T> und Dictionary<K V>?
Update3:
Ich ein Benchmark set_intersection und bekam schreckliche Ergebnisse:
(set_intersection) Found 313 values in the intersection, in 11345ms
(set_intersection) Found 309 values in the intersection, in 12332ms
Code:
int runIntersectionTestAlgo()
{
set<int> set1;
set<int> set2;
set<int> intersection;
//Create 100,000 values for set1
for ( int i = 0; i < 100000; i++ )
{
int value = 1000000000 + i;
set1.insert(value);
}
//Create 1,000 values for set2
for ( int i = 0; i < 1000; i++ )
{
int random = rand() % 200000 + 1;
random *= 10;
int value = 1000000000 + random;
set2.insert(value);
}
set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), inserter(intersection, intersection.end()));
return intersection.size();
}
- Ich empfehle Sie profile erstellen, um mehr Informationen sammeln. Wenn Sie die große Datenstrukturen, die malloc-Sache ist eine Möglichkeit.
- Wenn ich kommentieren die Einsätze und suchen in der Karte und drehen Sie einfach durch meine sets geht die Zeit Weg nach unten. 90% der Zeit damit verbracht wird, in der Karte. Ich habe versucht, schalten auf boost::unordered_map, Gleiches Ergebnis.
- HI Alex, also, was war deine Finale version ? Immer noch mit std::set ? Oder eine sortierte Vektor ? [Interesse] Danke.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Sollten Sie auf jeden Fall mit Feste Vektoren, die den Weg schneller. Das problem mit machen set Kreuzung mit der stl-sets ist, dass jedes mal, wenn Sie verschieben, um das nächste element, das Sie jagen einen dynamisch zugewiesenen Zeiger, das konnte einfach nicht sein, in Ihrer CPU-caches. Mit einem Vektor das nächste element wird oft im cache, weil es körperlich sehr nahe zu dem vorherigen element.
Den trick mit Vektoren, ist, dass wenn Sie nicht preallocate die Erinnerung für eine Aufgabe wie diese, werde es durchführen NOCH SCHLIMMER, denn es gehe auf die Neuzuweisung von Speicher, wie es ändert sich während Ihrer Initialisierung Schritt.
Versuchen, so etwas wie dieses, nur - es wird schneller.
Ohne zu wissen, mehr über Ihr problem, "check mit eine gute profiler" ist der beste general beraten, ich geben kann. Darüber hinaus...
Wenn der Speicher dein problem ist, wechseln Sie zu einer Art von Pool-Zuweisung, reduziert Aufrufe von
malloc
. Boost hat eine Reihe von benutzerdefinierten allocators, der sollte kompatibel sein mitstd::allocator<T>
. In der Tat, Sie können sogar versuchen, diese vor-profiling, wenn Sie haben bereits bemerkt debug-break-Proben, an deren Ende immer inmalloc
.Wenn Ihre Zahl-Raum ist bekannt, dicht sein, können Sie wechseln, um mit einem
vector
- oderbitset
-basierte Implementierung, Verwendung der zahlen als Indizes im Vektor.Wenn Ihre Zahl-Raum ist meist karg, aber hat einige Natürliche clustering (dies ist ein großer wenn), Sie können wechseln, um ein map-of-Vektoren. Verwenden höherwertigen bits für die map-Indizierung, und lower-order bits für die Vektor-Indizierung. Dies ist funktionell sehr ähnlich einfach mit einem Pool-Zuweisung, aber es ist wahrscheinlich, um Ihnen eine bessere caching-Verhalten. Dies macht Sinn, da Sie die Bereitstellung von mehr Informationen, um die Maschine (clustering ist die explizite und cache-freundlich, eher als eine zufällige Verteilung, die man erwarten würde von der pool-Zuweisung).
Ich würde den zweiten Vorschlag, um Sie zu Sortieren. Es gibt bereits STL-set-algorithmen arbeiten auf sortierten Bereichen (wie set_intersection, set_union, etc):
set_intersection
Ich verstehe nicht, warum Sie noch auf der Karte zu tun, Kreuzung. Wie die Leute gesagt haben, Sie könnten die Sätze in
std::set
's, und verwenden Sie dannstd::set_intersection()
.Oder Sie können Sie in
hash_set
's. Aber dann müsste man zur Umsetzung Kreuzung manuell: technisch Sie müssen nur eines der sets in einerhash_set
, und dann eine Schleife durch die andere, und zu testen, wenn jedes element enthalten ist, in derhash_set
.Kreuzung mit der Karten sind langsam, versuchen Sie, einen
hash_map
. (dies ist jedoch nicht in allen STL-Implementierung.Alternativ Sortieren Sie die beiden anzeigen, und tun es in einer merge-sort-wie Weg.
Was ist Ihr Schnittpunkt-Algorithmus? Vielleicht gibt es einige Verbesserungen gemacht werden?
Hier ist eine Alternative Methode
Ich weiß es nicht schneller oder langsamer, aber es könnte sein, etwas zu versuchen. Bevor Sie dies tun, empfehle ich auch mit einem profiler, um sicherzustellen, Sie arbeiten wirklich auf den hotspot. Ändern Sie die Sätze von zahlen, die Sie werden verschneiden zu verwenden
std::set<int>
statt. Dann Durchlaufen Sie die kleinsten sucht bei jedem Wert, den Sie finden. Für jeden Wert in den kleinsten Satz, verwenden Sie diefind
Methode, um festzustellen, ob die Anzahl in jedem der anderen Sätze (für die Leistung, Suche vom kleinsten zum größten).Dieser ist optimiert in dem Fall, dass die Nummer nicht gefunden in all den sets, also wenn die Schnittmenge ist relativ klein, kann es schnell sein.
Speichern Sie dann die Kreuzung in
std::vector<int>
statt - insertion mitpush_back
ist auch sehr schnell.Hier ist eine weitere Alternative Methode
Ändern Sie die Sätze von zahlen zu
std::vector<int>
und verwendenstd::sort
Sortieren vom kleinsten zum größten.verwenden Sie DannEigentlich nie daran, dass Sie können Sie dann einfach Durchlaufen und die Werte in lock-step, Blick auf diejenigen, die mit dem gleichen Wert. Inkrement nur die Iteratoren, die kleiner sind als der minimale Wert, den Sie sah, die Sie im vorhergehenden Schritt (wenn die Werte unterschiedlich waren).std::binary_search
zu finden, die Werte, mit ungefähr der gleichen Methode wie oben. Dies kann schneller sein als die Suche nach einemstd::set
da das array mehr dicht gepackt in den Speicher.Könnte Ihr Algorithmus. Wie ich es verstehe, Sie sind Spinnen über jeden Satz (was ich hoffe, ist ein standard-set), und wirft Sie in noch eine weitere Karte. Dies macht eine Menge Arbeit, Sie brauchen nicht zu tun, da die Tasten einer standard-set sind in sortierter Reihenfolge schon. Stattdessen nehmen Sie eine "merge-sort" wie Ansatz. Spin über jede iter, array-Dereferenzierung zu finden, die min. Zählen Sie die Zahl haben, die min, und erhöhen diese. Wenn die Zählung N, fügen Sie es zu der Kreuzung. Wiederholen Sie dies, bis die erste Karte, die trifft es am Ende (Wenn Sie vergleichen Sie die Größen, bevor Sie beginnen, werden Sie nicht haben, um zu überprüfen, jede Karte, die end-jeder Zeit).
Reagieren zu aktualisieren: Es existieren die Fakultäten, speed up memory allocation durch die vor-Reservierung von Raum, wie boost::pool_alloc. So etwas wie:
Aber ehrlich gesagt, malloc ist ziemlich gut in dem, was es tut; ich würde das Profil, bevor Sie etwas zu extrem.
Blick auf Ihre algorithmen, dann wählen Sie den richtigen Datentyp. Wenn Sie gehen, um set Verhalten, und wollen zu tun, Kreuzungen und dergleichen,
std::set
ist der container zu verwenden.Da es Elemente gespeichert sind, in einer geordneten Art und Weise, einsetzen kann, kostet Sie O(log N), aber Kreuzung mit einem anderen (nicht sortiert!)
std::set
getan werden kann in linearer Zeit.Ich etwas herausgefunden: wenn ich den debugger, um entweder zu RELEASE oder DEBUG-builds (z.B. F5 in der IDE), dann bekomme ich schreckliche Zeiten.