Скільки занадто багато вкладених функцій викликів?


15

Цитується з MSDN про StackOverflowException :

Виняток, який видається, коли стек виконання переповнюється, оскільки він містить занадто багато вкладених викликів методу.

Too manyтут досить розпливчасто. Як я знаю, коли занадто багато справді занадто багато? Тисячі викликів функцій? Мільйони? Я припускаю, що це має бути певним чином пов'язане з кількістю пам'яті в комп’ютері, але чи можна придумати приблизно точний порядок?

Мене це турбує, оскільки я розробляю проект, який передбачає значне використання рекурсивних структур та рекурсивних викликів функцій. Я не хочу, щоб програма вийшла з ладу, коли я починаю використовувати її для більш ніж просто невеликих тестів.


4
Перетворити рекурсивну функцію в нерекурсивну функцію порівняно просто. Просто наклейте свої дані на Stack<T>.
Брайан

1
Наскільки велика кількість "занадто багато", ви самі визначаєте. Для .NET використовуйте editbin /stack:WHATEVER-NUMBER-YOU-LIKE yourexefile.exe.
SK-логіка

Відповіді:


28

Мене це турбує, оскільки я розробляю проект, який передбачає значне використання рекурсивних структур та рекурсивних викликів функцій. Я не хочу, щоб програма вийшла з ладу, коли я починаю використовувати її для більш ніж просто невеликих тестів.

Якщо ваше мовне середовище не підтримує оптимізацію хвостових викликів (і ваша рекурсія є хвостовим викликом), основним правилом є: глибина рекурсії повинна бути гарантована O (log n), тобто використовуючи алгоритми або структури даних, засновані на ділінні та захоплення (наприклад , дерев, більшість сортувальних alogorithms і т.д.) в порядку, але весь лінійні (як рекурсивних реалізації пов'язаної обробки списку) не є.


3
+1. Дуже гарна відповідь, я неявно використовував це правило, але я цього не знав.
Джорджіо

У цей день і вік майже будь-який компілятор якості виробництва, гідний словосполучення прикметника, підтримуватиме оптимізацію хвостових дзвінків.
Джон Р. Стром

1
@ JohnR.Strohm Однак ОП позначила це питання .NET, тому AFAIK лише 64-бітний тремтіння робить оптимізацію хвостових дзвінків на даний момент.
Марк Херд

1
Просто, щоб не було плутанини, і x86, і x64 завжди шанують CIL "хвіст". префікс інструкції Отже, підсумовуючи, у .NET land, F # робить повну оптимізацію хвостових викликів, C # не робить, а тремтіння x64 робить, якщо це "просто" (від цього не можна залежати). Дивіться blogs.msdn.com/b/clrcodegeneration/archive/2009/05/11/…
Стівен Свенсен

1
Правда, але в деяких випадках, як у сумнозвісному IIS, на якому розміщено ASP.NET, навіть проста глибина рекурсії O (log n) може вийти з ладу через їх жалюгідну, невиправдану, сміхову крихітну граничну глибину стека, яка практично спричиняє будь-яку рекурсію (або навіть довгий ланцюг нерекурсивних вкладених дзвінків) неможливий. Єдиний шлях - це редагувати сам двійковий файл.
SK-логіка

17

За замовчуванням CLR виділяє 1 Мб для стека для кожного потоку (див. Цю статтю ). Отже, для перевищення цієї суми потрібно багато дзвінків. Це буде залежати від того, скільки місця в стеку використовує кожен виклик для таких речей, як параметри та локальні змінні.

Ви навіть можете змусити його кинутись за StackOverflowExceptionдопомогою одного дзвінка, якщо ви хочете бути трохи неортодоксальним:

private static unsafe void BlowUpTheStack()
{
    var x = stackalloc byte[1000000000];
    Console.WriteLine("Oh no, I've blown up the stack!");
}

На жаль, цей розумний дефолт часто порушується (наприклад, на asp.net).
SK-логіка

2

Оскільки Коул Кемпбелл відзначив розмір пам’яті, а Майкл Боргвардт зазначив оптимізацію хвостових викликів, я не покриватиму їх.

Інша річ, яку слід пам’ятати - це CPS, який можна використовувати для оптимізації декількох переплетених функцій, де оптимізація хвостового виклику призначена для одиничних функцій.

Ви можете збільшити розмір стека, як ми це робили тут , і пам'ятати, що 64-бітний код з'їдає стек швидше, ніж 32-бітний код.

Варто зазначити, що ми провели один із прикладів під F # interactive більше 40 годин, не роздуваючи стек. Так, це був один функціональний виклик, який безперервно проходив до успішного завершення.

Крім того, якщо вам потрібно зробити покриття кодом, щоб з’ясувати, де виникають проблеми, і не маєте покриття коду за допомогою VS, який ви можете використовувати, TestDriven.NET

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.