【问题标题】:Haskell parallelisation and strict evaluation in thread..?Haskell并行化和线程中的严格评估..?
【发布时间】:2018-03-16 12:23:20
【问题描述】:

我试图从给定的数字列表中找到素数。 到目前为止,我有一段代码可以工作,但是如果我取消注释某些行并注释其他一些行,我看不出速度有任何差异。 我几乎可以肯定我必须在单独的线程中强制评估,因为我认为我启动线程但由于懒惰而没有在那里评估代码。但我找不到强制进行评估的方法。我正在根据示例here 工作。所以我创建了函数parMapstrMap,它们是并行映射和严格的[并行] 映射。在parMap 中有 2 行注释,因此如果您取消注释它们,并注释掉当前未注释的其他 4 行,那么您不会注意到速度上的任何差异,尽管它应该是非并行的并且速度较慢。我现在也忽略了 main 函数中的程序参数。

所以基本上我的问题是 - 是否有可能实现,对于列表中提供给 parMap 的每个数字,都会产生一个新线程,因此一切工作得更快?

代码如下:

module W7T5
(
  main
) where

import Control.Concurrent
import Control.Parallel (par, pseq)
import System.Environment

main = do
  args' <- getArgs
  let
--    args = map (\x -> read x :: Int) args'
    args = [2000000..2000200]
    tfPrime = parMap isPrime' args
--    tfPrime = strMap isPrime' args
    argsNtf = zip args tfPrime
    primes' = filter (\(num, tfPrime) -> tfPrime) argsNtf
    primes = map fst primes'
  putStrLn ("Init list: " ++ show args)
  putStrLn ("Primes   : " ++ show primes)

-- Map in parallel
parMap :: NFData a => (a -> b) -> [a] -> [b]
parMap _ [] =
  []
--parMap f (x:xs) = -- sadly without any parallelisation it's not slower
--  (f x) :parMap f xs
parMap f (x:xs) =
  par r (r:parMap f xs)
  where
    r = f x

-- Map in parallel strictly
strMap :: (a -> b) -> [a] -> [b]
strMap f xs =
  forceList xs `seq` map f xs

forceList :: [a] -> ()
forceList (x:xs) =
  xs `pseq` forceList xs
forceList _ =
  ()

isPrime' :: Int -> Bool
isPrime' 0 = True
isPrime' 1 = True
isPrime' 2 = True
isPrime' num =
  all (/=0) [mod num x | x <- [2..(num-1)]]

你可以运行程序

runhaskell W7T5.hs 1 2 3 4

【问题讨论】:

  • 你确定你使用超过 1 个线程吗? ghc 有一个选项“-thread”,用于编译超过 1 个线程。编译后的程序采用“+RTS -N2”选项在两个内核上运行。不知道如何使用 runhaskell 来执行此操作。

标签: haskell parallel-processing


【解决方案1】:

为了速度(这是并行性的重点),Haskell 程序应该编译(使用ghc)而不是解释(使用runghc)。如果可能的话,我不知道如何实际使用runghc 进行多线程处理。

ghc W7T5 -threaded -with-rts -N2
./W7T5

parMap 的实现不正确:计算(r : parMap f xs) 立即返回并且只是在尾部敲击,只有在需要时才会触发(但到那时为时已晚)。下面的一个在调用头部和尾部之前会触发它们,所以当调用者看到构造函数时,列表的其余部分正在后台进行评估。

parMap :: (a -> b) -> [a] -> [b]
parMap f [] = []
parMap f (x : xs) = rs `par` r `par` (r : rs)
  where
    r = f x
    rs = parMap f xs

当您编译程序时,您可能不会看到与解释器相同的缓冲行为,因为默认情况下可执行文件使用行缓冲。可以使用System.IO.hSetBuffering关闭缓冲

import System.IO

main = do
  hSetBuffering stdout NoBuffering
  ...

【讨论】:

最近更新 更多