Haskell推测并行执行

时间:2010-02-05 10:22:05

标签: haskell concurrency parallel-processing speculative-execution

我正在考虑为我想解决的一个问题利用并行性。问题大致如下:给定输入(点序列)找到最佳输出(由这些点组成的最大三角形,最长线等)。在点序列中有3种不同的“形状”,但我只对“最佳得分”(通常是某种形式的“长度”倍数系数)感兴趣。我们称之为形状S1,S2,S3。

我有两种不同的算法来解决S1 - 'S1a'在O(n 2 ),'S1b'大多表现得更好,但最坏的情况是大约O(n 4功能)。

第一个问题:是否有一些简单的方法可以并行运行S1a和S1b,使用先完成并停止另一个的方法?至于我正在阅读文档,这可以使用一些forkIO编程并在获得结果时杀死线程 - 只是询问是否有更简单的东西?

第二个问题 - 更加困难:我以这种方式调用优化函数:

optimize valueOfSx input

valueOfSx特定于每个形状,并返回“得分”(或得分的猜测)可能的解决方案。优化调用此函数以找出最佳解决方案。我感兴趣的是:

s1 = optimize valueOfS1 input
s2 = optimize valueOfS2 input
s3 = optimize valueOfS3 input
<- maximum [s1,s2,s3]

但是,如果我知道S1的结果,我可以丢弃所有较小的解决方案,从而使得如果没有更好的解决方案(或者至少丢弃最差的解决方案,从而提高空间效率),s2和s3会收敛得更快。我现在在做的是:

zeroOn threshold f = decide .f
    where decide x = if (x < threshold) then 0 else x
s1 = optimize valueOfS1 input
s2 = optimize (zeroOn s1 valueOfS2) input
s3 = optimize (zeroOn (max s1 s2) valueOfS3) input

问题是:我可以运行,例如S2和S3以这种方式并行,无论哪个先完成都会更新在另一个线程中运行的得分函数的'threshold'参数?在某种意义上的东西:

threshold = 0
firstSolution = firstOf (optimize (zeroOn threshold valueOfS2), optimize (zeroOn threshold valueofS3))
update threshold from firstSolution
wait for second solution

2 个答案:

答案 0 :(得分:5)

对于第一个问题,请查看Conal Elliott的unambhttp://hackage.haskell.org/package/unamb

答案 1 :(得分:5)

最终,任何解决方案都将最终使用ForkIO,因为您希望并行发生多个事务。即使是康纳尔也是如此。

对于后者你可能想要一个更复杂的monad,它会批量运行并运行一段时间,然后偶尔检查一个MVar以获得单调发布的改进值,但是交错的最简单答案(在一个线程内)就是编写一个Partiality单子。

data Partial a = Return a | Partial (Partial a)

instance Monad Partial where
    return = Return
    Return a >>= f = f a
    Partial m >>= f = Partial (m >>= k)


run :: Partial a -> a
run (Partial m) = run m
run (Return a) = a

race :: Partial a -> Partial a -> Partial a
race (Return a) _ = a
race _ (Return b) = b
race (Partial m) (Partial n) = race m n

yield :: Partial ()
yield = Partial (Return ())

使用适当的MonadFix实例来处理递归或自由散布的“yield”调用,您可以在Partial monad中执行两个操作并对它们进行竞争以获得确定性结果。

但实际上,如果你想要获得并行性的全部好处,你需要定期更新并检查某种“改进”的MVar。

有点像(关闭袖口,对不起,没有编译器方便!):

import Control.Concurrent.MVar (newMVar, readMVar)
import Control.Parallel.Strategies (using, parList)
import GHC.IOBase (unsafeDupablePerformIO, unsafePerformIO)

diag x = (x,x)

parMax :: (Bounded a, Ord a) => [a] -> a
parMax xs = unsafePerformIO $ do
    threshold <- newMVar minBound
    let optimize x = unsafeDupablePerformIO $
        x `seq` modifyMVar threshold (return . diag . max x)
    return . maximum $ map optimize xs `using` parList

当然,应该能够重写以支持任何幂等的可交换monoid,而不仅仅是max。