Is there a limit to the number of nested 'for' loops?

C#For LoopCompilationLimit

C# Problem Overview


Since everything has a limit, I was wondering if there is a limit to the number of nested for loops or as long as I have memory, I can add them, can the Visual Studio compiler create such a program?

Of course a 64 or more nested for loops would be not handy to debug, but is it doable?

private void TestForLoop()
{
  for (int a = 0; a < 4; a++)
  {
    for (int b = 0; b < 56; b++)
    {
      for (int c = 0; c < 196; c++)
      {
        //etc....
      }
    }
  }
}

C# Solutions


Solution 1 - C#

I'm going out on a limb by posting this, but I think that the answer is:

Between 550 and 575

with default settings in Visual Studio 2015

I created a small program that generates nested for loops...

for (int i0=0; i0<10; i0++)
{
    for (int i1=0; i1<10; i1++)
    {
        ...
        ...
        for (int i573=0; i573<10; i573++)
        {
            for (int i574=0; i574<10; i574++)
            {
                Console.WriteLine(i574);
            }
        }
        ...
        ...
    }
}

For 500 nested loops, the program can still be compiled. With 575 loops, the compiler bails out:

> Warning AD0001 Analyzer 'Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticAnalyzer' threw an exception of type 'System.InsufficientExecutionStackException' with message 'Insufficient stack to continue executing the program safely. This can happen from having too many functions on the call stack or function on the stack using too much stack space.'.

with the underlying compiler message

> error CS8078: An expression is too long or complex to compile

Of course, this is a purely hypothetical result. If the innermost loop does more than a Console.WriteLine, then fewer nested loops may be possible before the stack size is exceeded. Also, this may not be a strictly technical limit, in the sense that there may be hidden settings to increase the maximum stack size for the "Analyzer" that is mentioned in the error message, or (if necessary) for the resulting executable. This part of the answer, however, is left to the people who know C# in depth.


Update

In response to the question in the comments :

> I would be interested to see this answer expanded to "prove" experimentally whether you can put 575 local variables on the stack if they're not used in for-loops, and/or whether you can put 575 non-nested for-loops in a single function

For both cases, the answer is: Yes, it is possible. When filling the method with 575 auto-generated statements

int i0=0;
Console.WriteLine(i0);
int i1=0;
Console.WriteLine(i1);
...
int i574=0;
Console.WriteLine(i574);

it can still be compiled. Everything else would have surprised me. The stack size that is required for the int variables is just 2.3 KB. But I was curious, and in order to test for further limits, I increased this number. Eventually, it did not compile, causing the error

> error CS0204: Only 65534 locals, including those generated by the compiler, are allowed

which is an interesting point, but has already been observed elsewhere: Maximum number of variables in method

Similarly, 575 non-nested for-loops, as in

for (int i0=0; i0<10; i0++)
{
    Console.WriteLine(i0);
}
for (int i1=0; i1<10; i1++)
{
    Console.WriteLine(i1);
}
...
for (int i574=0; i574<10; i574++)
{
    Console.WriteLine(i574);
}

can be compiled as well. Here, I also tried to find the limit, and created more of these loops. Particularly, I wasn't sure whether the loop variables in this case also count als "locals", because they are in their own { block }. But still, more than 65534 is not possible. Finally, I added a test consisting of 40000 loops of the pattern

for (int i39999 = 0; i39999 < 10; i39999++)
{
    int j = 0;
    Console.WriteLine(j + i39999);
}

that contained an additional variable in the loop, but these seem to count as "locals" as well, and it was not possible to compile this.


So to summarize: The limit of ~550 is indeed caused by the nesting depth of the loops. This also was indicated by the error message

> error CS8078: An expression is too long or complex to compile

The documentation of error CS1647 unfortunately (but understandably) does not specify a "measurement" of complexity, but only gives the pragmatic advice

> There was a stack overflow in the compiler processing your code. To resolve this error, simplify your code.

To emphasize this again: For the particular case of deeply nested for-loops, all this is rather academic and hypothetical. But websearching for the error message of CS1647 reveals several cases where this error appeared for code that was most likely not intentionally complex, but created in realistic scenarios.

Solution 2 - C#

There is no hard limit in the C# language specification or the CLR. Your code would be iterative, rather than recursive, which could lead to a stack overflow quite fast.

There are a few things that could count as a threshold, for example the (generally) int counter you would use, which would allocate an int in memory for each loop (and before you have allocated your entire stack with ints...). Note that the use of that int is required and you may reuse the same variable.

As pointed out by Marco, the current threshold is more in the compiler than in the actual language specification or runtime. Once that is recoded, you might up with quite some more iterations. If you for example use Ideone, which by default uses the old compiler, you can get over 1200 for loops easily.

That much for loops are an indicator of bad design though. I hope this question is purely hypothetical.

Solution 3 - C#

There is a limit for all C# compiled down to MSIL. MSIL can only support 65535 local variables. If your for loops are like the ones you showed in the example, each one requires a variable.

It's possible that your compiler could allocate objects on the heap to act as storage for local variables, circumventing this limit. However, I'm not sure what sorts of odd results would come from that. There may be issues that arise with reflection which make such an approach illegal.

Solution 4 - C#

Between 800 and 900 for empty for(;;) loops.

Mirrored Marco13's approach, except tried for(;;) loops:

for (;;)  // 0
for (;;)  // 1
for (;;)  // 2
// ...
for (;;)  // n_max
{
  // empty body
}

It worked for 800 nested for(;;), but it gave the same error that Marco13 encountered when trying 900 loops.

When it does compile, the for(;;) appears to block the thread without maxing out the CPU; superficially, it seems to act like a Thread.Sleep().

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFred SmithView Question on Stackoverflow
Solution 1 - C#Marco13View Answer on Stackoverflow
Solution 2 - C#Patrick HofmanView Answer on Stackoverflow
Solution 3 - C#Cort AmmonView Answer on Stackoverflow
Solution 4 - C#NatView Answer on Stackoverflow