openlang

CPS 求和在 Debug 下栈溢出,在 Release 下只有第二个函数溢出

大家好,

我在拿延续传递风格做练习,用的是我做数据工作时的一个小型传感器工具。loadReadings 会生成一个列表,暂时代替我还没写的 CSV 加载器。然后我用两种方式对这些值求和。据我理解,两种写法都让递归调用成为最后执行的操作。

let rec sumK (xs: int list) (k: int -> int) =
    match xs with
    | [] -> k 0
    | x :: rest -> sumK rest (fun s -> k (s + x))

let rec sumCheckedK (xs: Reading list) (k: int -> int) =
    match xs with
    | [] -> k 0
    | r :: rest ->
        try
            if r.Value < 0 then
                failwithf "faulty reading from %s" r.Sensor
            sumCheckedK rest (fun s -> k (s + r.Value))
        with ex ->
            log ex.Message
            sumCheckedK rest k

在 main 里我先调用 sumK values id,再调用 sumCheckedK readings id,读数用默认的 1,000,000 个(每第 250000 个是有问题的 -1)。

  • dotnet run(Debug)几乎立刻就崩溃,报 Stack overflow.,后面跟着一大段重复的堆栈跟踪,连一个总和都没打印出来。
  • dotnet run -c Release 下,sumK 的总和能正确打印,然后又报 Stack overflow.,重复的栈帧里提到了 sumCheckedK。
  • 用 -- 10000 的话,两种构建都正常。

我一开始怀疑是列表构造的问题,但 Release 的运行结果说明这里没问题。后来我又想,是不是 JIT 对深层嵌套的闭包放弃了优化。我没在 fsproj 里设置任何特殊选项(net8.0,.NET 8 SDK,M 系列 Mac 上用 Rider)。

这是一个问题还是两个?我更想搞懂原因,而不是直接把线程栈调大。

从英语翻译查看原文

后续讨论 2

顺着彼此的依据读下去
  1. 固定链接

    我觉得是两个问题。

    第一,Debug 默认关闭尾调用,所以 sumK 在那里会溢出。我猜你的 fsproj 里没有 <Tailcalls> 这一行,所以 Debug 用的就是这个默认值。我之前在一个客户项目里碰到过一模一样的情况,Release 下正常,Debug 下崩了,白白丢了一个下午。

    第二,sumCheckedK 在 try 里面调用自身。那不是尾位置,所以 Release 下也会为每个元素保留一个栈帧。把递归调用移到 try 外面。

    从英语翻译查看原文
  2. 固定链接

    另外,with 里的 sumCheckedK rest k 也不是尾调用。让 try 只负责判断有效还是错误,返回一个 bool 或 option,然后在它后面再做两个递归调用。我自己的代码就是这样改好的。

    从英语翻译查看原文

同一个问题的其他角度

  1. 同一个 Int,两个编译目标,两种结果

    同样的源码,同样的 JSON 文件,不同目标的结果却不一样。这很糟糕。 cohort_join 读取我们跟踪系统导出的 JSON,并按 id 给参与者建立索引。id 有 16 位,因为系统是用时间戳加计数器生成的。我手动检查过文件,这三个 id 是不…

    回复 0最近活动 gleam
  2. pure 宿主函数被缓存了,对参数的写入直接消失了

    周末小项目,一个玩具表达式求值器,宿主函数用 D 写,按名字注册。我在注册表里加了一个 memo 缓存(挺得意的,吹了一整周了)。规则是这样的,如果函数是 pure,就不可能有副作用,所以按名字加打印出来的参数做键,缓存结果。 然后我写了 bump,…

    回复 2最近活动 d
  3. 同样的 parseServices,错误行只在构建时才会出问题?

    内置的表一直不会变,所以我把 auto 改成了 enum,没问题吧?现在 smtp 25(没写 proto)会让 dub build 失败,报错最后停在 stderr writeln 不能在编译期使用。ci.sh 里用 ldc2 也找不到 servi…

    回复 2最近活动 d