C# · CodeProject · Dotnet

Internals of Params TIP (Beginner/Intermediate)

Dear Readers,

A bit history:

From past 2 days i have been taking few interviews (telecons mostly) for guys having 4-6 years experience in C# .NET. Though i would not claim myself that i am very much talented than them in assessing them.

So on the same, for 4 guys i asked few questions on params. The question first i asked was: Whats the use of params in C#. For which the candidates gave away a straight bookish answer (yes you can make out by the way they explain), but i did not stop there. I went a bit further and asked them (because they are more experienced and they should know more than what book says): Why do we need params: For which they still answered right which again as per all books/msdn. Further i asked again: Why do i need it, i could have done just by passing a an array with different size as argument. For which i did not get any answer, they seem to got confused.

Then further to this, i again asked: Do you find any hidden problem with this feature or other wise tell me how it works. For which again no answers. Yes you may think i am interviewing the wrong talented guys? May be, but i have to do it because of the protocol ūüôā

Description:

So after these interviews, when i got back to my seat even i got a bit curious to dig deep into this topic. Although i knew already that every time you call a params method, it would build an array and creating this array is a bit performance overhead. But i never cared enough to convince myself as whats been told is really what it is and to know more.

So this post is about that, upon disassembling the below code, i got below IL:

void ParamsMethod(params int[] arguments)
{

}

ParamsMethod(1,2,3);
IL_0001: ldc.i4.3
IL_0002: newarr [mscorlib]System.Int32
IL_0007: stloc.1
IL_0008: ldloc.1
IL_0009: ldc.i4.0
IL_000a: ldc.i4.1
IL_000b: stelem.i4
IL_000c: ldloc.1
IL_000d: ldc.i4.1
IL_000e: ldc.i4.2
IL_000f: stelem.i4
IL_0010: ldloc.1
IL_0011: ldc.i4.2
IL_0012: ldc.i4.3
IL_0013: stelem.i4
IL_0014: ldloc.1
IL_0015: call void ConsoleApplication.Program::SomeMethod(int32[])
IL_001a: nop

As you can see first the compiler has converted the call to a sequence of OpCode starting with creating an array via newarr opcode. Now i started to dig a bit on this keyword. As per that, arrays do get created on heap and the size is known by the number of arguments passed which is what the first IL opcode  IL_0001: ldc.i4.3 talks about. The 3 at the end is the size of the array which needs to be created on heap. Do vary the arguments number, and see the IL code, the 3 changes to appropriate number of arguments. With that more IL instructions do get added for the additional arguments to be loaded onto array.

As soon as i read this, i started wondering since a valuetype is getting created on heap do the values also gets boxed because they needs to be created on heap. Based on some googling, i came to know that array contents will not be boxed even though they get created on heap. The reason why it gets created on heap is because the array sizes are never known and internally the run time decides as per its rules that arrays are usually a long-lived objects. Hence heap is preferred.

Still i had the doubt why boxing is not required if a value type is getting stored on heap. It’s because the value type which is getting stored inside the array content is actually stored as value itself even though it’s on heap, unless you wish to reference that same array content value as reference type then boxing will happen. So in simple words, just because you need to store some thing on heap, does not mean you need to box it. If you box a valuetype because you wanted it to be a ref type, then it must be stored on the heap only.

So what i can infer from the above analysis is that: Every call creates a new array over heap.

Hence based on the above points, we need to carefully choose if we really need method having params in its signature, so that frequent usage of the same does not add performance overhead to the application.

Now coming back to my tricky question like “You can use direct array to pass instead of params”. The answer is really simple if your still looking for it. Once array is declared and defined with contents you can not modify it again as shown below:

int[] myarray = { 1, 2, 3 };
//Do some operations
myarray = {1,2,3,4,5};//error, cant do!

So every time you need to pass varying number of arguments to the method taking int[] array as argument, then you need to create new as shown below:

int[] myarray = { 1, 2, 3 };
//Do some operations
myarray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };//This is bad!

The above style is not a good coding and not really worth either to write. So this way params do come in handy for a nice interface.

I tried doing a performance study between params and passing array, since both ways the run time is creating arrays on heap, just look at the code:

static void ParamsMethod(params int[] args)
{
var x = args.Length;
}

static void NonParamMethod(int[] args)
{
var x = args.Length;
}

static void Main(string[] args)
{
int count = 1000;
Stopwatch sp = Stopwatch.StartNew();

for (int i = 0; i < count; i++)
{
ParamsMethod(1, 2, 3, 4, 5, 6);
}
sp.Stop();
Console.WriteLine(sp.ElapsedTicks);

int[] myArray = { 1, 2, 3, 4, 5, 6 };

sp = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
NonParamMethod(myArray);
}

sp.Stop();
Console.WriteLine(sp.ElapsedTicks); }

The output i got was:

465939
235953

Note: I did run the above test code multiple times to make sure that the output results are quite consistent. And yet it was. As well as kept the method body same and simple to have some consistency in results.

As you can see the difference is quite more, this is because in params style every call creates a new array on heap and in the later style only once created array is used for each call.

Hence based on all the above points, it is suggested that carefully decide if to use params and the best guidelines suggests that do not use params for critical methods in your application.

Thanks ūüôā

P.S: Your valuable comments/votes are much appreciated.

Advertisements

2 thoughts on “Internals of Params TIP (Beginner/Intermediate)

  1. It will always do, because in case of Params, you are presenting new arrays always, but in second case, you are passing the same Array.

    So the second one is reference copy, passing the same object from heap. Params are doing the extra work to create the array for you every time you call.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s