Hallo zusammen,
ich beschäftige mich gerade etwas mit den Grundlagen, Thema Boxing - unboxing.
Simpler Code:
int iTest = 5;
Console.WriteLine("Dies ist die Ausgabe:" + iTest);
Die Variable iTest ist zuerst ein primitiver Datentyp vom Typ integer mit genau 32 Bit.
Durch die CW-Anweisung wird die Variable iTest durch boxing in ein Object umgewandelt und die Funktion ToString() ausgeführt, anschließend wird der String mit dem vorstehenden Text angehängt (concat).
Dies ist schön im Disassembler zu sehen:
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: stloc.0
IL_0003: ldstr "Dies ist die Ausgabe:"
IL_0008: ldloc.0
IL_0009: box [mscorlib]System.Int32
IL_000e: call string [mscorlib]System.String::Concat(object,
object)
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: nop
IL_0019: ret
Zuerst der einfache Datentyp, dann mit der Anweisung box in ein Object umgewandelt. Anschließend wird String.Concat ausgeführt.
Zweites Beispiel:
int iTest = 5;
Console.WriteLine("Dies ist die Ausgabe:" + iTest.ToString());
Hier wird direkt ToString() ausgeführt und somit kein boxing vorgenommen:
.locals init ([0] int32 iTest)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: stloc.0
IL_0003: ldstr "Dies ist die Ausgabe:"
IL_0008: ldloca.s iTest
IL_000a: call instance string [mscorlib]System.Int32::ToString()
IL_000f: call string [mscorlib]System.String::Concat(string,
string)
IL_0014: call void [mscorlib]System.Console::WriteLine(string)
IL_0019: nop
IL_001a: ret
Es wird die Methode String.Concat mit zwei Strings anstatt Objects ausgeführt.
Jetzt frage ich mich:
Warum ist das so? Ein primitiver Datentyp hat keine Methode Tostring(), daher wird dieser doch implizit auch in object umgewandelt???
ist das tatsächlich der bessere (performance) Weg?
Kann mir das jemand erklären? 🙂
Vielen Dank
Du übersiehst, wie Concat funktioniert:
//string.cs-Quellcode
return Concat(arg0.ToString(), arg1.ToString());
Mit anderen Worten drehst du nur die Reihenfolge herum, in der ToString() aufgerufen wird.
LaTino
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
Dann ist also zwischen den zwei Beispielen kein Unterschied was die Performance betrifft?
Beim zweiten wird ja nicht "geboxt" und dadurch Rechenzeit gespart?
Also beim zweiten Beispiel wird wohl nicht geboxt:Boxing/Unboxing
Fall 1:
int32 -> (box) object -> (unbox) int32 -> ToString
Fall 2:
int32 -> ToString
Soweit der Ausführungsplan im Quellcode und in der IL (die du gepostet hast). Was nun ausgeführt wird, liegt aber an der CLR und deren Implementierung, da speziell am JIT-Compiler. Du wirst feststellen, dass dabei durchaus unterschiedliche Dinge herauskommen können. EDIT: wahrer Satz, aber nicht für boxing/unboxing.
Boxing/Unboxing ist jedenfalls kein wirkliches Performance-Thema, sondern eins, das mehr das Vorgehen und die Muster der Programmiersprache beeinflusst. Performancevergleiche ergeben da nahezu null Sinn.
LaTino
EDIT: hab mal in der spec nachgeschaut. Im Wesentlichen kostet Boxing eine extra-Kopieraktion in den Heap, und Unboxing eine cmp-Aktion. Ein Szenario, in dem das der allerletzte Hebel ist, an dem man noch optimieren kann, fällt mir nicht ein. Klingt ein bisschen nach D. Knuth:
Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
Hallo,
bei solchen primitiven Dingen würde ich mir an Deiner Stelle über Performance gar keine Gedanken machen, sondern mich darauf verlassen, dass der Compiler das richtig optimiert.
Alles andere fällt in den Bereich PMO (pre mature optimization -> the root of all evil 😉 )
Mit anderen Worten: Könnte man hier viel Rechenzeit sparen, würde der Compiler IMO an der Stelle die performantere Variante generieren.
Gruß, MarsStein
Edit: Dazu kommt, wie LaTino richtig anmerkt, auch noch die Tatsache, dass der JITter auch noch ins Spiel kommt. Was der letztlich daraus macht... ist seine Sache.
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
Trotz vernachlässigbarer Performancesteigerungen ein sehr interessantes Thema, vielen Dank.
Es gibt halt 2 Concat Methoden: Concat( string, string )
und Concat( object, object )
Wenn jetzt 2 strings verbunden werden sollen, dann geht eben die erste Methode. Wenn nicht, dann muss eben die zweite Methode ran, wo dann eventuell geboxt wird.
"foo" + "bar"
=> 2 strings => Methode 1
"foo" + intBar
=> leider keine 2 strings => Methode 2 mit boxing
"foo" + intBar.ToString()
=> 2 strings => Methode 1
Eigentlich recht logisch