web-dev-qa-db-fra.com

Y a-t-il une limite au nombre de boucles 'pour' imbriquées?

Puisque tout a une limite, je me demandais s'il y avait une limite au nombre de boucles imbriquées for ou si je pouvais avoir de la mémoire, je peux les ajouter. Le compilateur Visual Studio peut-il créer un tel programme?

Bien sûr, une boucle imbriquée de for ou plus ne serait pas pratique pour 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
Fred Smith

Je vais sur un membre en postant ceci, mais je pense que la réponse est:

Entre 550 et 575

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

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

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 toujours être compilé. Avec 575 boucles, le compilateur échoue:

Avertissement AD0001 Analyzer 'Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticAnalyzer' a lancé une exception de type 'System.InsufficientExecutionStackException' avec le message 'Pile insuffisante pour continuer l'exécution du programme. Cela peut arriver si vous avez trop de fonctions dans la pile d'appels ou si vous utilisez trop d'espace de pile. '.

avec le message du compilateur sous-jacent

error CS8078: Une expression est trop longue ou complexe à 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, moins de boucles imbriquées peuvent alors être possibles avant que la taille de la pile ne soit dépassée. De plus, cela peut ne pas être une limite strictement technique, en ce sens qu'il peut y avoir des paramètres cachés pour augmenter la taille de pile maximale pour "Analyzer" 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 élargie pour "prouver" expérimentalement si vous pouvez mettre 575 variables locales sur la pile si elles sont pas utilisées dans des boucles for, et/ou si vous pouvez mettre 575 non-imbriqué pour-boucles dans une seule fonction

Dans les deux cas, la réponse est: oui, c'est possible. Lorsque vous remplissez la méthode avec 575 instructions générées automatiquement

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 m'aurait surpris. La taille de la pile requise pour les variables int est de 2,3 Ko seulement. Mais j'étais curieux et, afin de tester de nouvelles limites, j'ai augmenté ce nombre. Finalement, il n'a pas compilé, provoquant l'erreur

erreur CS0204: seules les sections locales 65534, y compris celles générées par le compilateur, sont autorisées

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

De même, 575 non imbriquéesfor- 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 être compilé aussi bien. Ici, j'ai aussi essayé de trouver la limite et créé plus de ces boucles. En particulier, je ne savais pas si les variables de boucle dans ce cas comptaient également comme "locales", car elles sont dans leur propre { block }. Mais toujours, plus de 65 534 n'est pas possible. Enfin, j'ai ajouté un test composé de 40000 boucles du motif

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

qui contenait une variable supplémentaire in la boucle, mais celles-ci semblent également compter en tant que "locales", et il n’a pas été possible de les compiler.


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

error CS8078: Une expression est trop longue ou complexe à compiler

Le documentation de l'erreur CS1647 malheureusement (mais de manière compréhensible) ne spécifie pas une "mesure" de la complexité, mais ne donne que le conseil pragmatique

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

Pour souligner encore ceci: Pour le cas particulier des boucles for- profondément imbriquées, tout ceci est plutôt théorique et hypothétique. Mais la recherche Web pour le message d'erreur de CS1647 révèle plusieurs cas dans lesquels cette erreur s'est produite pour du code probablement non intentionnellement complexe, mais créé dans des scénarios réalistes.

119
Marco13

Il n'y a pas de limite stricte dans la spécification du langage C # ou dans le CLR. Votre code serait itératif, plutôt que récursif, ce qui pourrait conduire à un débordement de pile très rapidement.

Quelques éléments peuvent être considérés comme un seuil, par exemple le compteur (généralement) int que vous utiliseriez, qui allouerait un int en mémoire pour chaque boucle (et avant que vous ayez alloué votre pile entière avec ints ...). Notez que l'utilisation de ce int est requise et que vous pouvez réutiliser la même variable.

Comme souligné par Marco , le seuil actuel est davantage dans le compilateur que dans la spécification de langue ou le runtime. Une fois que cela est recodé, vous aurez peut-être encore plus d'itérations. Si vous utilisez par exemple Ideone , qui utilise par défaut l'ancien compilateur, vous pouvez obtenir plus de 1200 boucles for facilement.

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

40
Patrick Hofman

Il y a une limite pour tout C # compilé jusqu'à MSIL. MSIL ne peut supporter que 65535 variables locales. Si vos boucles for ressemblent à celles que vous avez montrées dans l'exemple, chacune d'elles nécessite une variable.

Il est possible que votre compilateur puisse allouer des objets sur le tas pour servir de stockage pour les variables locales, contournant cette limite. Cependant, je ne suis pas sûr du genre de résultats étranges qui en découleraient. Des réflexions peuvent rendre une telle approche illégale.

13
Cort Ammon

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

Reflète l'approche de Marco13, à l'exception des boucles for(;;) essayées:

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

Cela a fonctionné pour 800 imbriqués for(;;), mais cela a donné la même erreur que Marco13 a rencontrée lors de l’essai de 900 boucles.

Quand il compile, le symbole for(;;) apparaît pour bloquer le thread sans surcharger le processeur; superficiellement, il semble agir comme un Thread.Sleep().

7
Nat