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

Haskell并行策略疑问:fun1与fun2为何串行执行?

问题核心:主线程抢先求值导致spark被丢弃

你遇到的问题本质是**rparWith rdeepseq会让主线程立即把目标表达式求值到完全范式,刚生成的spark还没机会被其他线程调度就直接作废了**。咱们一步步拆解来看:

为什么fun1和fun2没有spark?

先看rparWith的实际行为:它的定义等价于do { rpar x; s x }——也就是先给x生成一个spark,然后立刻对x应用策略s。

当s是rdeepseq时:

  • rdeepseq x会立即在主线程把x求值到完全范式(对于Just (sum [1..x])来说,就是计算完sum的结果)。
  • 这时候刚生成的spark(原本负责求值x)就完全没用了——主线程已经把活干完了,spark会被直接丢弃,所以你看不到任何spark统计。

你的fun1用了parList rdeepseq,而parList本身就是evalList (rparWith s),所以和fun2的evalList (rparWith rdeepseq)是完全一样的逻辑,自然都不会生成有效spark。

为什么fun3能正常生成spark?

fun3用的是evalList (rpar . force),这个组合的行为和rparWith rdeepseq完全不同:

  • (rpar . force) x等价于rpar (force x)——这里是把force x作为spark的任务,而不是先生成spark再让主线程求值。
  • force x是一个未求值的thunk(任务是把x求值到范式),rpar会为这个thunk生成spark,主线程不会立即执行它,所以spark可以被其他核心的线程调度执行,最终你能看到正常的spark统计。

书中parPair例子的问题

书中的parPair rdeepseq rdeepseq本质是evalPair (rparWith rdeepseq) (rparWith rdeepseq),和fun1/fun2犯了同样的错误:

  • 对元组的每个元素,都是先生成spark,然后主线程立刻用rdeepseq把元素求值到范式。
  • 主线程抢先完成了所有计算,spark根本没机会被调度,所以Threadscope只能看到单核心运行,也没有spark生成。

怎么修改才能让例子并行执行?

如果想让这些例子真正生成spark并行执行,需要让主线程不要抢先求值,把求值任务留给spark:

  1. 把rparWith rdeepseq换成rpar . force,比如:
    fun1Fixed :: [Maybe Integer]
    fun1Fixed = map f l `using` parList (rpar . force)
    
  2. 对于书中的parPair例子,修改策略为:
    parPairForce :: (NFData a, NFData b) => Strategy (a,b)
    parPairForce = evalPair (rpar . force) (rpar . force)
    
    然后用这个策略代替parPair rdeepseq rdeepseq,就能看到spark生成和多核心运行了。

另外要记得运行程序时加上+RTS -N参数来启用并行(比如./your-program +RTS -N4用4个核心),不过你能看到fun3的spark,说明已经加过了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:16:16