Haskell中的并行映射

40

有没有map的替代品可以并行地对列表进行评估?我不需要它是惰性的。

类似于:pmap :: (a -> b) -> [a] -> [b]让我执行pmap expensive_function big_list,并让所有核心都达到100%。

2个回答

46

是的,可以看一下parallel package

ls `using` parList rdeepseq

将使用 rdeepseq 策略并行评估列表的每个元素。注意,如果您的元素太便宜以至于并行评估每个元素不会带来好处(因为这样可以节省为每个元素激发的开销),则使用适当的块值使用 parListChunk 可能会提供更好的性能。

编辑:根据您的问题,我觉得应该解释一下为什么这是一个答案。那是因为Haskell是惰性的!考虑这个声明

let bs = map expensiveFunction as

还没有进行任何计算。你只是创建了一个将 expensiveFunction 映射的惰性计算表达式(thunk)。那么我们如何并行地对其进行求值呢?

let bs = map expensiveFunction as
    cs = bs `using` parList rdeepseq

现在不要再使用bs列表进行未来的计算,改为使用cs列表。也就是说,您不需要并行映射,而是可以使用常规(惰性)映射和并行评估策略。

编辑:如果您仔细查看,您会发现parMap函数可以将我展示的内容包装成一个帮助函数。

回应您的评论,下面的代码对您不起作用吗?对我有效。

import Control.Parallel.Strategies

func as =
        let bs = map (+1) as
            cs = bs `using` parList rdeepseq
        in cs

1
对我不起作用。出现“没有实例(Control.DeepSeq.NFData b)与'rdeepseq'的使用有关”的情况。 - Clark Gaebel
1
@clark,你必须在特定的上下文或使用显式类型签名时使用它。确保列表的元素具有“NFData”实例-这是使用“rdeepseq”的要求。如果太繁琐,请改用“rseq”,它将评估为WHNF。 - Thomas M. DuBuisson
3
@clark 你是否使用带线程选项(ghc -O2 -threaded blah.hs --make)编译代码,并在运行时使用正确的 RTS 选项(./blah +RTS -Nx,其中 x 是你想要使用的核心数,例如 2)?需要注意的是,在 GHC 7 中,你只需输入 ghc -O2 -threaded -with-rtsopts=-N blah.hs,然后运行 ./blah 即可。 - Thomas M. DuBuisson
当您在映射列表时,是否也适用于您自己定义的数据结构?我尝试了这种方法,但是出现了“没有实例(NFData myDataType)”的错误。 - Astarno
如果类型具有您所需的实例,则它可以正常工作,就像您观察到的那样。 - Thomas M. DuBuisson
显示剩余5条评论

22
除了像Tom所描述的那样使用显式策略之外,parallel还导出了parMap函数。
 parMap :: Strategy b -> (a -> b) -> [a] -> [b]

其中的策略参数是类似于rdeepseq的东西。

而且还有parMap在par-monad包中(您会从纯Haskell步入并行monad):

 parMap :: NFData b => (a -> b) -> [a] -> Par [b]

par-monad 包的 文档在此处


3
这里有一个小注意事项。parMap使用了mapM,它是严格的。这意味着在计算开始之前,列表骨架会被完全评估——如果列表很长,例如您正在对从(巨大)文件中读取的记录进行parMap,那么这可能不是您想要的结果。也许使用一个懒惰的parMap,或者通过循环分发元素会更好。 - Ketil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接