关于多线程:对Node.js的Haskell响应是什么?

What is the Haskell response to Node.js?

我相信Erlang社区并不羡慕Node.js,因为它本身可以进行非阻塞I / O,并且可以轻松地将部署扩展到多个处理器(甚至没有内置在Node.js中)。有关详情,请访问http://journal.dedasys.com/2010/04/29/erlang-vs-node-js和Node.js或Erlang

那么Haskell呢? Haskell能否提供Node.js的一些好处,即一个干净的解决方案,以避免在不使用多线程编程的情况下阻止I / O?

Node.js有很多吸引人的东西

  • 事件:没有线程操作,程序员只提供回调(如在Snap框架中)
  • 保证回调在单个线程中运行:不存在竞争条件。
  • 简单易用的UNIX友好API。好处:优秀的HTTP支持。 DNS也可用。
  • 默认情况下,每个I / O都是异步的。这样可以更容易地避免锁定。但是,回调中过多的CPU处理会影响其他连接(在这种情况下,任务应分成较小的子任务并重新调度)。
  • 客户端和服务器端的语言相同。 (但是我没有看到这个值太多了.jQuery和Node.js共享事件编程模型,但其余部分非常不同。我只是看不出服务器端和客户端之间的共享代码如何在实践中很有用。)
  • 所有这些都包装在一个产品中。

  • 好的,所以看了@gawi指出的一些node.js演示文稿,我可以多说一下Haskell与node.js的比较。在演示中,Ryan描述了Green Threads的一些好处,但接着说他并没有发现缺少线程抽象是一个缺点。我不同意他的立场,特别是在Haskell的上下文中:我认为线程提供的抽象对于使服务器代码更容易正确,更加健壮至关重要。特别是:

    • 每个连接使用一个线程允许您编写表示与单个客户端通信的代码,而不是编写同时处理所有客户端的代码。可以这样想:处理具有线程的多个客户端的服务器看起来几乎与处理单个客户端的客户端相同;主要区别在于前者的某处fork。如果您正在实现的协议非常复杂,那么同时管理多个客户端的状态机会非常棘手,而线程则只允许您与单个客户端进行通信编写脚本。代码更容易正确,更易于理解和维护。

    • 单个OS线程上的回调是协作式多任务处理,而不是抢占式多任务处理,这是线程所能获得的。合作多任务的主要缺点是程序员负责确保没有饥饿。它失去了模块性:在一个地方犯了一个错误,它可以搞砸整个系统。这真的是你不想担心的事情,而抢占是一个简单的解决方案。而且,回调之间的通信是不可能的(它会死锁)。

    • 并发性在Haskell中并不难,因为大多数代码都是纯粹的,因此通过构造是线程安全的。有简单的通信原语。在Haskell中使用并发性比在具有不受限制的副作用的语言中更难以拍摄自己。


    Can Haskell provide some of the benefits of Node.js, namely a clean solution to avoid blocking I/O without having recourse to multi-thread programming?

    是的,实际上事件和线程在Haskell中是统一的。

    • 您可以使用显式轻量级线程进行编程(例如,单个笔记本电脑上的数百万个线程)
    • 要么;您可以基于可伸缩事件通知以异步事件驱动的方式进行编程。

    线程实际上是根据事件实现的,并且可以跨多个内核运行,具有无缝的线程迁移,具有记录的性能和应用程序。

    例如。对于

    • 大规模并发工作编排
    • 并发集合在32或48个核心上进行扩展
    • 用于分析和调试多线程/多事件程序的工具支持。
    • 高性能事件驱动的Web服务器。
    • 有趣的用户:如高频交易。

    32个核心上的并发集合nbody

    alt text

    在Haskell中,您既有事件又有线程,因为它是引擎盖下的所有事件。

    阅读描述实施的文章。


    首先,我不认为node.js正在做正确的事情暴露所有这些回调。你最终用CPS编写程序(继续传递样式),我认为应该是编译器完成转换的工作。

    Events: No thread manipulation, the programmer only provides callbacks (as in Snap framework)

    因此,考虑到这一点,如果您愿意,可以使用异步样式编写,但这样做会错过以高效的同步样式编写,每个请求只有一个线程。 Haskell在同步代码中非常有效,特别是与其他语言相比时。这是下面的所有事件。

    Callbacks are guaranteed to be run in a single thread: no race condition possible.

    你仍然可以在node.js中遇到竞争条件,但这更难。

    每个请求都在它自己的线程中。当你编写必须与其他线程通信的代码时,由于haskell的并发原语,使其成为线程安全非常简单。

    Nice and simple UNIX-friendly API. Bonus: Excellent HTTP support. DNS also available.

    看看hackage,看看自己。

    Every I/O is by default asynchronous (this can be annoying sometimes, though). This makes it easier to avoid locks. However, too much CPU processing in a callback will impact other connections (in this case, the task should split into smaller sub-tasks and re-scheduled).

    你没有这样的问题,ghc将在真实的OS线程中分配你的工作。

    Same language for client-side and server-side. (I don't see too much value in this one, however. JQuery and Node.js share the event programming model but the rest is very different. I just can't see how sharing code between server-side and client-side could be useful in practice.)

    哈斯克尔不可能在这里赢得...对吧?再想一想,http://www.haskell.org/haskellwiki/Haskell_in_web_browser。

    All this packaged in a single product.

    下载ghc,点燃cabal。每个需要都有一个包。


    我个人认为Node.js和编程回调是不必要的低级别和有点不自然的事情。为什么在良好的运行时(例如在GHC中找到的运行时)可以为您处理回调并且非常有效地执行回调时编程回调?

    与此同时,GHC运行时有了很大的改进:它现在有一个名为MIO的"新的新IO管理器",其中"M"代表我相信的多核。它建立在现有IO管理器的基础之上,其主要目标是克服4+核心性能下降的原因。本文提供的性能数字令人印象深刻。看看自己:

    With Mio, realistic HTTP servers in Haskell scale to 20 CPU cores, achieving peak performance up to factor of 6.5x compared to the same servers using previous versions of GHC. The latency of Haskell servers is also improved: [...] under a moderate load, reduces expected response time by 5.7x when compared with previous versions of GHC

    和:

    We also show that with Mio, McNettle (an SDN controller written in Haskell) can scale effectively to 40+ cores, reach a thoroughput of over 20 million new requests per second on a single machine, and hence become the fastest of all existing SDN controllers.

    Mio已经进入GHC 7.8.1版本。我个人认为这是Haskell性能的重大进步。比较以前的GHC版本和7.8.1编译的现有Web应用程序性能将非常有趣。


    恕我直言的事件是好的,但通过回调编程不是。

    大多数使Web应用程序编码和调试变得特殊的问题来自使它们具有可扩展性和灵活性的原因。最重要的是HTTP的无状态特性。这增强了可导航性,但这会导致控制反转,其中IO元素(在本例中为Web服务器)调用应用程序代码中的不同处理程序。这个事件模型 - 或回调模型,更准确地说 - 是一场噩梦,因为回调不共享变量范围,并且导航的直观视图丢失。当用户来回导航以及其他问题时,很难防止所有可能的状态改变。

    可以说问题类似于GUI编程,其中事件模型工作正常,但GUI没有导航,也没有后退按钮。这使Web应用程序中可能的状态转换倍增。尝试解决这些问题的结果是重型框架,配置复杂,有大量普遍的魔术标识符,而不会质疑问题的根源:回调模型及其固有的缺乏共享变量范围,没有排序,所以序列必须通过链接标识符来构建。

    有基于顺序的框架,如ocsigen(ocaml)海边(smalltalk)WASH(已停产,Haskell)和mflow(Haskell),它们在保持导航性和REST-fulness的同时解决了状态管理问题。在这些框架内,程序员可以将导航表达为命令序列,其中程序发送页面并在单个线程中等待响应,变量在范围内,后退按钮自动工作。这固有地产生更短,更安全,更易读的代码,其中导航对于程序员来说是清晰可见的。 (公平警告:我是mflow的开发者)


    这个问题非常荒谬,因为1)Haskell已经以更好的方式解决了这个问题,并且2)以与Erlang大致相同的方式解决了这个问题。以下是针对节点的基准:http://www.yesodweb.com/blog/2011/03/prelimin-warp-cross-language-benchmarks

    为Haskell提供4个内核,它可以在单个应用程序中每秒执行100k(简单)请求。节点不能做多少,也无法跨核心扩展单个应用程序。而且你不需要做任何事情来收获它,因为Haskell运行时是非阻塞的。 Erlang中唯一一种内置于运行时的非阻塞IO的其他(相对常见)语言。


    正如nodejs已经放弃了libev
    Snap Haskell Web框架也放弃了libev。