2016-12-27 6 views
80

Поскольку все имеет предел, мне было интересно, существует ли ограничение на количество вложенных циклов for или пока у меня есть память, я могу их добавить, может ли компилятор Visual Studio создать такую ​​программу?Есть ли ограничение на количество вложенных циклов for?

Конечно, 64-х или более вложенные петли for были бы непригодны для отладки, но возможно ли это?

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.... 
     } 
    } 
    } 
} 
+0

Я согласен с другими, но когда у вас уже есть такая ситуация, вы должны поместить внутренние петли во внешние методы. По крайней мере, это было бы читаемо – Sasha

+78

Вы программист на компьютере: ответьте на свой вопрос с помощью компьютерного программирования. Напишите программу, которая генерирует, компилирует и запускает программы с 1, 2, 4, 8, 16, 32, ... вложенными циклами, и вы очень быстро узнаете, есть ли предел. –

+38

# 1. ОП не указал, что это никак не связано с производственным кодом, а не только с гипотетическим вопросом типа «что-если». # 2. Независимо от того, сколько вложенных циклов, которые делает OP во время экспериментов, они никогда не достигнут бесконечности; независимо от того, как далеко продвигает OP, единственный способ, который когда-либо докажет, есть ли предел, если и когда он действительно сломается. – Panzercrisis

ответ

118

Я выхожу на конечности путем размещения, но я думаю, что ответ:

Между 550 и 575

с настройками по умолчанию в Visual Studio 2015

Я создал небольшую программу, которая генерирует вложенные петли 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); 
      } 
     } 
     ... 
     ... 
    } 
} 

Для 500 вложенных циклов программа все еще может быть скомпилирована. С 575 циклов, компилятор вываливается:

Предупреждение AD0001 Analyzer «Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames.CSharpSimplifyTypeNamesDiagnosticAnalyzer» бросил исключение типа «System.InsufficientExecutionStackException» с сообщением «Недостаточно стека продолжить выполнение безопасно. Это может произойти из-за слишком большого количества функций в стеке вызовов или функции в стеке, используя слишком много пространства стека. '.

с основным сообщением компилятора

ошибка CS8078: Выражение является слишком длинным или сложным для компиляции

Конечно, это чисто гипотетический результат. Если самый внутренний цикл содержит больше, чем Console.WriteLine, тогда может быть меньше вложенных циклов до превышения размера стека.Кроме того, это может быть не строго технический предел в том смысле, что могут быть скрытые настройки для увеличения максимального размера стека для «Анализатора», упомянутого в сообщении об ошибке, или (если необходимо) для результирующего исполняемого файла. Однако эта часть ответа остается для людей, которые знают C# по глубине.


Update

В ответ на question in the comments:

мне было бы интересно увидеть этот ответ расширен, чтобы «доказать» экспериментально, можно ли ставить 575 локальные переменные в стеке, если они 're не Используется в петлях и/или вы можете положить 575 не вложенные для петель в одной функции

В обоих случаях ответ: Да, это возможно. При заполнении метода 575 автоматически сгенерированных операторов

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

он все еще может быть скомпилирован. Все остальное меня удивило бы. Размер стека, необходимый для переменных int, составляет всего 2,3 КБ. Но мне было любопытно, и для того, чтобы проверить дальнейшие ограничения, я увеличил это число. В конце концов, он сделал не компиляции, вызывая ошибку

ошибка CS0204: только 65534 местных жителей, в том числе, генерируемый компилятором, допускаются

который интересный момент, но уже наблюдали в других местах: Maximum number of variables in method

Аналогично, 575 невложенныхfor -loops, как и в

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

также может быть скомпилирован. Здесь я также попытался найти предел и создал больше этих циклов. В частности, я не был уверен, что переменные цикла в этом случае также считают als «locals», потому что они находятся в их собственном { block }. Но все же более 65534 невозможно. Наконец, я добавил тест, состоящий из 40000 петель узора

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

, который содержал дополнительную переменную в цикла, но они, кажется, считаются «местных», а также, и не было возможности собрать это.


Итак, подведем итог: Предел ~ 550 действительно вызвано вложенности петель.Это также было указано в сообщении об ошибке

ошибка CS8078: выражение слишком длинное или сложное для компиляции

documentation of error CS1647, к сожалению (но по понятным причинам) не определяет «измерение» сложности, но дает только прагматичный совет

В вашем компиляторе произошел переполнение стека. Чтобы устранить эту ошибку, упростите свой код.

Чтобы подчеркнуть это снова: Для конкретного случая глубоко вложенных for -loops, все это скорее академическое и гипотетический. Но поиск в Интернете сообщения об ошибке CS1647 показывает несколько случаев, когда эта ошибка появилась для кода, который, скорее всего, не был намеренно сложным, но создан в реалистичных сценариях.

+0

Итак, факт, что текущий компилятор Roslyn останавливается там, хорошо. Приятно знать. Скорректированный компилятор может дать совершенно разные результаты. +1 тем не менее;) –

+0

См. Мой обновленный ответ. При использовании старого компилятора C# число намного выше. –

+2

@PatrickHofman Да, подсказка * «с настройками по умолчанию ...» * важна: это относится к наивному созданию проекта по умолчанию в VS2015. Настройки проекта не отображали «очевидный» параметр, чтобы увеличить лимит. Но независимо от этого, я задаюсь вопросом, не существуют ли какие-либо дополнительные ограничения, налагаемые CLI/CLR/что угодно. [Спецификация ECMA-335] (http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf) имеет раздел * «III.1.7.4 Должен обеспечить maxstack» *, но я слишком много C# noob, чтобы сказать, действительно ли это связано, или даже проанализировать его значение подробно .... – Marco13

40

В спецификации языка C# или в CLR нет жесткого ограничения. Ваш код будет итеративным, а не рекурсивным, что может привести к переполнению стека довольно быстро.

Есть несколько вещей, которые могли бы рассчитывать в качестве порогового значения, к примеру (в целом) int счетчика вы будете использовать, что бы выделить определенную int в памяти для каждого цикла (и, прежде чем вы выделили весь стек с Интс. ..). Обратите внимание, что использование этого int требуется, и вы можете повторно использовать одну и ту же переменную.

Как pointed out by Marco, текущий порог больше в компиляторе, чем в фактической спецификации языка или во время выполнения. Как только это будет перекодировано, у вас может возникнуть еще несколько итераций. If you for example use Ideone, который по умолчанию использует старый компилятор, вы можете легко получить более 1200 for.

Это много для петель, хотя индикатор плохой конструкции. Надеюсь, этот вопрос является чисто гипотетическим.

+0

Я согласен с этим, но добавлю, что вы, вероятно, достигнете ограничения времени/оборудования до того, как достигнете ограничения на программное обеспечение (например, для компиляции требуется слишком много времени, или ваш код слишком длинный/слишком много ресурсов для выполнения) , –

+0

Файлы кода из миллиона строк компилируются довольно быстро, так что это не проблема. Кроме того, поскольку код довольно прямолинейный для механизма выполнения, я бы не ожидал слишком многого от его производительности. –

+0

много кода в одном методе может повредить производительность метода. но если вы разделите логику, она может работать быстрее (лучше). –

14

Существует лимит для всех C#, скомпилированных до MSIL. MSIL может поддерживать только 65535 локальных переменных. Если ваши петли for похожи на те, которые были показаны в примере, для каждого из них требуется переменная.

Возможно, ваш компилятор может выделять объекты в куче, чтобы действовать как хранилище для локальных переменных, обходя это ограничение. Однако я не уверен, какие из этих странных результатов будут получены. Могут возникнуть проблемы, возникающие с размышлениями, которые делают такой подход незаконным.

+5

Вам не нужны переменные для цикла for. –

+1

@PatrickHofman Вы правы, я отредактировал часть, которую я забыл включить –

+0

@PatrickHofman, если я не ошибаюсь, цикл for без переменных эквивалентен 'while (true)', который формирует бесконечный цикл. Если мы отредактировали вопрос на «Есть ли ограничение на количество вложенных циклов в завершающей программе?» то можно ли использовать локальный переменный трюк, чтобы сформировать жесткий предел? – emory

7

Между 800 и 900 для пустых for(;;) петель.

подхода Зеркального Marco13, за исключением пытавшихся for(;;) петель:

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

Он работал на 800 вложенных for(;;), но он дал ту же ошибку, что Marco13 столкнулся при попытке 900 петель.

Когда он компилируется, for(;;), по-видимому, блокирует поток без превышения ЦП; по-видимому, он действует как Thread.Sleep().

+2

*« он заканчивается бег довольно много мгновенно »* - это странно - я думал, что' for (;;) 'будет бесконечным циклом в C# ?! (Я не могу попробовать это сейчас, но если это окажется неправильным, я удалю этот комментарий) – Marco13

+0

@ Marco13: Да, вы правы! Не уверен, что происходило в моем тестовом коде раньше, но 'for (;;)' блокируется, не потребляя процессорное время. – Nat

+0

A for (;;) loop - это бесконечный цикл, поэтому зачем добавлять другой для цикла (;;) или 800 из них, потому что они не будут запущены. первый будет работать вечно. –

Смежные вопросы