Laden...

String (Un)Boxing: Wird prim. Datentyp in object gewandelt-Kann toString() aufrufen? + Performance

Erstellt von schuppsl vor 7 Jahren Letzter Beitrag vor 7 Jahren 1.496 Views
S
schuppsl Themenstarter:in
789 Beiträge seit 2007
vor 7 Jahren
String (Un)Boxing: Wird prim. Datentyp in object gewandelt-Kann toString() aufrufen? + Performance

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:

  1. Warum ist das so? Ein primitiver Datentyp hat keine Methode Tostring(), daher wird dieser doch implizit auch in object umgewandelt???

  2. ist das tatsächlich der bessere (performance) Weg?

Kann mir das jemand erklären? 🙂
Vielen Dank

3.003 Beiträge seit 2006
vor 7 Jahren

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)

S
schuppsl Themenstarter:in
789 Beiträge seit 2007
vor 7 Jahren

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

3.003 Beiträge seit 2006
vor 7 Jahren

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)

3.170 Beiträge seit 2006
vor 7 Jahren

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

S
schuppsl Themenstarter:in
789 Beiträge seit 2007
vor 7 Jahren

Trotz vernachlässigbarer Performancesteigerungen ein sehr interessantes Thema, vielen Dank.

D
985 Beiträge seit 2014
vor 7 Jahren

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