ond*_*dra 8 parallel-processing concurrency haskell speculative-execution
我正在考虑为我想解决的一个问题利用并行性.问题大致如下:给定输入(点序列)找到最佳输出(由这些点组成的最大三角形,最长线等).在点序列中有3种不同的"形状",但我只对"最佳得分"(通常是某种形式的"长度"倍数系数)感兴趣.我们称之为形状S1,S2,S3.
我有2种不同的算法来解决S1 - 'S1a'在O(n 2)中,'S1b'大多表现得更好,但最坏的情况是O(n 4).
第一个问题:是否有一些简单的方法可以并行运行S1a和S1b,使用先完成并停止另一个的方法?至于我正在阅读文档,这可以使用一些forkIO编程并在获得结果时杀死线程 - 只是询问是否有更简单的东西?
第二个问题 - 更加困难:我以这种方式调用优化函数:
optimize valueOfSx input
Run Code Online (Sandbox Code Playgroud)
valueOfSx特定于每个形状,并返回"得分"(或得分的猜测)可能的解决方案.优化调用此函数以找出最佳解决方案.我感兴趣的是:
s1 = optimize valueOfS1 input
s2 = optimize valueOfS2 input
s3 = optimize valueOfS3 input
<- maximum [s1,s2,s3]
Run Code Online (Sandbox Code Playgroud)
但是,如果我知道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
Run Code Online (Sandbox Code Playgroud)
现在的问题是:我能以这样的方式如运行S2和S3并行,无论哪完成第一次将更新其他线程运行的得分功能的"门槛"参数?在某种意义上的东西:
threshold = 0
firstSolution = firstOf (optimize (zeroOn threshold valueOfS2), optimize (zeroOn threshold valueofS3))
update threshold from firstSolution
wait for second solution
Run Code Online (Sandbox Code Playgroud)
最终,任何解决方案都将最终使用ForkIO,因为您希望并行发生多个事务.甚至Conal的unamb也是这样的.
对于后者,你可能想要一个更复杂的monad,它可以批量运行并运行一段时间,然后偶尔检查一个MVar以获得单调发布的改进值,但是交错(在一个线程内)的最简单答案就是编写一个Partiality monad.
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 ())
Run Code Online (Sandbox Code Playgroud)
使用适当的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
Run Code Online (Sandbox Code Playgroud)
当然,应该能够重写以支持任何幂等的可交换monoid,而不仅仅是max.