【问题标题】:How Haskell handles parallel computing on a multicore machine/clusterHaskell 如何在多核机器/集群上处理并行计算
【发布时间】:2017-11-07 16:09:07
【问题描述】:

我正在考虑一种新的语言来学习那些日子,用于在我们拥有的计算机集群上进行高性能计算,在这些语言中,我正在考虑使用 Haskell。

我读过一些关于 Haskell 的文章,但仍然有关于在高性能和分布式计算中使用 Haskell 的问题,该语言以该语言而闻名,但我读到了一些关于 Haskell 因懒惰而不适用于这类系统的争论,我可以将我的问题总结为以下几行:

  1. Haskell 使用绿色线程,这对于处理大量并发连接非常有用,但是当其中一个任务花费的时间比平均时间长并阻塞其余任务时会发生什么,整个线程阻塞(Node.js 样式),将下一个任务转发到另一个处理器/线程(Golang),使用缩减技术(Erlang),在预定数量的滴答声后将任务踢出处理上下文,或者其他?

  2. 在分布式计算环境中,惰性求值函数会发生什么情况,是否必须强制严格?

  3. 如果一个函数/模块需要严格评估,但它依赖于其他惰性函数/模块,我是否应该修改其他函数/模块的代码使其也严格,或者编译器会处理给我并强制该链中的所有内容严格或懒惰。

  4. 在处理非常大的数据序列时,Haskell如何处理并行处理,是遵循某种隐式map-reduce技术,还是我自己做了。

  5. 语言中是否有一个集群抽象,它为我处理计算能力,自动将下一个任务转发到空闲处理器,无论它是在同一台计算机上还是同一集群中的另一台计算机上.

  6. Haskell 如何确保公平地分配工作到同一台计算机或可用集群上的所有可用内核。

【问题讨论】:

    标签: multithreading haskell concurrency cluster-computing


    【解决方案1】:
    1. GHC 使用一个可用工作池(称为 sparks)和一个work-stealing 系统:当线程耗尽工作时,它会在池中或在它可以窃取的其他线程的工作队列。

    2. 没有像(比如说)Erlang 那样对分布式计算的内置支持。语义是您的实现定义的任何内容。您可以查看现有的实现,例如 Cloud Haskell

    3. 两者都不是。 Haskell 将自动执行任何必要的工作来提供所需的值,仅此而已。

    4. Haskell(尤其是 GHC)不会做任何事情来自动并行化评估,因为没有已知的通用并行化策略严格来说比不并行化更好。更多信息请参见Why is there no implicit parallelism in Haskell?

    5. 没有。见(2)。

    6. 对于同一台机器,它使用火花池和上述工作窃取系统。没有“集群”的概念。

    有关 Haskell 中并行和并发编程的概述,请参阅 GHC 运行时系统的主要作者 Simon Marlow 的 free book of the same name

    【讨论】:

    • 在阅读您的回答时,一个问题突然出现在我的脑海中。 Haskell 有没有一种方法可以自动创建与一台机器上可用的内核数一样多的操作系统线程?
    • @securecurve 通过设置 RTS 选项 -N,可以作为可执行文件的命令行参数或编译器标志。见stackoverflow.com/questions/3704430/…
    • 我还应该提到,(1)中的“线程”实际上是capabilities,而不是绿色线程。绿色线程被调度到功能上,而这些功能又映射到核心上。
    【解决方案2】:

    多线程

    就 SMP parallelism 而言,Haskell 非常有效。它是not quite automatic,但parallel library 使得并行化几乎任何事情都变得非常容易。因为 spark 太便宜了,所以你可能会很粗心,只要求 很多 并行性;然后运行时将知道该做什么。
    与大多数其他语言不同,如果您拥有高度分支的数据结构、棘手的动态算法等,这不是什么大问题——由于纯函数范式,并行 Haskell 在访问时无需担心锁数据是共享的。

    我认为最大的警告是内存:GHC 的垃圾收集器不是并发的,函数式风格是相当分配快乐的。
    除此之外,还可以编写看起来看似并行的程序,但实际上根本不做任何工作,只是因为懒惰而开始并立即返回。一些测试和经验仍然是必要的。但是惰性和并行并不是不相容的;至少如果你确保你有足够大的“块”严格性的话。强迫一些严格的东西在很大程度上是微不足道的。

    更简单、常见的并行任务(可以用 map-reduce 方式表示,或者经典的数组向量的东西——在许多语言中也很容易)通常可以在 Haskell 中使用并行库更容易处理数据结构;其中最著名的是repa

    分布式计算

    在 Cloud Haskell 上有相当多的工作,它基本上是库形式的 Erlang。这种任务不那么简单:任何显式发送消息的想法有点违背 Haskell 的原则,如果语言过于关注其强大的静态类型(在 Haskell 中通常是这样),工作流的许多方面会变得更加繁琐一个巨大的好处,不仅提高了安全性和性能,而且还使编写更容易)。

    我认为以分布式并发方式使用 Haskell 已经不远了,但我们还不能说它在这个角色上已经成熟了。对于分布式并发任务,Erlang 本身当然是要走的路。

    集群

    老实说,Haskell 在这里根本帮不了你。集群原则上当然是分布式设置的一种特殊情况,因此您可以使用 Cloud Haskell;但在实践中,需求是非常不同的。今天的 HPC 世界(可能在未来相当长的一段时间内)依赖于 MPI,尽管 there is a bit of existing work on MPI bindings,我还没有发现它们可用,至少不是像那样

    MPI 肯定也完全违背了 Haskell 的原则,它的面向 FORTRAN 的数组中心主义、处理类型的奇怪方式等等。但是,除非您对 Haskell 的酷炫功能感到疯狂(尽管它通常如此很诱人!),否则您没有理由不能在 Haskell 中编写典型的数字运算代码。唯一的问题是支持/成熟度,但这是一个相当大的问题;所以对于集群计算,我推荐使用 C++、Python 或 Julia。

    一个有趣的替代方法是从 Haskell 生成 MPI 并行化的 C 或 C++ 代码。 Paraiso 是一个很好的项目。

    梦想成真

    我经常想到在惯用的 Haskell 中可以做些什么来使分布式计算变得可行。 原则上我相信懒惰在这方面可能会有很大的帮助。我设想的模型是让所有机器独立计算相同的程序,但利用 Haskell 评估通常没有预定顺序的事实。该顺序将在每台机器上随机化。此外,运行时将跟踪某个计算分支完成所需的时间,以及结果有多大。如果一个结果被认为既昂贵又紧凑,足以保证它的有效性,那么它将被广播到其他节点,以及一些合适的散列,使它们能够简化计算。

    这样的系统永远不会像手动优化的 MPI 应用程序那样高效,但它至少可以在许多情况下提供相同的渐近性。它可以轻松处理复杂得多的算法。

    但是,这完全只是我对不远的未来的模糊希望。


    你说 concurrency (这不是关于计算,而是关于 interaction),但似乎你的问题本质上是关于纯计算?

    【讨论】:

    • 感谢您的详细回答:) ...关于您的最后一点,是的,您是对的,并发不是并行,但我在这里使用了并发这个词,因为我在处理时考虑了 Golang 技术事情在同一个线程上并发,当第一个阻塞时将下一个工作转发到空闲处理器/线程。
    • 您说:But laziness and parallelism are not incompatible; at least not if you make sure you have big enough “chunks” of strictness in it,能否详细说明一下?谁控制big enough chunk?而且,是什么让它变得严格,你的意思是我应该直接对函数进行严格的编程?
    • @securecurve 不,您通常不需要自己更改函数。就在您想要并行计算两个 结果 时,可能需要在 spark 认为它已完成其工作之前将它们 强制 转换为正常形式。
    • 如何以正常形式强制它们(你的意思是严格?)?以编程方式,使用编译器指令还是其他方式?
    • 经典地,带有pseq operator,或者来自deepseq library的“深”吊坠。
    猜你喜欢
    • 2011-10-16
    • 1970-01-01
    • 1970-01-01
    • 2015-12-12
    • 2015-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-14
    相关资源
    最近更新 更多