Die meisten effiziente Möglichkeit, Werte zuzuweisen, Karten
Welche Art und Weise Werte zuweisen einer Karte ist am effizientesten? Oder sind Sie optimiert für den gleichen code (in den meisten Compilern)?
//1) Assignment using array index notation
Foo["Bar"] = 12345;
//2) Assignment using member function insert() and STL pair
Foo.insert(std::pair<string,int>("Bar", 12345));
//3) Assignment using member function insert() and "value_type()"
Foo.insert(map<string,int>::value_type("Bar", 12345));
//4) Assignment using member function insert() and "make_pair()"
Foo.insert(std::make_pair("Bar", 12345));
(Ich weiß, ich könnte benchmark und prüfen, compiler-Ausgaben, aber diese Frage stellte sich nun, und das einzige, was ich in der Nähe bei der hand ist mein Handy... hehe)
- Ich würde Wetten auf
they are all the same
. Ich konnte erläutern, warum das eine schneller als das andere, aber das wäre nur, wenn wir Sie ignorieren compiler-Optimierungen. - eigentlich
4)
funktioniert,std::pair<std::string, int> p = std::make_pair("Bar", 12345);
da der Konstruktor derpair
ist Freizügig, die Art und Weise (wie lange, wie die Typen, die umgewandelt werden können, funktioniert es).
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ersten, es gibt semantische Unterschiede zwischen
[]
undinsert
:[]
wird ersetzen den alten Wert (wenn überhaupt)insert
wird ignorieren der neue Wert (wenn ein Alter Wert ist bereits vorhanden)daher ein Vergleich der beiden ist sinnlos im Allgemeinen.
In Bezug auf die Einsätze Versionen:
std::map<std::string, int>::value_type
iststd::pair<std::string const, int>
also keine (wichtigen) Unterschied zwischen 3 und 4std::make_pair("Bar", 12345)
ist billiger alsstd::pair<std::string, int>("Bar", 12345)
weil diestd::string
Typ ist eine vollwertige Klasse mit Operationen zu tun, kopieren und"Bar"
ist nur ein string ein literal ist (er also nur einen Zeiger kopieren); aber, da am Ende Sie tun müssen, erstellen Sie diestd::string
... es hängt von der Qualität Ihres CompilersAllgemein würde ich empfehlen:
[]
für die Aktualisierunginsert(std::make_pair())
für das ignorieren Duplikatestd::make_pair
ist nicht nur kürzer, es respektiert auch die TROCKEN-Leitlinie: "Wiederhole Dich nicht".Vollständigkeit halber, obwohl, der Schnellste (und einfachste) wäre
emplace
(C++11 fähigen):Sein Verhalten ist, dass der
insert
, aber es baut das neue element in place.[]
ersetzt den vorhandenen Wert. Das gleiche gilt für diese notation auf den meisten Datenstrukturen. So war ich denken, sich eine Karte rein und einfach einfügen einen einzelnen Wert. Aber da die anderen eigentlich tun verfolgen, ob oder nicht eine alte Wert vorhanden ist, würde ich vermuten, dass es einige zusätzliche generierte code für diesen Zweck. Damit unterscheiden Sie sich von[]
.insert
werden: 1) interne suchen-Funktionen, um den Baum Knoten, wenn vorhanden 2) falls vorhanden, zurück, die anderen 3) verwenden Sie das Ergebnis der Suche zum einfügen des neuen Wertes und Rückkehr. Eine Implementierung füroperator[]
wird genau die gleiche Sache mit Ausnahme 3) fügt eine Standard gebaut-Wert und gibt dass. (Sie überschreiben Sie dann, was zurückgegeben wurde.)std::map<std::string, int>::value_type
iststd::pair<const std::string, int>
, nichtstd::pair<std::string, int>
. Daher nur 3) hat einen Typ, der kann direkt eingesetzt werden, ohne einen zusätzlichen Typ-Konvertierung (oder die overhead -[]
). Dieser Aufwand könnte entfernt werden, indem der compiler, aber im Allgemeinen würde ich nicht rechnen.emplace
ist sogar noch besser -> in-place-Konstruktion nur, wenn notwendig.emplace
bei assoziativen Containern, so ist es vielleicht nicht eine option sein.std::string
, aber ich bin nicht sicher, ob Sie berechtigt sind, um aus einerconst std::string
, und das könnte bedeuten, dass 3) haben eine höhere Kosten-in eine C++11-compiler.1) kann etwas langsamer sein, als die anderen Methoden, weil
std::map::operator[]
erste Standard erstellt das Objekt, wenn es nicht bereits vorhanden ist, und gibt dann eine Referenz, die Sie verwenden könnenoperator=
auf den gewünschten Wert, d.h. es sind zwei Operationen.2-4) sollten gleichwertig sein, da
map::value_type
ist ein typedef aufstd::pair
für die gleichen Arten, und dahermake_pair
ist ebenfalls äquivalent. Sollte der compiler behandelt diese identisch.Beachten Sie auch, dass die Leistung weiter erhöht werden kann, wenn Sie brauchen, um beide zu überprüfen Existenz (z.B. zur Ausführung von speziellen Logik, je nachdem, ob es existiert oder nicht) und dann auch einfügen, indem
map::lower_bound
zuerst ein Hinweis, wo das element " sein sollte, somap::insert
nicht zu suchen haben, das ganzemap
wieder:mymap.key_comp()(key, hint->first)
überprüft, obkey
ist weniger alshint->first
. Wenntrue
bedeutet, dasskey
war nicht vorhanden (dalower_bound
gibt einen iterator auf das erste element nicht weniger).false
würde bedeuten, dasskey
ist identisch mithint->first
.lower_bound
gibt einen iterator auf ein bestehendes element, dass entweder gleich oder größer als das mitgeliefertekey
Bedeutunghint->first < key
würde immerfalse
; es gibt keine Notwendigkeit, dies zu überprüfen. Stattdessen nur die überprüfungkey < hint->first
erlaubt uns zu bestätigen, ob der zurückgegebene iterator gleich ((key < hint->first) == false
) oder größer ((key < hint->first) == true
), d.h. obkey
bereits in der Zuordnung vorhanden ist oder nicht. Dies deckt alle Fälle ab.Ihre erste Möglichkeit:
Foo["Bar"] = 12345;
hat etwas andere Semantik als die anderen -- es wird ein neues Objekt eingefügt, falls keine vorhanden ist (wie die anderen), aber überschreiben die aktuellen Inhalte, wenn keiner vorhanden ist (wo die anderen mitinsert
wird scheitern, wenn dieser Schlüssel ist bereits vorhanden).So weit wie Geschwindigkeit geht, es das Potenzial hat, langsamer zu sein als die anderen. Wenn Sie einfügen eines neuen Objekts, es hat ein paar mit dem angegebenen Schlüssel und ein default-konstruiert value_type, dann weisen Sie die richtige value_type danach. Alle anderen bauen das paar den richtigen Schlüssel und Wert, und legen Sie das Objekt. Fair, aber meine Erfahrung ist, dass der Unterschied selten signifikant (bei älteren Compilern, es war mehr von Bedeutung, aber mit neueren ziemlich minimal).
Den nächsten beiden sind äquivalent zu einander. Sie sind nur zwei verschiedene Möglichkeiten, um den Namen des gleichen Typs. Von run-time, da gibt es keinen Unterschied zwischen Ihnen allen.
Den vierten nutzt eine template-Funktion (make_pair), die theoretisch könnte um eine zusätzliche Stufe der Aufruf der Funktion. Ich wäre sehr überrascht, zu sehen, einen echten Unterschied von dieser aber, außer (möglicherweise) wenn Sie waren vorsichtig, um sicherzustellen, dass der compiler hat absolut keine - Optimierung (vor allem inlining) überhaupt.
Bottom line: Die erste wird oft ein wenig langsamer als der rest (aber nicht immer und nicht viel). Die anderen drei sind fast immer gleich (wie in: in der Regel erwarten, dass jeder vernünftige compiler zu produzieren identischen code für alle drei), obwohl es die theoretische Rechtfertigung für die vierte wird langsamer.
Wenn kein Objekt vorhanden ist, an diesem Ort, dann:
std::map::emplace
am effizientesten ist.insert
ist der zweite (aber sehr nahe).[]
ist die am wenigsten effiziente.[]
, wenn es kein Objekt gibt, triviale Konstrukte ein. Es ruft dannoperator=
.insert
macht ein copy-Konstruktor-Aufruf auf derstd::pair
argument.Jedoch, im Falle von Karten, die
map.insert( make_pair( std::move(key), std::move(value) ) )
wird in der Nähemap.emplace( std::move(key), std::move(value) )
.Wenn es ein Objekt am Ort, dann
[]
rufenoperator=
, währendinsert
/emplace
wird, löschen Sie das alte Objekt und erstellen Sie eine neue.[]
könnte leicht billiger in diesem Fall.In das Ende, es hängt davon ab, was Ihr
operator=
vs copy-Konstrukt vs trivial-Konstrukt vs Destruktor Kosten für Ihre Schlüssel und Wert.Die eigentliche Arbeit suchen, die Dinge in der Baum-Struktur des
std::map
so nah zu identisch es ist nicht lustig.Die Dritte ist die beste Wahl (IMHO), aber 2, 3 und 4 sind gleich.
Warum ich denke, die Dritte ist die beste Wahl: Sie sind die Durchführung nur eine Betrieb, den Wert einfügen: einlegen (gut, es ist eine Suche auch) und Sie kann wissen, ob der Wert eingefügt wurde, die überprüfung der
second
Mitglied der Rückgabewert und die Umsetzung gewährt nicht überschreiben Sie den Wert.Den Einsatz der
value_type
hat auch Vorteile: Sie brauchen nicht zu wissen, der zugeordnete Typ-oder Schlüssel-Typ, so ist es nützlich, mit template-Programmierung.Den schlimmsten Fall (IMHO) ist die erste:
Sind Sie aufrufen der
std::map::operator[]
wich erstellt ein Objekt und gibt einen Verweis darauf, das abgebildete Objektoperator =
genannt wird. Du machst zwei Operationen für das einfügen: zuerst die Einfügemarke, die zweite asignation.Und Sie haben ein weiteres problem: Sie wissen nicht, wenn der Wert wurde eingefügt oder überschrieben.
3)
schneller als2)
?std::pair<const std::string, int>
.Obwohl es hat schon ein paar gute Antworten schon dachte ich, ich könnte genauso gut tun, eine schnelle benchmark. Lief jeweils 5 Millionen mal verwendet c++11 ' s chrono zum Messen der Zeit, die es brauchte.
Hier ist der code:
Den Ausgang für 5 Millionen Iterationen ist:
GCC-version:
Meine Maschine:
EDIT1: Feste der code umgestrickt und der benchmark.
EDIT2: Hinzugefügt Matthieu M. s Vorschlag für C++11 emplace und er hat Recht, emplace ist am schnellsten
insert
Versionen wird hier eigentlich nie stecken, in der Erwägung, dass die[]
version immer zuordnen. Das ist also geschraubt, um mit zu beginnen...insert
nichts 2) erstellen einesstringstream
ist ziemlich teuer, so könnte dies Einfluss auf die Messungen.std::to_string
könnte eine geringere Auswirkung option (ich bin mir nicht ganz sicher, ob das einen Einfluss hat in Anbetracht der letzten mapsize, aber sicher besser sein).