Welcher STL-Container passt am besten zu meinen Anforderungen? Ich habe im Grunde einen 10 Elemente breiten Container, in dem ich ständig push_back
neue Elemente pop_front
einbaue, während ich das älteste Element (ungefähr eine Million Mal) bin .
Ich verwende derzeit ein std::deque
für die Aufgabe, habe mich aber gefragt, ob ein std::list
effizienter wäre, da ich mich nicht neu zuweisen müsste (oder ich verwechsle ein std::deque
mit einem std::vector
?). Oder gibt es einen noch effizienteren Container für meinen Bedarf?
PS Ich brauche keinen wahlfreien Zugriff
std::deque
wird nicht neu zugewiesen. Es ist eine Mischung aus astd::list
und a,std::vector
bei der größere Teile als a zugewiesen werden, diestd::list
jedoch nicht wie a neu zugewiesen werdenstd::vector
.Antworten:
Da es eine Vielzahl von Antworten gibt, könnten Sie verwirrt sein, aber um es zusammenzufassen:
Verwenden Sie a
std::queue
. Der Grund dafür ist einfach: Es handelt sich um eine FIFO-Struktur. Sie wollen FIFO, Sie verwenden einestd::queue
.Es macht Ihre Absicht allen anderen und sogar Ihnen selbst klar. A
std::list
oderstd::deque
nicht. Eine Liste kann überall eingefügt und entfernt werden, was eine FIFO-Struktur nicht tun soll, und eine Liste kanndeque
an beiden Enden hinzugefügt und entfernt werden, was auch eine FIFO-Struktur nicht kann.Deshalb sollten Sie a verwenden
queue
.Nun haben Sie nach der Leistung gefragt. Denken Sie immer an diese wichtige Faustregel: Guter Code zuerst, Leistung zuletzt.
Der Grund dafür ist einfach: Menschen, die Leistung vor Sauberkeit und Eleganz anstreben, kommen fast immer als Letzte ins Ziel. Ihr Code wird zu einem Brei, weil sie alles Gute aufgegeben haben, um wirklich nichts daraus zu machen.
Wenn Sie zuerst guten, lesbaren Code schreiben, lösen sich die meisten Ihrer Leistungsprobleme von selbst. Und wenn Sie später feststellen, dass Ihre Leistung fehlt, können Sie jetzt ganz einfach einen Profiler zu Ihrem netten, sauberen Code hinzufügen und herausfinden, wo das Problem liegt.
Das alles
std::queue
ist nur ein Adapter. Es bietet die sichere Schnittstelle, verwendet jedoch innen einen anderen Container. Sie können diesen zugrunde liegenden Container auswählen, was ein hohes Maß an Flexibilität ermöglicht.Welchen zugrunde liegenden Container sollten Sie also verwenden? Wir wissen , dass
std::list
undstd::deque
sowohl die notwendigen Funktionen (push_back()
,pop_front()
, undfront()
), so wie entscheiden wir?Verstehen Sie zunächst, dass das Zuweisen (und Freigeben) von Speicher im Allgemeinen nicht schnell erledigt werden kann, da Sie zum Betriebssystem gehen und es auffordern müssen, etwas zu tun. A
list
muss jedes Mal, wenn etwas hinzugefügt wird, Speicher zuweisen und ihn freigeben, wenn es verschwindet.A
deque
hingegen teilt in Stücken zu. Es wird weniger häufig als a zugewiesenlist
. Stellen Sie sich das als Liste vor, aber jeder Speicherblock kann mehrere Knoten enthalten. (Natürlich würde ich vorschlagen, dass Sie wirklich lernen, wie es funktioniert .)Allein damit
deque
sollte a eine bessere Leistung erbringen, da es nicht so oft mit dem Gedächtnis zu tun hat. In Verbindung mit der Tatsache, dass Sie Daten mit konstanter Größe verarbeiten, muss diese wahrscheinlich nicht nach dem ersten Durchlauf der Daten zugeordnet werden, während eine Liste ständig zugeordnet und freigegeben wird.Eine zweite Sache zu verstehen ist die Cache-Leistung . Das Auslagern in den Arbeitsspeicher ist langsam. Wenn die CPU dies wirklich benötigt, kann sie das Beste aus dieser Zeit herausholen, indem sie einen Teil des Arbeitsspeichers in den Cache zurücknimmt. Da eine
deque
Zuweisung in Speicherblöcken erfolgt, führt der Zugriff auf ein Element in diesem Container wahrscheinlich dazu, dass die CPU auch den Rest des Containers zurückbringt. Jetzt sind alle weiteren Zugriffe auf diedeque
schnell, da sich die Daten im Cache befinden.Dies ist anders als bei einer Liste, bei der die Daten einzeln zugewiesen werden. Dies bedeutet, dass Daten über den gesamten Speicher verteilt werden können und die Cache-Leistung schlecht ist.
In Anbetracht dessen
deque
sollte a eine bessere Wahl sein. Aus diesem Grund ist dies der Standardcontainer bei Verwendung von aqueue
. Trotzdem ist dies immer noch nur eine (sehr) fundierte Vermutung: Sie müssen diesen Code profilieren, indem Siedeque
in einem Test undlist
im anderen einen verwenden, um wirklich sicher zu sein.Aber denken Sie daran: Lassen Sie den Code mit einer sauberen Oberfläche arbeiten, und sorgen Sie sich dann um die Leistung.
John äußert die Besorgnis, dass das Einwickeln eines
list
oderdeque
einen Leistungsabfall verursachen wird. Noch einmal, er oder ich können es mit Sicherheit sagen, ohne es selbst zu profilieren, aber es besteht die Möglichkeit, dass der Compiler die Aufrufe, die derqueue
macht, inline macht. Das heißt, wenn Sie sagenqueue.push()
, wird es wirklich nur sagenqueue.container.push_back()
, den Funktionsaufruf vollständig zu überspringen.Dies ist wiederum nur eine fundierte Vermutung, aber die Verwendung von a
queue
beeinträchtigt die Leistung nicht im Vergleich zur Verwendung des zugrunde liegenden Rohcontainers. Wie ich bereits sagte, verwenden Sie dasqueue
, weil es sauber, einfach zu bedienen und sicher ist und wenn es wirklich zu einem Problemprofil und Test wird.quelle
Auschecken
std::queue
. Es umschließt einen zugrunde liegenden Containertyp und der Standardcontainer iststd::deque
.quelle
queue
ist das ein Typ? Guter Code zuerst, Leistung später. Zur Hölle, die meiste Leistung entsteht durch die Verwendung von gutem Code.queue
würde das Entfernen der Sicherheitsschale die Leistung nicht steigern, wie ich bereits sagte. Sie haben eine vorgeschlagenlist
, die wahrscheinlich schlechter abschneiden wird.Wo Leistung wirklich wichtig ist, lesen Sie die Boost-Rundpufferbibliothek .
quelle
Eine Million ist wirklich keine große Zahl im Computer. Verwenden
std::queue
Sie, wie andere vorgeschlagen haben, a als erste Lösung. In dem unwahrscheinlichen Fall, dass es zu langsam ist, identifizieren Sie den Engpass mithilfe eines Profilers (raten Sie nicht!) Und implementieren Sie ihn mithilfe eines anderen Containers mit derselben Schnittstelle erneut.quelle
Warum nicht
std::queue
? Alles was es hat istpush_back
undpop_front
.quelle
Eine Warteschlange ist wahrscheinlich eine einfachere Schnittstelle als eine Deque, aber für eine so kleine Liste ist der Leistungsunterschied wahrscheinlich vernachlässigbar.
Gleiches gilt für die Liste . Es hängt nur von der Auswahl der gewünschten API ab.
quelle
Verwenden Sie a
std::queue
, aber beachten Sie die Leistungskompromisse der beiden StandardklassenContainer
.Standardmäßig
std::queue
befindet sich ein Adapter darüberstd::deque
. In der Regel ergibt dies eine gute Leistung, wenn Sie eine kleine Anzahl von Warteschlangen mit einer großen Anzahl von Einträgen haben, was wohl der häufigste Fall ist.Allerdings sei nicht blind für die Implementierung von std :: deque . Speziell:
"... Deques haben normalerweise große minimale Speicherkosten; eine Deque, die nur ein Element enthält, muss ihr vollständiges internes Array zuweisen (z. B. das 8-fache der Objektgröße in 64-Bit-libstdc ++; das 16-fache der Objektgröße oder 4096 Bytes, je nachdem, welcher Wert größer ist , auf 64-Bit-libc ++). "
Angenommen, ein Warteschlangeneintrag ist etwas, das Sie in die Warteschlange stellen möchten, dh relativ klein. Wenn Sie also vier Warteschlangen mit jeweils 30.000 Einträgen haben, ist die
std::deque
Implementierung die Option der Wahl. Wenn Sie dagegen 30.000 Warteschlangen mit jeweils 4 Einträgen haben, ist diestd::list
Implementierung höchstwahrscheinlich optimal, da Sie denstd::deque
Overhead in diesem Szenario niemals amortisieren .Sie werden viele Meinungen darüber lesen, wie Cache König ist, wie Stroustrup verknüpfte Listen hasst usw., und all das ist unter bestimmten Bedingungen wahr. Akzeptieren Sie es einfach nicht im blinden Glauben, da es in unserem zweiten Szenario ziemlich unwahrscheinlich ist, dass die Standardimplementierung ausgeführt
std::deque
wird. Bewerten Sie Ihre Nutzung und messen Sie.quelle
Dieser Fall ist so einfach, dass Sie einfach Ihren eigenen schreiben können. Hier ist etwas, das sich gut für Situationen mit Mikrocontrollern eignet, in denen die Verwendung von STL zu viel Platz beansprucht. Es ist eine gute Möglichkeit, Daten und Signale vom Interrupt-Handler an Ihre Hauptschleife zu übergeben.
quelle