《Haskell并行与并发编程》——第2章,第2.2节Eval monad、rpar和rseq

本文涉及的产品
视频直播,500GB 1个月
简介:

本节书摘来自异步社区《Haskell并行与并发编程》一书中的第2章,第2.2节Eval monad、rpar和rseq,作者【英】Simon Marlow,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.2 Eval monad、rpar和rseq
Haskell并行与并发编程
下面介绍模块Control.Parallel.Strategies提供的用于并行编程的一些基本内容,定义如下:

data Eval a
instance Monad Eval
runEval :: Eval a -> a
rpar :: a -> Eval a
rseq :: a -> Eval a

并行性是通过Eval monad表达的,具体包括rpar和rseq两个运算。组合子rpar用于描述并行,即其参数可以并行求值;而rseq则用于强制串行求值,即对其参数求值并等待结果。两者的求值的结果都是弱首范式。rpar的参数不必是未求值的计算,即thunk,若参数是已经被求值的,则不会发生任何事情,因为没有东西需要并行计算。

Eval monad提供了runEval运算用于执行Eval计算,然后返回结果。值得注意的是,runEval是纯函数,没有副作用,无需在IO monad中使用。

为了观察rpar和rseq的效果,假设有一个函数f,以及两个被f应用的参数x和y,而且f x算得比f y慢,希望能够并行的计算f x和f y的结果。下面会使用几种不同的方法编写代码,然后研究它们间的区别。首先,对f x和f y使用rpar,然后返回一对结果,如例2-1所示。

例2-1 rpar/rpar

runEval $ do
   a <- rpar (f x)
   b <- rpar (f y)
   return (a,b)

该代码片断执行的情况如图2-5所示。
图2-5 rpar/rpar的时间线


8e3a6507bd26f4398f9fb7d888b3ac9c851f4757

从图2-5中可以看到f x和f y同时开始求值,而return这句也是立刻被执行的:并不等待f x或f y完成求值。在f x和f y开始并行求值的同时,剩下的程序接着执行。

下面尝试另一种写法,将第二个rpar换成rseq。

例2-2 rpar/rseq

runEval $ do
   a <- rpar (f x)
   b <- rseq (f y)
   return (a,b)

执行后,结果如图2-6所示。
图2-6 rpar/rseq的时间线


739979a5372b819261bd7d1836d8cf8a2615de76

图2-6中f x和f y仍然并行求值,但最后的return是f y完成后才执行的。这是因为使用了rseq,该函数会在返回前等待其参数完成求值。

若添加一个额外的rseq等待f x,则会等待f x和f y都完成。

例2-3 rpar/rseq/rseq

runEval $ do
   a <- rpar (f x)
   b <- rseq (f y)
   rseq a
   return (a, b)

需要注意的是,新的rseq是应用于a,第一个rpar的结果。结果如图2-7所示。

上述代码直到f x和f y都完成求值后才返回。

对使用的模式,应如何选择?

由于程序员很少会提前知道哪个计算最耗时,因此在两个计算中任意等待一个是毫无道理的,所以rpar/rseq的模式不太有用。

图2-7 rpar/rseq/rseq的时间线


3e68b6a1c8c36ade1b3401e95deadff0642822b4

对于rpar/rpar和rpar/rseq/rseq两种模式的选择,则视具体情况而定。如果期望尽早开始更多的并行计算,而且返回值不依赖任何运算的结果,那么使用rpar/rpar是合理的。反而言之,如果所有可能的并行计算都已经开始了,或下面的代码需要用到其中一个运算的结果,那么显然应该使用rpar/rseq/rseq。
下面是最后一种写法。

例2-4 rpar/rpar/rseq/rseq

runEval $ do
   a <- rpar(f x)
   b <- rpar(f y)
   rseq a
   rseq b
   return (a, b)

这段代码和rpar/rseq/rseq的行为是一样的,等待两个求值完成后再返回。虽然该写法是最长的,但和其他写法相比,显得更为对称,基于该原因,这个写法可能更加可取。

通过范例程序rpar.hs可以试验这些不同的写法,程序使用Fibonacci函数模拟并行运行的大量的计算。GHC需要-threaded参数才能支持并行,程序请按下面的方式编译:

$ ghc -O2 rpar.hs -threaded
按如下方式,可以试验rpar/rpar写法,其中+RTS -N2标志告诉GHC使用双核来运行程序(请确保机器至少是双核的):

$ ./rpar 1 +RTS -N2
time: 0.00s
(24157817,14930352)
time: 0.83s
当rpar/rseq代码片断返回1,打印第一行时间戳,第二行时间戳则在最后的计算结束后打印。正如所看到的,代码片断是立即返回的。在rpar/rseq中,第二个计算(短的那个)完成后才返回:

$ ./rpar 2 +RTS -N2
time: 0.50s
(24157817,14930352)
time: 0.82s
在rpar/rseq/rseq中,返回是在最后的:

$ ./rpar 3 +RTS -N2
time: 0.82s
(24157817,14930352)
time: 0.82s

1这里指的是包含rpar和rseq的代码片断,而非rpar/rseq模式。——译者注

相关文章
|
2月前
|
Python
解释Python中的并发编程和并行编程之间的区别。
解释Python中的并发编程和并行编程之间的区别。
|
4天前
|
分布式计算 并行计算 安全
在Python Web开发中,Python的全局解释器锁(Global Interpreter Lock,简称GIL)是一个核心概念,它直接影响了Python程序在多线程环境下的执行效率和性能表现
【6月更文挑战第30天】Python的GIL是CPython中的全局锁,限制了多线程并行执行,尤其是在多核CPU上。GIL确保同一时间仅有一个线程执行Python字节码,导致CPU密集型任务时多线程无法充分利用多核,反而可能因上下文切换降低性能。然而,I/O密集型任务仍能受益于线程交替执行。为利用多核,开发者常选择多进程、异步IO或使用不受GIL限制的Python实现。在Web开发中,理解GIL对于优化并发性能至关重要。
22 0
|
2月前
|
并行计算 调度 开发者
深入浅出Python协程:提升你的异步编程效率
在当今快速发展的软件开发领域,异步编程已成为提高程序性能和用户体验的关键技术。Python,作为一门广泛使用的高级编程语言,其协程(Coroutine)功能为开发者提供了强大的异步编程工具。本文将从协程的基本概念入手,通过实例深入浅出地讲解如何在Python中有效利用协程来提升异步编程的效率和可读性。我们将探讨协程的工作原理、与传统多线程/多进程相比的优势,以及如何在实际项目中应用协程来解决复杂的并发问题。通过本文的学习,读者将能够掌握Python协程的核心知识,为构建高效、可维护的异步应用奠定坚实基础。
|
2月前
|
程序员 数据处理 调度
深入浅出Python协程:提高异步编程效率
在本文中,我们将探讨Python协程的概念及其在异步编程中的应用。不同于传统的摘要,我们采用故事化的方式引入主题,设想一个开发者Alice面临的问题:如何在保持代码可读性的同时,大幅提升数据处理任务的执行效率。通过对协程的深入分析,我们将展示如何利用Python的asyncio库来解决Alice的难题,从而揭示协程技术在现代编程中的重要性和实用价值。
26 1
|
2月前
|
调度 开发者 Python
深入浅出Python协程:提升异步编程效率
在当今的软件开发领域,异步编程已成为提升应用性能和响应速度的关键。本文将探讨Python中的协程(Coroutine)机制,一种轻量级的线程替代方案,用于实现高效的异步编程。我们将从协程的基本概念出发,深入其工作原理,并通过实例演示如何在实际项目中应用协程来优化性能。此外,文章还将比较协程与传统多线程编程的差异,帮助读者更好地理解协程在异步编程中的优势。
|
2月前
|
Python
Python 的异步编程:什么是协程(Coroutine)和生成器(Generator)之间的区别?
Python 的异步编程:什么是协程(Coroutine)和生成器(Generator)之间的区别?
Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现
普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态。而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手段,就是生成器,或者说的更具体一些:协程就是一种特殊的生成器,而生成器,就是协程的入门心法。
Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现
|
缓存 开发者 Python
3_python高阶_协程—yield实现多任务
python高阶_协程—yield实现多任务
130 0
|
Python
5_python高阶_协程—gevent实现多任务(重点)
5_python高阶_协程—gevent实现多任务(重点)
176 0
|
Python
4_python高阶_协程—greenlet实现多任务
python高阶_协程—greenlet实现多任务
178 0

热门文章

最新文章