Effiziente Akkumulation

7

Angenommen, ich habe einen Vektor von Strings und möchte sie über std :: accumulate verketten.

Wenn ich den folgenden Code verwende:

%Vor%

Ich kann ziemlich sicher sein, dass es eine temporäre Objektkonstruktion geben wird.

In dieser Antwort wird gesagt, dass der Effekt von std :: accumulate folgendermaßen angegeben wird:

  

Berechnet das Ergebnis durch Initialisierung des Akkumulators acc mit   Initialwert init und ändert es dann mit acc = acc + * i oder acc =   binary_op (acc, * i) für jeden Iterator i im Bereich [first, last] in   bestellen.

Also frage ich mich, was ist der richtige Weg, dies zu tun, um die unnötige temporäre Objektkonstruktion zu vermeiden.

Eine Idee war, das Lambda so zu ändern:

%Vor%

In diesem Fall dachte ich, dass ich eine effiziente Verkettung der Strings erzwinge und dem Compiler helfe (ich weiß, dass ich ) die unnötige Kopie weglassen, da dies (Pseudocode) entsprechen sollte:

%Vor%

und somit

%Vor%

Eine andere Idee war es,

zu verwenden %Vor%

Aber das würde wahrscheinlich zu etwas wie:

führen %Vor%

Was mir sehr verdächtig erscheint.

Was ist der richtige Weg dies zu schreiben, um das Risiko der unnötigen Erstellung von temporären Objekten zu minimieren? Ich interessiere mich nicht nur für std :: string, ich wäre glücklich, eine Lösung zu haben, die wahrscheinlich für jedes Objekt funktionieren würde, das Kopier- und Verschiebungskonstruktoren / Zuordnungen implementiert hat.

    
tach 29.10.2013, 16:40
quelle

4 Antworten

4

Versuchen Sie Folgendes

%Vor%

Vor diesem Aufruf ist es vielleicht sinnvoll,

anzurufen %Vor%     
Vlad from Moscow 29.10.2013, 16:47
quelle
11

Ich würde dies in zwei Operationen aufteilen, zuerst std::accumulate , um die Gesamtlänge der zu erstellenden Zeichenfolge zu erhalten, dann eine std::for_each mit einem Lambda, das die lokale Zeichenfolge aktualisiert:

%Vor%

Die gebräuchlichste Alternative ist die Verwendung von Ausdrucksvorlagen, die jedoch nicht in eine Antwort passen. Grundsätzlich erstellen Sie eine Datenstruktur, die die Operationen abbildet, aber nicht ausführt. Wenn der Ausdruck schließlich ausgewertet wird, kann er die Informationen, die er benötigt, im Voraus sammeln und verwenden, um den Platz zu reservieren und die Kopien zu erstellen. Der Code, der die Ausdrucksvorlage verwendet, ist schöner, aber komplizierter.

    
quelle
3

Die effiziente Verwendung von std::accumulate ohne redundante Kopien ist nicht offensichtlich Zusätzlich dazu, dass der akkumulierende Wert neu zugewiesen und in und aus dem Lambda übergeben wird, kann er intern durch die Implementierung kopiert werden.
Beachten Sie außerdem, dass std::accumulate() selbst den Anfangswert by-value verwendet , einen copy-ctor aufrufen und somit% code% s ignorieren, das auf der Quelle der Kopie ausgeführt wurde (wie in einigen der anderen Antworten vorgeschlagen).

Der effizienteste Weg, die Strings zu verketten, ist folgender:

%Vor%

Das Ergebnis wird in reserve() angezeigt.
Diese Implementierung enthält keine redundanten Kopien, Verschiebungen oder Neuzuordnungen.

    
Adi Shavit 14.09.2016 15:23
quelle
1

Dies ist ein bisschen schwierig, da zwei Operationen sind, der Zusatz und die Zuordnung. Um die Kopien zu vermeiden, Sie müssen die Zeichenfolge im Zusatz und ändern Stellen Sie sicher, dass die Zuweisung ein No-Op ist. Es ist der zweite Teil Das ist schwierig.

Was ich gelegentlich getan habe, ist einen benutzerdefinierten "Akkumulator" zu erstellen, in Anlehnung an:

%Vor%

Beim Aufruf von accumulate und von Rückgabewert, aber während der tatsächlichen Akkumulation nichts. Gerade Aufrufen:

%Vor%

(Wenn Sie sich wirklich Gedanken über die Leistung machen, können Sie hinzufügen ein Kapazitätsargument für den Konstruktor von Accu , so dass es möglich ist Führen Sie reserve für die Member-Zeichenfolge aus. Wenn ich das täte, würde ich schreiben Sie wahrscheinlich auch den Kopierkonstruktor, um das sicherzustellen Die Zeichenfolge im kopierten Objekt hatte die erforderliche Kapazität.)

    
James Kanze 29.10.2013 18:26
quelle