计算应该有多重要才能使其在 Haskell 中引发并行执行是合理的?
How non-trivial should a computation be to make it reasonable to get it sparked for a parallel execution in Haskell?
根据 doumentation for Control.Parallel
,应该确保被激发的计算是重要的,这样创建火花比计算本身更便宜。
这是有道理的,但在听了 Bartosz Milewski 谈论火花有多便宜之后,我想知道经验丰富的 Haskell 程序员如何确定计算是否值得并行处理。
这个主题是事实,而不是基于观点。
阅读前请先拍:
".. creating spark doesn't immediately wakeup idle capability, see here. By default scheduling interval is 20ms, so when you create a spark, it will take up to 20 ms to turn it to a real thread. By that time the calling thread most likely will already evaluate the thunk, and the spark will be either GC'd or fizzled.
By contrast, forkIO
will immediately wakeup idle capability if any. That is why explicit concurrency is more reliable then parallel strategies."
因此,请记住将 forkIO
生成的功能块的基准成本添加 +20 毫秒 and/or 到下面引用的实际可实现的附加开销 cost/benefit(加速 ) 公式。
这个问题早在几十年前就被 Gene AMDAHL 博士解决了
更近几代的 C/S 学生或从业者似乎以某种方式忘记了基本的流程调度逻辑(即规则,而不是艺术,正确组织代码执行流程系统的受限物理资源)。
虽然一个公平的反对意见可能并且将会来自函数式语言的性质,其中 lambda 演算可以并且经常利用 space,否则对于命令式语言来说是隐藏的,为了进入一个聪明,好的-grain,并行性,从 lambda- 或 pi- 演算的法则中派生出来。
然而,核心信息仍然存在并存在了 60 多年。
关于这一点的定量、公平、基于证据记录的理由就足够了:(没有魔法,没有任何性质的隐藏艺术)
请首先尝试尽自己最大的努力首先充分理解 original formulation of the Amdahl's Law, plus kindly also revise the recent criticism 和开销严格、资源感知的原始、普遍有效的通用系统调度法则的重新表述。
在 [Criticism ] 部分中添加的内容旨在完全匹配实际发生的情况,当有人对一段代码提出 "re-organise" 的想法时计算图并进入流程,其中一些 "just"-[CONCURRENT]
or true-[PARALLEL]
计算语法构造函数(无论实际的代码执行工具是什么)。
得到overhead-strict Amdahl's Law theory之后,我们来测量一下:
这部分简单且系统化:我的学生经常抱怨,但展望未来,他们每个人都收集了一系列实践经验,实际需要(以成本计)进入任何形式的使用承诺并行代码执行。
1 ) 创建一个 NOP 函数 - 一个实际上什么都不做的函数,除了是 运行 (没有义务通过任何,越少在数量、参数方面非常出色,在其(空)代码执行期间没有尝试分配任何一位内存,也没有 returning 任何值 "back")。这是一个理想化的 NOP 函数负载,让它被生成/激发/分发到选择驱动执行的并行工具中。
准备好 NOP-fun,让我们在多个实例中对这种 NOP-fun 代码执行的纯开销进行基准测试,并测量它所花费的时间。
确保所有此类实例确实什么都不做 "there",两条时间线之间花费的时间总和是——太棒了——并行化和进程重新收集的纯开销成本管理费用。
如此简单,如此容易。
不同的工具在用户程序将产生多少成本方面有所不同,但指标和方法都是 crystal-明确的。
CuT_start <- liftIO $ getCurrentTime -- a Code_Under_Test___START
-- I use a ZeroMQ Stopwatch() instance
-- having a better than [us] resolution
-- but haven't found it in Haskell binding
--CuT_TIMING_CRITICAL_SECTION_/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
<_CuT_a_Code_syntax-constructor_Under_Test_>
--CuT_TIMING_CRITICAL_SECTION_/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
CuT_end <- liftIO $ getCurrentTime -- a Code_Under_Test___END
liftIO $ print ( diffUTCTime CuT_end CuT_start )
2 ) 已经记录了产生/激发预期工作量的净成本,可以转发:
- 添加 "remote" 内存分配(字面上直到交换杀死 O/S )
- 添加 "remote" CPU 绑定处理(再次,就人们闻到 O/S 内核为创建一些可行的硬件线程映射而进行的调度工作的疲劳而言)
- 添加 "remote" 根据调用接口缩放(需要从调用者传递到被调用者的数据量)依赖项的过程测试
- 添加 "remote" 进程 return 值
- 添加 "remote" 进程需要访问一些共享资源
最终决定走 - 不走:
所有这些,以上收集和记录的附加成本,只是增加了现实世界的代码开销成本,必须在最近输入,re-formulated Amdahl's Law。
当且仅当
开销严格,资源感知 speedup 结果是 >> 1.00 进入并行代码执行是有意义的
在所有情况下,其中
“改进”加速实际上是 <= 1.00 支付确实是一个非常糟糕的主意不止一个人从这样一个 "improvement"
(相反的公式总是可能的——推导最少的处理量,这将至少证明使用相应类型的并行代码语法构造函数的上述系统基准成本是合理的)
Q.E.D.
根据 doumentation for Control.Parallel
,应该确保被激发的计算是重要的,这样创建火花比计算本身更便宜。
这是有道理的,但在听了 Bartosz Milewski 谈论火花有多便宜之后,我想知道经验丰富的 Haskell 程序员如何确定计算是否值得并行处理。
这个主题是事实,而不是基于观点。
阅读前请先拍
".. creating spark doesn't immediately wakeup idle capability, see here. By default scheduling interval is 20ms, so when you create a spark, it will take up to 20 ms to turn it to a real thread. By that time the calling thread most likely will already evaluate the thunk, and the spark will be either GC'd or fizzled.
By contrast,
forkIO
will immediately wakeup idle capability if any. That is why explicit concurrency is more reliable then parallel strategies."
因此,请记住将 forkIO
生成的功能块的基准成本添加 +20 毫秒 and/or 到下面引用的实际可实现的附加开销 cost/benefit(加速 ) 公式。
这个问题早在几十年前就被 Gene AMDAHL 博士解决了
更近几代的 C/S 学生或从业者似乎以某种方式忘记了基本的流程调度逻辑(即规则,而不是艺术,正确组织代码执行流程系统的受限物理资源)。
虽然一个公平的反对意见可能并且将会来自函数式语言的性质,其中 lambda 演算可以并且经常利用 space,否则对于命令式语言来说是隐藏的,为了进入一个聪明,好的-grain,并行性,从 lambda- 或 pi- 演算的法则中派生出来。
然而,核心信息仍然存在并存在了 60 多年。
关于这一点的定量、公平、基于证据记录的理由就足够了:(没有魔法,没有任何性质的隐藏艺术)
请首先尝试尽自己最大的努力首先充分理解 original formulation of the Amdahl's Law, plus kindly also revise the recent criticism 和开销严格、资源感知的原始、普遍有效的通用系统调度法则的重新表述。
在 [Criticism ] 部分中添加的内容旨在完全匹配实际发生的情况,当有人对一段代码提出 "re-organise" 的想法时计算图并进入流程,其中一些 "just"-[CONCURRENT]
or true-[PARALLEL]
计算语法构造函数(无论实际的代码执行工具是什么)。
得到overhead-strict Amdahl's Law theory之后,我们来测量一下:
这部分简单且系统化:我的学生经常抱怨,但展望未来,他们每个人都收集了一系列实践经验,实际需要(以成本计)进入任何形式的使用承诺并行代码执行。
1 ) 创建一个 NOP 函数 - 一个实际上什么都不做的函数,除了是 运行 (没有义务通过任何,越少在数量、参数方面非常出色,在其(空)代码执行期间没有尝试分配任何一位内存,也没有 returning 任何值 "back")。这是一个理想化的 NOP 函数负载,让它被生成/激发/分发到选择驱动执行的并行工具中。
准备好 NOP-fun,让我们在多个实例中对这种 NOP-fun 代码执行的纯开销进行基准测试,并测量它所花费的时间。
确保所有此类实例确实什么都不做 "there",两条时间线之间花费的时间总和是——太棒了——并行化和进程重新收集的纯开销成本管理费用。
如此简单,如此容易。
不同的工具在用户程序将产生多少成本方面有所不同,但指标和方法都是 crystal-明确的。
CuT_start <- liftIO $ getCurrentTime -- a Code_Under_Test___START
-- I use a ZeroMQ Stopwatch() instance
-- having a better than [us] resolution
-- but haven't found it in Haskell binding
--CuT_TIMING_CRITICAL_SECTION_/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
<_CuT_a_Code_syntax-constructor_Under_Test_>
--CuT_TIMING_CRITICAL_SECTION_/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
CuT_end <- liftIO $ getCurrentTime -- a Code_Under_Test___END
liftIO $ print ( diffUTCTime CuT_end CuT_start )
2 ) 已经记录了产生/激发预期工作量的净成本,可以转发:
- 添加 "remote" 内存分配(字面上直到交换杀死 O/S )
- 添加 "remote" CPU 绑定处理(再次,就人们闻到 O/S 内核为创建一些可行的硬件线程映射而进行的调度工作的疲劳而言)
- 添加 "remote" 根据调用接口缩放(需要从调用者传递到被调用者的数据量)依赖项的过程测试
- 添加 "remote" 进程 return 值
- 添加 "remote" 进程需要访问一些共享资源
最终决定走 - 不走:
所有这些,以上收集和记录的附加成本,只是增加了现实世界的代码开销成本,必须在最近输入,re-formulated Amdahl's Law。
当且仅当
开销严格,资源感知 speedup 结果是 >> 1.00 进入并行代码执行是有意义的
在所有情况下,其中
“改进”加速实际上是 <= 1.00 支付确实是一个非常糟糕的主意不止一个人从这样一个 "improvement"
(相反的公式总是可能的——推导最少的处理量,这将至少证明使用相应类型的并行代码语法构造函数的上述系统基准成本是合理的)
Q.E.D.