Verwendet die Methode StringBuilder Remove mehr Speichereffizienz als das Erstellen eines neuen StringBuilder in der Schleife?

8

In C #, was mehr Speichereffizienz bedeutet: Option # 1 oder Option # 2?

%Vor%     
Sixto Saez 05.11.2008, 22:11
quelle

10 Antworten

7

Einige der Antworten suggerierten sanft, dass ich aus meinem Duff herausgehe und es selbst herausfinden würde, also sind unten meine Ergebnisse. Ich denke, dass dieses Gefühl im Allgemeinen gegen den Strich dieser Website geht, aber wenn Sie etwas richtig gemacht wollen, könnten Sie auch tun ....:)

Ich habe die Option # 1 geändert, um den @Ty-Vorschlag zu nutzen, StringBuilder.Length = 0 anstelle der Remove-Methode zu verwenden. Dies machte den Code der beiden Optionen ähnlicher. Die zwei Unterschiede sind jetzt, ob der Konstruktor für den StringBuilder innerhalb oder außerhalb der Schleife ist und Option 1 verwendet nun die Length-Methode, um den StringBuilder zu löschen. Beide Optionen wurden so eingestellt, dass sie über ein outputStrings-Array mit 100.000 Elementen laufen, damit der Garbage Collector etwas Arbeit leistet.

Ein paar Antworten boten Hinweise, um die verschiedenen PerfMon Counter & amp; so und verwenden Sie die Ergebnisse, um eine Option auszuwählen. Ich recherchierte und nutzte den integrierten Performance Explorer der Visual Studio Team Systems Developer Edition, die ich bei der Arbeit habe. Ich habe den zweiten Blogeintrag einer mehrteiligen Serie gefunden, in der erklärt wird, wie man ihn erstellt hier . Grundsätzlich verbinden Sie einen Komponententest, um auf den Code zu zeigen, den Sie profilieren möchten. gehe durch einen Assistenten & amp; einige Konfigurationen; und starten Sie das Komponententest-Profiling. Ich habe die .NET-Objektzuordnung & amp; Lebensdauermesswerte Die Ergebnisse der Profilerstellung waren für diese Antwort schwer zu formatieren, deshalb habe ich sie am Ende platziert. Wenn Sie den Text kopieren und in Excel einfügen und ein wenig massieren, sind sie lesbar.

Option # 1 ist die effizienteste Speichereffizienz, da der Garbage Collector etwas weniger Arbeit verrichtet und dem StringBuilder-Objekt die Hälfte des Speichers und der Instanzen als Option # 2 zugewiesen wird. Für die tägliche Codierung ist die Auswahloption 2 völlig in Ordnung.

Wenn Sie immer noch lesen, habe ich diese Frage gestellt, weil Option # 2 die Speicherleck-Detektoren eines erfahrenen C / C ++ - Entwicklers ballistisch machen lässt. Ein großer Speicherverlust tritt auf, wenn die StringBuilder-Instanz vor der Neuzuweisung nicht freigegeben wird. Natürlich machen uns C # -Entwickler keine Sorgen über solche Dinge (bis sie aufspringen und uns beißen). Danke an alle !!

%Vor%     
Sixto Saez 07.11.2008, 21:48
quelle
6

Option 2 sollte (glaube ich) tatsächlich die Option 1 übertreffen. Der Aufruf von Remove "erzwingt", dass der StringBuilder eine Kopie der bereits zurückgegebenen Zeichenfolge erstellt. Die Zeichenfolge ist in StringBuilder tatsächlich änderbar, und StringBuilder nimmt keine Kopie an, es sei denn, dies ist erforderlich. Bei Option 1 wird kopiert, bevor das Array grundsätzlich gelöscht wird - bei Option 2 ist keine Kopie erforderlich.

Der einzige Nachteil von Option 2 ist, dass wenn der String lang ist, mehrere Kopien während des Anhängens gemacht werden - während Option 1 die ursprüngliche Größe des Puffers beibehält. Wenn dies jedoch der Fall ist, geben Sie eine Anfangskapazität an, um das zusätzliche Kopieren zu vermeiden. (In Ihrem Beispielcode ist die Zeichenfolge größer als die Standardzeichen. Wenn Sie sie mit einer Kapazität von beispielsweise 32 initialisieren, werden die zusätzlichen Zeichenfolgen reduziert.)

Abgesehen von der Leistung ist Option 2 jedoch sauberer.

    
Jon Skeet 05.11.2008 22:16
quelle
4

Während der Profilerstellung können Sie auch versuchen, die Länge des StringBuilders auf null zu setzen, wenn Sie die Schleife eingeben.

%Vor%     
Ty. 05.11.2008 23:49
quelle
2

Da Sie nur mit der Erinnerung beschäftigt sind, würde ich vorschlagen:

%Vor%

Die Variable namens output hat in Ihrer ursprünglichen Implementierung dieselbe Größe, aber keine anderen Objekte werden benötigt. StringBuilder verwendet Zeichenfolgen und andere Objekte intern und Sie werden viele Objekte erstellt, die GC'd werden müssen.

Beide Zeilen von Option 1:

%Vor%

Und die Zeile von Option 2:

%Vor%

erstellt ein unveränderliches Objekt mit dem Wert des Präfixes + outputString + postfix. Diese Zeichenfolge hat die gleiche Größe, egal wie Sie sie erstellen. Was Sie wirklich fragen, ist, was mehr Speichereffizienz ist:

%Vor%

oder

%Vor%

Das vollständige Überspringen des StringBuilders ist effizienter als die beiden anderen.

Wenn Sie wirklich wissen müssen, welche der beiden in Ihrer Anwendung effizienter ist (dies wird wahrscheinlich von der Größe Ihrer Liste, Präfix und outputStrings abhängen), würde ich den ANTS Profiler von Rot-Gate empfehlen Ссылка

Jason

    
Jason Hernandez 05.11.2008 23:41
quelle
1

Hasse es zu sagen, aber wie wäre es damit, es einfach zu testen?

    
Joel Lucsy 05.11.2008 22:19
quelle
1

Dieses Zeug ist leicht selbst zu finden. Führen Sie Perfmon.exe aus, und fügen Sie einen Zähler für .NET Memory + Gen 0 Collections hinzu. Führen Sie den Testcode eine Million Mal aus. Sie werden sehen, dass Option 1 die Hälfte der benötigten Anzahl von Sammlungen benötigt.

    
Hans Passant 05.11.2008 22:27
quelle
1

Wir haben schon einmal darüber mit Java gesprochen , hier ist's die [Release] -Ergebnisse der C # -Version:

%Vor%

Update: In meiner nicht-wissenschaftlichen Analyse, die die Ausführung der beiden Methoden während der Überwachung aller Speicherleistungsindikatoren in perfmon erlaubt, ergab sich bei keiner der Methoden ein erkennbarer Unterschied (abgesehen davon, dass einige Zähler nur während eines Tests eine Spitze aufwiesen) Ausführen).

Und hier ist, was ich getestet habe:

%Vor%

Option 1 in diesem Szenario ist geringfügig schneller, obwohl Option 2 leichter zu lesen und zu warten ist. Sofern Sie diese Operation nicht millionenfach hintereinander durchführen, würde ich bei Option 2 bleiben, weil ich annehme, dass Option 1 und 2 in einer einzigen Iteration ungefähr gleich sind.

    
cfeduke 05.11.2008 22:34
quelle
0

Ich würde Option # 2 sagen, wenn es auf jeden Fall einfacher ist. In Sachen Leistung klingt das nach etwas, das man nur testen und sehen muss. Ich nehme an, dass es nicht genug Unterschied macht, um die weniger unkomplizierte Option zu wählen.

    
bdukes 05.11.2008 22:13
quelle
0

Ich denke, dass Option 1 etwas mehr Speicher wäre, da ein neues Objekt nicht jedes Mal erstellt wird. Nachdem das gesagt wurde, erledigt der GC ziemlich gut die Ressourcen wie in Option 2.

Ich denke, dass Sie in die Falle einer vorzeitigen Optimierung geraten ( die Wurzel allen Übels - Knuth ) ). Ihre IO wird viel mehr Ressourcen benötigen als der String Builder.

Ich gehe eher mit der Clearer / Cleaner Option, in diesem Fall Option 2.

Rob

    
Robert Wagner 05.11.2008 22:31
quelle
0
  1. Messen Sie es
  2. Ordnen Sie so genau wie möglich vor, wie viel Speicher Sie benötigen
  3. Wenn Geschwindigkeit Ihre Präferenz ist, dann betrachten Sie eine ziemlich geradlinige multi-threaded Front zu Mitte, von Mitte zu Ende gleichzeitige Ansatz (erweitern Sie die Arbeitsteilung wie erforderlich)
  4. wiederhole es erneut

Was ist dir wichtiger?

  1. Speicher

  2. Geschwindigkeit

  3. Klarheit

Adam Straughan 06.11.2008 15:55
quelle