6

I have following program to add the values. When I am comenting Add method call in main method and looking into ILDASM.EXE Maxstack size is 2. And after uncommenting maxstack size becomes 4.

Why in the case of Main method all variables are not going into stack as stack size remains 2 only, While in case of Add method call every variable goes into stack? Is this the case that inside main method calculation is happening one by one so that it takes only two variable at a time.

Please clear my confusion.

static void Main(string[] args)
{
    int x = 2;
    int y = 3;
    int a = 4;
    int b = 5;
    int c = 6;

    Console.WriteLine(x + y + a + b + c);
    Console.WriteLine(Add(10, 20, 30, 40));
    Console.ReadLine();
}

static int Add(int x, int y, int z, int a)
{
    return x + y + z + a;
}
Markus Safar
  • 6,324
  • 5
  • 28
  • 44
Manjay_TBAG
  • 2,176
  • 3
  • 23
  • 43

1 Answers1

8

Each variable initialization:

int x = 2;

Will require the value to be on the stack: (stack size: 1 required so far)

.locals init ([0] int32 x,
       [1] int32 y,
       [2] int32 a,
       [3] int32 b,
       [4] int32 c)
IL_0000:  ldc.i4.2  // push 2 to the stack
IL_0001:  stloc.0   // load local variable 0 from stack ( x = 2 )

These operations happen sequentially, therefore max stack size required is still 1, during:

int y = 3;
int a = 4;
int b = 5;
int c = 6;

And when it comes to this:

Console.WriteLine(x + y + a + b + c);

To add any two variables, a stack size of 2 is required:

IL_000b:  ldloc.0 // copy to stack x, max stack size required is still 1.
IL_000c:  ldloc.1 // copy to stack y, max stack size required is 2 now.
IL_000d:  add     // execute add, will cause the sum x + y to be on stack
IL_000e:  ldloc.2 // copy to stack a
IL_000f:  add     // execute add... (adds a to the result of x + y)
....

The differential IL when you uncomment the Add method is below.

When calling a method, you need to push the instance reference onto the stack (meaning, if the Add method had been non-static, the instance pointer to its declaring type should be pushed onto the stack)

Then, each argument that needs to be passed to the method should also be pushed onto the stack.

Therefore, it is the number of parameters of the Add method in your case which defines the max stack size. Add a parameter to this Add method, and you will see that the max stack size will increase to 5:

// method is static so no need to push an instance pointer to the stack
IL_001a:  ldc.i4.s   10 // push to stack
IL_001c:  ldc.i4.s   20 // push to stack
IL_001e:  ldc.i4.s   30 // push to stack
IL_0020:  ldc.i4.s   40 // push to stack
IL_0022:  call       int32 Program::Add(int32,
                                      int32,
                                      int32,
                                      int32)
IL_0027:  call       void [mscorlib]System.Console::WriteLine(int32)
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
Oguz Ozgul
  • 6,809
  • 1
  • 14
  • 26
  • I'd slightly tweak the phrasing at the top - it's not the *declaration* that uses stack space, it's the *initialization*. – Damien_The_Unbeliever Nov 27 '15 at 07:44
  • @Damien_The_Unbeliever Thank you for the review & correction. Fixing it. – Oguz Ozgul Nov 27 '15 at 07:45
  • So is it like IL is not generated line by line. I mean to say is as x=2 takes one stack spack then why not other value declaration? Is it like compiler came to knew that it requires just addition so 2 space in stack will be enogh. – Manjay_TBAG Nov 27 '15 at 07:53
  • 2
    @TBAG - `x=2` required one stack space - it pushed `2` onto the stack, then popped it back off the stack into the variable `x`. At this point, the stack is once again empty. – Damien_The_Unbeliever Nov 27 '15 at 07:56
  • The IL is generated line by line. It is the compiler keeping track of the number of elements on the stack . – Oguz Ozgul Nov 27 '15 at 07:56
  • Think like this, it compiles int x = 2, it just generates the IL, and while generating the IL, the compiler naturally knows which instructions push to the stack, which pops, and which are operations causing the stack to shorten (like add. add sums up 2 values from the stack and removes them and sets the result to the stack so the stack has now 1 values instead of 2). So it can keep track of all this. At the end, it comes to know the maximum number required. – Oguz Ozgul Nov 27 '15 at 08:03
  • Thanks. As creating method takes more space on stack. Can I draw a conclusion that we should create method only when the code block will be reused. Otherwise better to write code inside Main or same method? – Manjay_TBAG Nov 27 '15 at 09:35
  • 1
    The profit of inlining a method body will be a number of IL instructions depending mostly on the number of parameters the method has. As seen above, 5 IL instructions is required to call a static method with 4 parameters. The compiler is smart enough to inline things when necessary. Don't break modularity for that. – Oguz Ozgul Nov 27 '15 at 10:05
  • In terms of memory usage, the default stack size for a thread is by default 1 MB. There may be cases where you want to INCREASE this, but since you already have 1 MB of stack space available, having a lower "max stack size" value in the generated IL will have no effects unless you recursively call for instance a method with 100 parameters 1000 times.. Best practices say we should limit the number of parameters a method accepts. You see, following the best practices and avoiding code smell always pay back. – Oguz Ozgul Nov 27 '15 at 10:05