跟踪“地图”的进度

时间:2015-01-19 01:02:46

标签: haskell parallel-processing

我有一个map操作(实际上是使用来自parMap的{​​{1}}并行运行)需要很长时间。鉴于我知道函数的应用次数(在此上下文中为Control.Parallel.Strategies),我如何能够轻松地每隔一段时间显示已评估了多少n个应用程序?

显而易见的解决方案是在映射函数中将地图设为n并使用mapM,但这样做:

  • 采取不必要的效率
  • 不是偶尔对每个应用程序中的状态进行采样
  • 基本上删除了并行上下文中关于确定性算法的所有好处

那么,有没有办法跟踪这些我遗漏的信息,避免这些问题?

2 个答案:

答案 0 :(得分:2)

在生产中,您可能不应该使用跟踪并被迫处理需要IO的复杂情况,但是对于测试,您可以修改parMap的定义以获取另一个参数,告知何时发出计数:

import Control.Monad (sequence)
import Control.Parallel.Strategies (Strategy, using, rseq, rparWith, parMap)
import Debug.Trace (traceShow)
import System.IO (hFlush, hSetBuffering, BufferMode(NoBuffering), stdout)

evalList' :: Integer -> Strategy a -> Strategy [a]
evalList' t s as = sequence $ foldr f [] $ zip as [1..]
  where f (a, n) ss | n `mod` t == 0 = s (traceShow n a):ss
                    | otherwise      = s a:ss

parList' :: Integer -> Strategy a -> Strategy [a]
parList' t s = evalList' t (rparWith s)

parMap' :: Integer -> Strategy b -> (a -> b) -> [a] -> [b]
parMap' t s f xs = map f xs `using` parList' t s

-- some work to do
fib :: Integer -> Integer
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib(n-2)

main = do hSetBuffering stdout NoBuffering
          print $ sum (parMap' 1000 rseq (fib.(+20).(`mod` 5)) ([1..10000]::[Integer]))

如果每个列表元素给出的工作包变小,则可以相应地调整parListChunk。

答案 1 :(得分:0)

可以尝试使用timeout来制作此行为。

seconds :: Int
seconds = 1000000

progress :: [a] -> IO ()
progress [] = return ()
progress l@(x:xs) =
  do r <- timeout (5 * seconds) x  -- 5s
     threadDelay (2 * seconds)     -- 2s more delay
     case r of
       Nothing -> progress l  -- retry
       Just y  -> do putStrLn "one done!"
                     progress xs

要小心,因为我担心timeout正在中止计算。如果有另一个评估x的线程应该没问题,但如果这是唯一的线程,如果5秒还不够,可能会导致活锁。