2016-03-02 48 views
6

pochodzi z this question, mam ten mały F kod # (github) do generowania wartości losowych według rozkładu normalnego:przepełnienie stosu mimo położenia połączeń ogon, ale tylko w wersji 64-bitowej

// val nextSingle : (unit -> float32) 
let nextSingle = 
    let r = System.Random() 
    r.NextDouble >> float32 

// val gauss : (float32 -> float32 -> seq<float32>) 
let gauss mean stdDev = 
    let rec gauss ready = seq { 
     match ready with 
     | Some spare -> 
      yield spare * stdDev + mean 
      yield! gauss None 
     | _ -> 
      let rec loop() = 
       let u = nextSingle() * 2.f - 1.f 
       let v = nextSingle() * 2.f - 1.f 
       let s = pown u 2 + pown v 2 
       if s >= 1.f || s = 0.f then loop() else 
       u, v, s 
      let u, v, s = loop() 
      let mul = (*)(sqrt(-2.f * log s/s)) 
      yield mul u * stdDev + mean 
      yield! mul v |> Some |> gauss 
    } 
    gauss None 

wydaje mi się, że to powinno się tylko wywoływać w pozycji wywołania końcowego, ergo nigdy nie powodować StackOverflowException, gdy TCO jest włączone. Ale to ma podczas pracy z 64-bitowym 642-bitowym. To nie ma wartości podczas uruchamiania 32-bitowego (tj. "Preferuj 32-bitowe" w ustawieniach projektu).

Używam .NET Framework 4.5.2 i F # 4.4.0.0.

Czy ktoś może wyjaśnić, co jest przyczyną problemu?

+1

Którą wersję .NET używasz? – Roujo

+0

Znalazłem te artykuły, mówiąc o optymalizacji połączeń ogonowych [przy użyciu .NET 2.0] (https://blogs.msdn.microsoft.com/davbr/2007/06/20/tail-call-jit-conditions/) vs [using. NET 4.0] (https://blogs.msdn.microsoft.com/clrcodegeneration/2009/05/11/tail-call-improvements-in-net-framework-4/), może mogą pomóc. – Roujo

+0

Również jakiej wersji F # używasz? Używam F # 4 i kiedy debuguję, w ogóle nie widzę stosu. Zdemontowałem kod IL i wygląda na to, że kod generowany przez F # 4 nie powinien być stackoverflow. – FuleSnabel

Odpowiedz

8

Wygląda jak błąd w kompilacji mechanizmu kompilacji wyrażeń sekwencji. Oto uproszczony repro:

let rec loop r = seq { 
    if r > 0 then 
     let rec unused() = unused() 
     yield r 
     yield! loop r 
} 

printfn "%i" (Seq.nth 10000000 (loop 1)) 

Oczywiście obecność niewykorzystanej rekurencyjnej definicji nie powinno mieć wpływu na to, czy generuje przepełnienie stosu, ale robi.

+0

Złożyłem to jako problem na [GitHub] (https://github.com/Microsoft/visualfsharp/issues/996). – kvb

+0

Czy intuicyjne lub nieintuicyjne rozumowanie prowadziło do problemu? Chciałbym wiedzieć, czy to było tylko podstawowe rozwiązywanie problemów przy użyciu inspekcji "IL" w połączeniu z faktoringiem do minimum, które mogę zrobić przy użyciu edycji społeczności VS, czy skorzystałeś z czegoś więcej? Próbuję poprawić swoją zdolność rozwiązywania problemów związanych z TCO. Jeśli chcesz to jako oddzielne pytanie SO, będę szczęśliwy, aby to zrobić. Również od której wersji kodu zacząłeś, GitHub full, GitHub minim lub przykład w pytaniu, ponieważ chcę go samodzielnie obliczyć. –

+0

@GuyCoder - Moje rozumowanie było podobne do tego: mówienie o "ogonie" przy patrzeniu na wyrażenie obliczeniowe może być mylące - naprawdę nie ma czegoś takiego (patrz np. Http://stackoverflow.com/a/6615060/ 82959 dla jednej pokrewnej dyskusji, ale niezwiązanej z wyrażeniami _sequence_). Więc coś takiego jak 'gauss' nie będzie nigdy przepełniać stosu, tylko kod wywołujący go może spowodować przepełnienie. – kvb