Using primitive data types and causing boxing when using in strings in c#
Is there a boxing operation [performance dip] when I use something like this
Console.WriteLine("The age of the person is : "+age.ToString());
开发者_运维问答
Else, if I use this there is no boxing happenning,
Console.WriteLine("The age of the person is : {0}",age);
Since I need to avoid even minor performance dips, I like to know the best option. Also give me links for contents so that I can learn about the performance deterioration statements and how to overcome them.
(assuming age is an Int32 variable).
- The first shouldn't because Int32 overrides ToString
.
L_0019: ldloca.s age
L_001b: call instance string [mscorlib]System.Int32::ToString()
L_0020: call string [mscorlib]System.String::Concat(string, string)
L_0025: call void [mscorlib]System.Console::WriteLine(string)
- The second version would box because it resolves to WriteLine(String, object).
.
L_003b: ldstr "The age of the person is : {0}"
L_0040: ldloc.0
L_0041: box int32
L_0046: call void [mscorlib]System.Console::WriteLine(string, object)
A good way to check if you're unsure is to open up reflector and inspect a snippet. Look for a box instruction in the IL similar to L_0041.
However, from my limited benchmarking it seems the second version is faster by 10-15%. So as always - profile before you optimize.
Rather than randomly speculating (yes, I'm probably guilty of this, too), how about we measure it? Compile the following code in "Release" mode (i.e., with optimizations enabled):
class TestProgram
{
static void Main(string[] args)
{
int age = 32;
WriteWithConcat(age);
WriteWithFormat(age);
}
static void WriteWithConcat(int age)
{
Console.WriteLine("The age of the person is : " + age.ToString());
}
static void WriteWithFormat(int age)
{
Console.WriteLine("The age of the person is : {0}", age);
}
}
Then examine the resulting IL using a program like .NET Reflector or ILDASM (I have omitted the uninteresting methods below for brevity):
.method private hidebysig static void WriteWithConcat(int32 age) cil managed
{
.maxstack 8
L_0000: ldstr "The age of the person is : "
L_0005: ldarga.s age
L_0007: call instance string [mscorlib]System.Int32::ToString()
L_000c: call string [mscorlib]System.String::Concat(string, string)
L_0011: call void [mscorlib]System.Console::WriteLine(string)
L_0016: ret
}
.method private hidebysig static void WriteWithFormat(int32 age) cil managed
{
.maxstack 8
L_0000: ldstr "The age of the person is : {0}"
L_0005: ldarg.0
L_0006: box int32
L_000b: call void [mscorlib]System.Console::WriteLine(string, object)
L_0010: ret
}
And let's walk through the interesting parts to see what they do. For the first method (corresponding to the first line of sample code in the question), the following interesting things happen:
- A string object is created in memory.
- The
ToString()
method is called on the integer we specified. - The
String.Concat
method is called to concatenate the two string instances. - The
Console.WriteLine
overload that accepts a single parameter of type string is called to display the resulting string in the Console window.
For the second method (corresponding to the second line of sample code in the question), the following interesting things happen:
- A string object is created in memory.
- The integer we specified is boxed to an
Object
. - The
Console.WriteLine
overload that accepts two parameters (one of typestring
and the other of typeobject
) is called to format the string value, and then display it in the Console window.
So, to answer your question, the boxing actually happens for the second version, not the first version.
But wait... Does that mean the performance is necessarily better and that you should always prefer the first version over the second version? The truth is, not necessarily.
Just for fun, I ran a few speed tests, looping the single line of code in each of the above methods 100,000 times. It turns out that the second version actually edges out the first version ever so slightly.
I emphasize "ever so slightly" because it's really important here. See for yourself:
00:00:00:0001676 // time for the first method
00:00:00:0001381 // time for the second method
And that's with looping over 100,000 times! There's simply no way that this could ever be a bottleneck in any application. Not only that, but the underlying call to Console.WriteLine
is always going to be the slowest part of your code, and that gets called no matter which way we choose.
So the upshot is that you really should forget everything that we talked about above. You need to learn to trust your compiler. This question is premature optimization at its finest. Unless you know specifically that this particular line of code is slowing down your application, do not waste any time optimizing it! In the overwhelming majority of cases, the compiler is smart enough to generate the best, most optimized IL, regardless of which of various equivalent syntax you choose to write.
And as a corollary to that, if you can't trust your compiler, trust your JITter. The JIT compiler is even smarter, and performs a whole host of optimizations that I can't even begin to describe in this answer. The upshot is that wasting time thinking about questions like this one is pointless, and that's time that could be better spent writing code.
Write code like this for human beings, not for the computer. Write code that is clear, expressive, and easily understandable. You'll get a lot more benefit from that in the long run than any "optimization".
Neither case causes boxing.
The built-in value types have their own implementation of ToString()
, which is called implicitly by your second line of code anyway.
The first line contain string concatenation, and may be marginally slower than the second for that reason.
using "+" concatenation in strings is immutable whereas {0} format is not immutable and better way to avoid additonal memory lots left in app. Go for {0} format rather than "+" concatenation.
精彩评论