c# - crear - ¿Existe un límite para el número de bucles ''for'' anidados?
foreach list object c# (4)
Como todo tiene un límite, me preguntaba si existe un límite para la cantidad de bucles anidados o si tengo memoria, puedo agregarlos, ¿puede el compilador de Visual Studio crear tal programa?
Por supuesto, un 64 o más bucles anidados no sería útil para depurar, pero ¿es factible?
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....
}
}
}
}
Hay un límite para todos los C # compilados en MSIL.
MSIL solo puede admitir 65535 variables locales.
Si sus bucles
for
son como los que mostró en el ejemplo, cada uno requiere una variable.
Es posible que su compilador pueda asignar objetos en el montón para actuar como almacenamiento de variables locales, eludiendo este límite. Sin embargo, no estoy seguro de qué tipo de resultados extraños vendrían de eso. Pueden surgir problemas con la reflexión que hacen que este enfoque sea ilegal.
No existe un límite estricto en la especificación del lenguaje C # o el CLR. Su código sería iterativo, en lugar de recursivo, lo que podría conducir a un desbordamiento de la pila bastante rápido.
Hay algunas cosas que podrían contar como un umbral, por ejemplo, el contador
int
(generalmente) que usaría, que asignaría un
int
en la memoria para cada ciclo (y antes de que haya asignado toda su pila con ints ...).
Tenga en cuenta que se requiere el uso de ese
int
y puede reutilizar la misma variable.
Como
señaló Marco
, el umbral actual está más en el compilador que en la especificación de lenguaje o tiempo de ejecución real.
Una vez que se haya recodificado, es posible que tenga más iteraciones.
Si, por ejemplo, usa Ideone
, que por defecto usa el compilador anterior, puede obtener más de 1200
for
bucles fácilmente.
Sin embargo, eso para los bucles es un indicador de mal diseño. Espero que esta pregunta sea puramente hipotética.
Voy a arriesgarme publicando esto, pero creo que la respuesta es:
Entre 550 y 575
con configuración predeterminada en Visual Studio 2015
Creé un pequeño programa que genera anidados
for
bucles ...
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);
}
}
...
...
}
}
Para 500 bucles anidados, el programa aún se puede compilar. Con 575 bucles, el compilador rescata:
Advertencia AD0001 Analyzer ''Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticAnalyzer'' arrojó una excepción de tipo ''System.InsufficientExecutionStackException'' con el mensaje ''Insuficiente pila para continuar ejecutando el programa de forma segura. Esto puede suceder por tener demasiadas funciones en la pila de llamadas o función en la pila usando demasiado espacio en la pila ''.
con el mensaje del compilador subyacente
error CS8078: una expresión es demasiado larga o compleja para compilar
Por supuesto, este es un resultado
puramente hipotético
.
Si el bucle más interno hace más que una
Console.WriteLine
, es posible que haya menos bucles anidados antes de que se exceda el tamaño de la pila.
Además, esto puede no ser un límite estrictamente técnico, en el sentido de que puede haber configuraciones ocultas para aumentar el tamaño máximo de pila para el "Analizador" que se menciona en el mensaje de error, o (si es necesario) para el ejecutable resultante.
Sin embargo, esta parte de la respuesta se deja a las personas que conocen C # en profundidad.
Actualizar
En respuesta a la pregunta en los comentarios :
Me interesaría ver esta respuesta ampliada para "probar" experimentalmente si puede poner 575 variables locales en la pila si no se usan en bucles for, y / o si puede poner 575 bucles for no anidados en una sola función
Para ambos casos, la respuesta es: Sí, es posible. Al completar el método con 575 declaraciones autogeneradas
int i0=0;
Console.WriteLine(i0);
int i1=0;
Console.WriteLine(i1);
...
int i574=0;
Console.WriteLine(i574);
Todavía se puede compilar.
Todo lo demás me habría sorprendido.
El tamaño de pila que se requiere para las variables
int
es de solo 2.3 KB.
Pero tenía curiosidad, y para probar más límites, aumenté este número.
Finalmente, no
se
compiló, causando el error
error CS0204: solo se permiten 65534 locales, incluidos los generados por el compilador
lo cual es un punto interesante, pero ya se ha observado en otros lugares: número máximo de variables en el método
Del mismo modo, 575 bucles
for
no anidados
, como en
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);
}
puede ser compilado también
Aquí, también intenté encontrar el límite, y creé más de estos bucles.
Particularmente, no estaba seguro de si las variables de bucle en este caso también cuentan como "locales", porque están en su propio
{ block }
.
Pero aún así, más de 65534 no es posible.
Finalmente, agregué una prueba que consta de 40000 bucles del patrón
for (int i39999 = 0; i39999 < 10; i39999++)
{
int j = 0;
Console.WriteLine(j + i39999);
}
que contenía una variable adicional en el bucle, pero también parecen contar como "locales", y no fue posible compilar esto.
Para resumir: el límite de ~ 550 es causado por la profundidad de anidamiento de los bucles. Esto también fue indicado por el mensaje de error
error CS8078: una expresión es demasiado larga o compleja para compilar
Desafortunadamente, la documentación del error CS1647 (pero comprensiblemente) no especifica una "medición" de la complejidad, sino que solo da consejos pragmáticos.
Hubo un desbordamiento de pila en el compilador que procesaba su código. Para resolver este error, simplifique su código.
Para enfatizar esto nuevamente:
Para el caso particular de bucles
for
profundamente anidados, todo esto es bastante académico e hipotético
.
Pero la búsqueda web para el mensaje de error de CS1647 revela varios casos en los que este error apareció para el código que probablemente no fue intencionalmente complejo, sino que se creó en escenarios realistas.
Entre 800 y 900 para vacío
for(;;)
bucles.
El enfoque de Marco13 reflejado, excepto que se intentó
for(;;)
bucles:
for (;;) // 0
for (;;) // 1
for (;;) // 2
// ...
for (;;) // n_max
{
// empty body
}
Funcionó para 800 anidados
for(;;)
, pero dio el mismo error que Marco13 encontró al intentar 900 bucles.
Cuando se compila, el
for(;;)
parece bloquear el hilo sin maximizar la CPU;
superficialmente, parece actuar como un
Thread.Sleep()
.