Quante sono troppe chiamate di funzione nidificate?


15

Citato da MSDN su StackOverflowException :

Eccezione generata quando lo stack di esecuzione trabocca perché contiene troppe chiamate di metodo nidificate.

Too manyè piuttosto vago qui. Come faccio a sapere se troppi sono davvero troppi? Migliaia di chiamate di funzione? Milioni? Presumo che debba essere in qualche modo correlato alla quantità di memoria nel computer, ma è possibile elaborare un ordine di grandezza approssimativamente accurato?

Sono preoccupato per questo perché sto sviluppando un progetto che prevede un uso intenso di strutture ricorsive e chiamate di funzioni ricorsive. Non voglio che l'applicazione non vada a buon fine quando inizio a usarla solo per piccoli test.


4
È relativamente facile convertire una funzione ricorsiva in una funzione non ricorsiva. Attacca i tuoi dati su a Stack<T>.
Brian

1
Quanto è grande "troppi" dipende interamente da te definire. Per .NET, utilizzare editbin /stack:WHATEVER-NUMBER-YOU-LIKE yourexefile.exe.
SK-logic

Risposte:


28

Sono preoccupato per questo perché sto sviluppando un progetto che prevede un uso intenso di strutture ricorsive e chiamate di funzioni ricorsive. Non voglio che l'applicazione non vada a buon fine quando inizio a usarla solo per piccoli test.

A meno che il tuo ambiente linguistico non supporti l' ottimizzazione delle chiamate di coda (e la tua ricorsione è una chiamata di coda), una regola empirica di base è: la profondità di ricorsione dovrebbe essere garantita come O (log n), cioè usando algoritmi o strutture di dati basati su divide-and- Conquer (come gli alberi, la maggior parte alogorithms smistamento, ecc) è OK, ma niente di lineare (come implementazioni ricorsive di movimentazione lista collegata) non lo è.


3
+1. Ottima risposta, ho implicitamente usato questa regola ma non ne ero consapevole.
Giorgio

Al giorno d'oggi, qualsiasi compilatore di qualità produttiva degno della frase aggettiva supporterà l'ottimizzazione delle chiamate di coda.
John R. Strohm,

1
@ JohnR.Strohm Tuttavia l'OP ha taggato la domanda .NET, quindi AFAIK al momento solo il jitter a 64 bit esegue l'ottimizzazione delle chiamate di coda.
Mark Hurd

1
Solo così non c'è confusione, entrambi gli x86 e x64 onorano sempre la "coda" del CIL. prefisso di istruzione. Quindi, per riassumere, in .NET land, F # completa l'ottimizzazione della coda, C # no, e il jitter x64 lo fa se è "facile" (non si può fare affidamento). Vedi blogs.msdn.com/b/clrcodegeneration/archive/2009/05/11/…
Stephen Swensen

1
È vero, ma in alcuni casi, come in un famigerato IIS che ospita ASP.NET, anche una semplice profondità di ricorsione O (log n) potrebbe non riuscire a causa del loro patetico, ingiustificato, ridicolo limite di profondità dello stack minuscolo, che rende praticamente qualsiasi ricorsione (o anche una lunga catena di chiamate annidate non ricorsive) impossibile. L'unico modo per aggirare è modificarebin il file binario stesso.
SK-logic

17

Per impostazione predefinita, il CLR alloca 1 MB allo stack per ogni thread (vedere questo articolo ). Quindi, sono comunque molte le chiamate necessarie per superare questo importo. Ciò varierà a seconda di quanto spazio nello stack viene utilizzato da ogni chiamata per cose come parametri e variabili locali.

Puoi anche farlo lanciare a StackOverflowExceptioncon una sola chiamata se sei disposto a essere un po 'poco ortodosso:

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

Sfortunatamente, questo ragionevole default è spesso violato (ad es. In asp.net).
SK-logic

2

Dato che Cole Campbell ha notato le dimensioni della memoria e Michael Borgwardt ha notato l' ottimizzazione delle chiamate in coda , non le coprirò.

Un'altra cosa da tenere presente è il CPS che può essere utilizzato per ottimizzare diverse funzioni intrecciate in cui l'ottimizzazione delle chiamate di coda è per singole funzioni.

Puoi aumentare le dimensioni dello stack come abbiamo fatto qui ed essere consapevoli del fatto che il codice a 64 bit mangia lo stack più velocemente del codice a 32 bit.

Da notare che abbiamo eseguito uno degli esempi in F # interattivo per più di 40 ore senza far saltare lo stack. Sì, è stata una chiamata di funzione che è stata eseguita da sola ininterrottamente per il completamento con esito positivo.

Inoltre, se è necessario eseguire la copertura del codice per capire dove si verificano i problemi e non avere una copertura del codice con VS, è possibile utilizzare TestDriven.NET

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.