Est-il une limite au nombre de imbriquée "pour" les boucles?

comme tout a une limite, je me demandais s'il y avait une limite au nombre de boucles for imbriquées ou aussi longtemps que j'ai de la mémoire, je peux les ajouter, le compilateur Visual Studio peut-il créer un tel programme?

bien sûr, une boucle for imbriquée de 64 ou plus ne serait pas pratique à déboguer, mais est-ce faisable?

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....
      }
    }
  }
}
79
demandé sur Peter Mortensen 2016-12-27 17:30:26

4 réponses

je prends des risques en postant ceci, mais je pense que la réponse est:

entre 550 et 575

avec paramètres par défaut dans Visual Studio 2015

j'ai créé un petit programme qui génère des boucles for imbriquées...

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);
            }
        }
        ...
        ...
    }
}

pour 500 boucles imbriquées, le programme peut encore être compilé. Avec 575 boucles, le compilateur renflouent:

Avertissement AD0001 Analyseur de 'Microsoft.CodeAnalysis.CSharp.Diagnostic.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticanalyzer "a lancé une exception du système de type".InsufficientExecutionStackException 'with message' pile insuffisante pour continuer à exécuter le programme en toute sécurité. Cela peut se produire en ayant trop de fonctions sur la pile d'appels ou de fonctions sur la pile en utilisant trop d'espace sur la pile.'.

avec le message de compilateur sous-jacent

erreur CS8078: une expression est trop longue ou complexe pour compiler

bien sûr, il s'agit d'un résultat purement hypothétique . Si la boucle la plus interne fait plus qu'un Console.WriteLine , alors moins de boucles imbriquées peuvent être possibles avant que la taille de la pile soit dépassée. Aussi, cela peut ne pas être strictement technique limite, dans le sens qu'il peut être caché paramètres de augmenter la taille maximale de la pile pour l ' "analyseur" mentionné dans le message d'erreur, ou (si nécessaire) pour l'exécutable résultant. Cette partie de la réponse, cependant, est laissée aux personnes qui connaissent C# en profondeur.


mise à Jour

en réponse à la question dans les commentaires :

je serais intéressé de voir cette réponse étendue pour "prouver" expérimentalement si vous pouvez mettre 575 variables locales sur la pile si elles sont et non utilisées dans les for-loops, et/ou si vous pouvez mettre 575 non imbriquées for-loops dans une seule fonction

Pour les deux cas, la réponse est: Oui, c'est possible. Lors du remplissage de la méthode avec 575 déclarations auto-générées

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

il peut encore être compilé. Tout le reste ne m'aurait surpris. La taille de la pile requise pour les variables int n'est que de 2,3 KB. Mais j'étais curieux, et pour tester d'autres limites, j'ai augmenté ce nombre. Finalement, il a fait pas compiler, causant l'erreur

erreur CS0204: seuls 65534 locaux, y compris ceux générés par le compilateur, sont autorisés

, qui est un point intéressant, mais qui a déjà été observé ailleurs: nombre Maximum de variables dans la méthode

de même, 575 Non emboîtés for - boucles, comme dans

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);
}

peut aussi être compilé. Ici, j'ai aussi essayé de trouver la limite, et a créé plus de ces boucles. En particulier, je n'étais pas sûr que les variables de boucle dans ce cas compte aussi "locaux", parce qu'ils sont dans leur propre { block } . Mais encore, plus de 65534 n'est pas possible. Enfin, j'ai ajouté un test composé de 40000 boucles du modèle

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

qui contenait une variable supplémentaire dans la boucle, mais ceux-ci semblent compter comme" locaux " aussi, et il n'a pas été possible de compiler cela.


pour résumer: la limite de ~550 est en effet causée par la profondeur de nidification des boucles. Cela a également été indiqué par le message d'erreur

erreur CS8078: une expression est trop longue ou complexe pour compiler

le documentation d'erreur CS1647 malheureusement (mais compréhensible) ne spécifie pas une "mesure" de la complexité, mais donne seulement le conseil pragmatique

il y avait un débordement de pile dans le compilateur traitant votre code. Pour corriger cette erreur, simplifiez votre code.

pour souligner encore ceci: pour le cas particulier des boucles for profondément imbriquées, tout cela est plutôt académique et hypothétique . Mais la recherche sur le web pour le message D'erreur de CS1647 révèle plusieurs cas où cette erreur est apparue pour du code qui n'était probablement pas intentionnellement complexe, mais créé dans des scénarios réalistes.

118
répondu Marco13 2017-05-23 12:02:56

il n'y a pas de limite stricte dans la spécification linguistique C# ni dans le CLR. Votre code serait itératif, plutôt que récursif, ce qui pourrait conduire à un débordement de la pile assez rapide.

il y a quelques choses qui pourraient compter comme un seuil, par exemple le compteur (généralement) int que vous utiliseriez, qui attribuerait un int en mémoire pour chaque boucle (et avant que vous ayez attribué votre pile entière avec des ints...). Notez que l'utilisation de ce int est nécessaire et vous pouvez réutiliser la même variable.

comme souligné par Marco , le seuil actuel est plus dans le compilateur que dans la spécification de langue réelle ou l'exécution. Une fois que c'est recodé, vous pourriez en place avec quelques itérations. si vous utilisez par exemple Ideone , qui par défaut utilise l'ancien compilateur, vous pouvez obtenir plus de 1200 for boucles facilement.

pour les boucles sont un indicateur de mauvaise conception. J'espère que cette question est purement hypothétique.

40
répondu Patrick Hofman 2017-05-23 10:29:17

il y a une limite pour tous les C# compilés JUSQU'à MSIL. MSIL ne peut prendre en charge que 65535 variables locales. Si vos boucles for sont comme celles que vous avez montrées dans l'exemple, chacune nécessite une variable.

il est possible que votre compilateur affecte des objets sur le tas pour agir comme stockage de variables locales, contournant cette limite. Cependant, je ne suis pas sûr que ce genre de résultats étranges viendrait de cela. Il peut y avoir des questions qui surgissent avec la réflexion ce qui rend une telle approche illégale.

13
répondu Cort Ammon 2016-12-28 00:59:59

entre 800 et 900 pour les boucles for(;;) vides.

reflète L'approche de Marco13, sauf les boucles for(;;) essayées:

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

il a fonctionné pour 800 emboîtés for(;;) , mais il a donné la même erreur que Marco13 rencontré en essayant 900 boucles.

lors de sa compilation, le for(;;) semble bloquer le thread sans maxer le CPU; superficiellement, il semble agir comme un Thread.Sleep() .

7
répondu Nat 2016-12-30 05:20:56