.net - 为什么堆栈的深度使用会导致简单解释器的超线性时间行为?

标签 .net memory f#

type Expr =
    | Lit of int
    | Add of Expr * Expr

let rec intr = function
    | Lit _ as x -> x
    | Add(Lit a,Lit b) -> Lit <| a + b
    | Add(a,b) -> intr <| Add(intr a, intr b)

let rec intr_cps x ret =
    match x with
    | Lit _ as x -> ret x
    | Add(Lit a,Lit b) -> Lit (a + b) |> ret
    | Add(a,b) -> 
        intr_cps a <| fun a ->
            intr_cps b <| fun b ->
                intr_cps (Add(a, b)) ret

let rec add n =
    if n > 1 then Add(Lit 1, add (n-1))
    else Lit 1

open System.Threading

let mem = 1024*1024*512 // ~536mb
// It stack overflows without being spun on a separate thread.
// By default, the program only has a few mb of stack memory at its disposal.
let run f = Thread(ThreadStart f,mem).Start() 

run <| fun _ ->
    let f n =
        let x = add n
        let stopwatch = System.Diagnostics.Stopwatch.StartNew()
        printfn "%A" (intr x)
        printfn "n_%i_std = %A" n stopwatch.Elapsed

        stopwatch.Restart()
        printfn "%A" (intr_cps x id)
        printfn "n_%i_cps = %A" n stopwatch.Elapsed
    f <| 1000*1000/2
    f <| 1000*1000
    f <| 1000*1000*2

//Lit 500000
//n_500000_std = 00:00:00.7764730
//Lit 500000
//n_500000_cps = 00:00:00.0800371
//Lit 1000000
//n_1000000_std = 00:00:02.9531043
//Lit 1000000
//n_1000000_cps = 00:00:00.1941828
//Lit 2000000
//n_2000000_std = 00:00:13.7823780
//Lit 2000000
//n_2000000_cps = 00:00:00.2767752

我有一个更大的解释器,我试图更好地理解它的性能行为,所以我做了上面的事情。我现在确信我在一些示例中看到的超线性时间缩放与它使用堆栈的方式有关,但我不确定为什么会发生这种情况,所以我想在这里问。

正如您所看到的,当我将 n 改变 2 倍时,时间变化远不止于此,而且似乎缩放是指数级的,这令我感到惊讶。同样令人惊讶的是,CPS 的解释器比基于堆栈的解释器更快。这是为什么?

我还想问,如果我用非 .NET 语言甚至 C 语言编写与上述内容等效的代码,是否会看到相同的行为?

最佳答案

看起来您正在测量的大部分内容都是在构建数据结构。因式分解

let data = add n

在时间测量之外(并将add n 替换为data),CPS 呈线性。

我对具有大堆栈的线程和内存性能了解不够,无法立即解释其余部分,也没有分析内存来获得任何感觉。

关于.net - 为什么堆栈的深度使用会导致简单解释器的超线性时间行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45662291/

相关文章:

c++ - Linux 分配器不会释放小块内存

f# - 通过 F# 的交互窗口添加引用

arrays - 如何在 F# 中添加两个数值数组

c# - 是否可以从 C# 重定向 stdio sprintf_s?

.net - Mono for Android Project 不适用于面向 .NET 框架 4 的库

memory - 在 Forth 中使用 HERE 作为临时空间

haskell - Monad 还可以测量副作用

.net - XmlDocument.CreateElement ("prefix:child") 没有设置 NamespaceURI

c# - .net 中的字符大小不符合预期?

arrays - Swift 分配指针数组