Haskell中平行的“任何”或“全部”


9

我现在遇到过一种模式,该模式需要通过在其上映射一些测试并查看是否有任何或所有元素通过来检查值列表。典型的解决方案是使用便捷的内置allany

问题是这些以串行方式进行评估。在许多情况下,这将是快平行的过程被完整的评估,一旦任何线程发现一个“假”的all或“真”的any。我很确定不能使用Control.Parallel来实现短路行为,因为它需要进程间的通信,而且我对Control.Concurrent的理解还不够,无法实现此目的。

这是数学中的一种很常见的模式(例如Miller-Rabin Primality),所以我觉得有人可能已经为此提出了解决方案,但是出于明显的原因,谷歌搜索了“平行或/和//任何/全部在列表中” haskell”不会返回许多相关结果。


1
您可能会发现在Haskell并行和并发编程有用的,特别是第234
bradrn

2
unamb库可以做到这一点
luqui

1
@luqui令人着迷;我会把这个弄乱。如果我为此写了一个很好的并行所有/任何内容,我将其发布为答案。
Arcuritech

11
在尝试并行化任何事物之前,请考虑在派生一个新进程所需的时间内可以测试多少个条件。
chepner

2
@chepner你在说什么?我们这里不是在谈论bash!我们可以对线程进行并发和并行处理(使用pthreadsC语言还是Haskell中的绿色线程),您不必启动多个Web服务器来处理并发Web请求,而是在单个进程中运行多个线程!同样适用于并行性。您可以分配与CPU数量一样多的线程,并平均分配工作,从而照顾CPU绑定的任务。试试这个库来说服自己github.com/lehins/haskell-scheduler
lehins

Answers:


2

在许多实际的程序中,您可以为此使用并行策略。这是因为,即使没有明确的机制可以取消不需要的计算,但当垃圾收集器运行时,这将隐式发生。作为一个具体的示例,请考虑以下程序:

import Control.Concurrent
import Control.Parallel.Strategies
import Data.Int
import System.Mem

lcgs :: Int32 -> [Int32]
lcgs = iterate lcg
  where lcg x = 1664525 * x + 1013904223

hasWaldo :: Int32 -> Bool
hasWaldo x = waldo `elem` take 40000000 (lcgs x)

waldo :: Int32
waldo = 0

main :: IO ()
main = do
  print $ or (map hasWaldo [1..100] `using` parList rseq)

这使用并行列表策略waldo = 0在100个PRNG流的输出中搜索(永远不会被发现),每个PRNG流具有4000万个数字。编译并运行它:

ghc -threaded -O2 ParallelAny.hs
./ParallelAny +RTS -s -N4

它钉住四个内核约16秒钟,最终打印出来False。在统计数据中请注意,所有100个火花都已“转换”,因此可以完成操作:

SPARKS: 100(100 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)

现在,更改waldo为一个可以尽早找到的值:

waldo = 531186389   -- lcgs 5 !! 50000

并进行修改main以使线程保持活动状态10秒钟:

main :: IO ()
main = do
  print $ or (map hasWaldo [1..100] `using` parList rseq)
  threadDelay 10000000

您会观察到它True几乎可以立即打印,但是4个内核仍保持100%CPU占用率(至少有一小段时间),这说明不必要的计算一直在运行,并且不会短路,就像您可能担心的那样。

但是,如果您在得到答案后强制进行垃圾收集,事情就会改变:

main :: IO ()
main = do
  print $ or (map hasWaldo [1..100] `using` parList rseq)
  performGC
  threadDelay 10000000

现在,您将看到CPU在打印后不久就进入空闲状态True,并且统计数据表明大多数计算是在运行之前被垃圾回收的:

SPARKS: 100(9 converted, 0 overflowed, 0 dud, 91 GC'd, 0 fizzled)

在现实的程序中,performGC将不需要显式的,因为理所当然会定期执行GC。找到答案后,一些不必要的计算将继续运行,但是在许多实际情况下,不必要的计算所占比例并不是特别重要。

特别是,如果列表很大,并且列表元素的每个单独测试都很快,那么并行策略将具有出色的实际性能,并且很容易实现讨价还价。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.