Arrays Repa阵列上的并行mapM
在我最近的《Gibbs采样》(Gibbs sampling)中,我一直在充分利用它,在我看来,它为随机数生成提供了一个近乎理想的接口。遗憾的是,由于无法在地图中使用一元操作,我一直无法使用Repa 虽然显然一元映射一般不能并行化,但在我看来,Arrays Repa阵列上的并行mapM,arrays,haskell,parallel-processing,repa,Arrays,Haskell,Parallel Processing,Repa,在我最近的《Gibbs采样》(Gibbs sampling)中,我一直在充分利用它,在我看来,它为随机数生成提供了一个近乎理想的接口。遗憾的是,由于无法在地图中使用一元操作,我一直无法使用Repa 虽然显然一元映射一般不能并行化,但在我看来,RVar可能至少是一个可以安全地并行化效果的单子示例(至少在原则上;我对RVar的内部工作原理不是很熟悉)。也就是说,我想写下面这样的东西 drawClass :: Sample -> RVar Class drawClass = ... drawC
RVar
可能至少是一个可以安全地并行化效果的单子示例(至少在原则上;我对RVar
的内部工作原理不是很熟悉)。也就是说,我想写下面这样的东西
drawClass :: Sample -> RVar Class
drawClass = ...
drawClasses :: Array U DIM1 Sample -> RVar (Array U DIM1 Class)
drawClasses samples = A.mapM drawClass samples
其中A.mapM
看起来像
mapM :: ParallelMonad m => (a -> m b) -> Array r sh a -> m (Array r sh b)
显然,这将如何工作主要取决于RVar
及其底层RandomSource
的实现,但原则上,人们会认为这将涉及为生成的每个线程绘制一个新的随机种子,并照常进行
直觉上,似乎同样的想法也可以推广到其他一些单子
所以,我的问题是:我们能否构造一个单子类ParallelMonad
,其效果可以安全地并行化(可能至少有RVar
)呢
它看起来像什么?这个类中还有哪些单子?其他人是否考虑过这在Repa中如何工作的可能性
最后,如果并行一元行为的概念不能被推广,有人认为在
RVar
的特定情况下,有什么好的方法可以使这一点起作用吗?为了并行性而放弃RVar是一个非常困难的权衡。由于PRNG固有的顺序性,这样做可能不是一个好主意。相反,您可能希望按如下方式转换代码:
main
,或您拥有的任何函数)这个问题已经问了7年了,但似乎仍然没有人想出一个好的解决方案。Repa没有类似于
mapM
/遍历
的函数,即使是一个可以在没有并行化的情况下运行的函数。此外,考虑到过去几年取得的进展,这种情况似乎也不太可能发生
由于Haskell中许多阵列库的陈旧状态,以及我对它们的功能集的总体不满,我对阵列库进行了几年的研究,该阵列库借用了Repa的一些概念,但将其提升到了一个完全不同的层次。介绍够了
在今天之前,在massiv
中有三个一元地图样函数(不包括同义词样函数:imapM
,forM
等):
- -任意
中的常用映射。由于明显的原因,不可并行化,而且速度有点慢(与通常的Monad
相比,列表速度慢)mapM
- -这里我们仅限于
,它比PrimMonad
快得多,但其原因对于本次讨论并不重要mapM
- -顾名思义,这一个仅限于
(或者更确切地说是IO
,但这与此无关)。因为我们在MonadUnliftIO
中,所以我们可以自动将数组分割成尽可能多的块,并使用单独的工作线程将IO
操作映射到这些块中的每个元素上。与同样可并行的纯IO
不同,由于调度的不确定性以及映射操作的副作用,我们必须在fmap
中IO
- 一个函数,它可以初始化尽可能多的生成器,只要有工作线程
- 以及一个抽象,它将根据操作运行在哪个线程中无缝地为映射函数提供正确的生成器李>
randomArrayWS::
(可变r ix e,单胞菌属m,单胞菌属m)
=>WorkerStates g--^使用“initWorkerStates”初始化每线程生成器
->Sz ix--^数组的结果大小
->(g->me)--^使用每线程生成器生成值。
->m(数组r ix e)
initWorkerStates::MonadIO m=>Comp->(WorkerId->ms)->m(WorkerStates)
对于那些不熟悉massiv
的人来说,参数是一种要使用的计算策略,值得注意的构造函数有:
-按顺序运行计算,不分叉任何线程Seq
-尽可能多地启动线程,并使用这些线程来完成工作Par
RVarT
:
λ>导入数据.Massiv.Array
λ> 导入System.Random.MWC(createSystemRandom,uniformR)
λ> 导入System.Random.MWC.Distributions(标准)
λ> gens createSystemRandom(随机)
在上面,我们使用系统随机性为每个线程初始化了一个单独的生成器,但是我们也可以通过从参数派生出一个唯一的每个线程种子,它只是worker的Int
索引。现在我们可以用这些发生器来