You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免元组值递归函数Worker中的提升值以减少堆分配?

如何让GHC为返回元组的函数生成无堆分配的 unlifted 值worker函数?

最小示例代码

import Test.Tasty.Bench

{-# INLINE loop #-}
loop :: Int -> Int -> Int
loop a 0 = a
loop a n = loop (a + x + y) (n - 1) where (x, y) = foo n

{-# INLINE foo #-}
foo :: Int -> (Int, Int)
foo n = if n > 0 then (n + 1, n) else foo (n + 1)

main :: IO ()
main = defaultMain [bench "test" $ whnf (loop 0) (1024 * 1024)]

运行结果

test: OK (0.24s)
  1.88 ms ±  88 μs,  32 MB allocated, 2.8 KB copied, 2.0 MB peak memory

Core Dump分析

查看Core Dump后,得到以下worker函数:

Rec {
-- RHS size: {terms: 18, types: 6, coercions: 0, joins: 0/0}
$wfoo
  = \ ww ->
      case ># ww 0# of {
        __DEFAULT -> $wfoo (+# ww 1#);
        1# -> (# I# (+# ww 1#), I# ww #)
      }
end Rec }

Rec {
-- RHS size: {terms: 26, types: 14, coercions: 0, joins: 0/0}
$wloop
  = \ ww ww1 ->
      case ww1 of ds {
        __DEFAULT ->
          case $wfoo ds of { (# ww3, ww4 #) ->
          case ww3 of { I# y ->
          case ww4 of { I# y1 -> $wloop (+# (+# ww y) y1) (-# ds 1#) }
          }
          };
        0# -> ww
      }
end Rec }

堆分配的根源是$wfoo返回的是lifted Int值,每次调用都会在堆上分配内存。

已尝试的解决方案

  • 添加seq x $ seq y这类严格性注解:对Core Dump无实质影响,仅会重命名绑定变量。
  • 改为让foo返回单个Int:GHC会自动移除所有lifted Int值,程序无堆分配,但不符合生产代码需要返回元组的需求。
  • 使用带严格字段的自定义数据类型data Pair = Pair !Int !Int:可以避免lifted Int值,且Core中会直接使用未装箱元组的unlifted值,但无法用多态严格元组实现,不适用于生产场景。

问题

生产代码必须使用标准元组,因此上述方案均不适用。既然严格字段的数据类型最终会被擦除,本质是一种间接的严格性注解,那是否存在更直接的方式,让GHC为foo生成返回unlifted值的worker函数,从而消除堆分配?

使用环境:GHC 8.10.7、LLVM 13.0.1,编译参数-O2 -fllvm -optlo-O3,运行参数+RTS -T


内容的提问来源于stack exchange,提问作者Jules

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:10:34