首页 文章

从技术上讲,为什么Erlang中的进程比OS线程更有效?

提问于
浏览
153

Erlang的特点

来自Erlang Programming(2009):

Erlang并发性快速且可扩展 . 它的进程是轻量级的,因为Erlang虚拟机不会为每个创建的进程创建一个OS线程 . 它们在VM中创建,调度和处理,与底层操作系统无关 . 结果,进程创建时间大约为微秒,并且与并发存在的进程的数量无关 . 将其与Java和C#进行比较,其中为每个进程创建一个底层OS线程:您将获得一些非常有竞争力的比较,Erlang大大优于两种语言 .

来自Concurrency oriented programming in Erlang (pdf) (slides)(2003):

我们观察到创建Erlang进程所需的时间是1μs到2,500个进程;此后,对于多达30,000个过程,它增加到约3μs . Java和C#的性能显示在图的顶部 . 对于少量过程,创建过程大约需要300μs . 创建两千多个流程是不可能的 . 我们看到,对于多达30,000个进程,在两个Erlang进程之间发送消息的时间约为0.8μs . 对于C#,每个消息大约需要50μs,直到最大进程数(大约1800个进程) . Java更糟糕的是,对于多达100个进程,每个消息大约需要50μs,然后当大约有1000个Java进程时,它会快速增加到每条消息10ms .

我的想法

我并不完全理解为什么Erlang进程在产生新进程方面更有效率,并且每个进程的内存占用量更小 . OS和Erlang VM都必须进行调度,上下文切换,并跟踪寄存器中的值等等......

为什么OS线程的实现方式与Erlang中的进程不同?他们还需要支持更多的东西吗?为什么他们需要更大的内存占用?为什么他们的产卵和沟通较慢?

从技术上讲,为什么在产生和通信时,Erlang中的进程比OS线程更有效?为什么操作系统中的线程不能以同样有效的方式实现和管理?为什么操作系统线程的内存占用量更大,产生和通信速度更慢?

更多阅读

7 回答

  • 30

    经过一些研究后,我找到了Joe Armstrong的演讲 .

    Erlang - software for a concurrent world (presentation)(13分钟):

    [Erlang]是一种并发语言 - 我的意思是线程是编程语言的一部分,它们不属于操作系统 . 这就是Java和C等编程语言的真正问题 . 它的线程不是编程语言,线程是操作系统中的东西 - 它们继承了操作系统中的所有问题 . 其中一个问题是内存管理系统的粒度 . 操作系统中的内存管理可以保护整个内存页面,因此线程可以使用的最小大小是页面的最小大小 . 这实际上太大了 . 如果你为你的机器增加了更多的内存 - 你拥有相同数量的位来保护内存,所以页面表的粒度会上升 - 你最终会使用64kB来表示你知道在几百字节内运行的进程 .

    我认为,如果不是全部,至少我的一些问题,它会回答

  • 40

    我在汇编程序中实现了协同程序,并测量了性能 .

    在协同程序之间切换,例如Erlang进程,在现代处理器上需要大约16条指令和20纳秒 . 此外,您经常知道要切换到的进程(例如:在队列中接收消息的进程可以实现为从调用进程到接收进程的直接切换),因此调度程序不会发挥作用,使得这是一个O(1)操作 .

    要切换OS线程,大约需要500-1000纳秒,因为你正在调用内核 . 操作系统线程调度程序可能在O(log(n))或O(log(log(n)))时间运行,如果你有数万甚至数百万个线程,这将开始变得明显 .

    因此,Erlang进程更快,更好地扩展,因为切换的基本操作更快,并且调度程序运行频率更低 .

  • 58

    我认为Jonas想要将OS线程与Erlang进程进行比较 . 编程Erlang的作者Joe Armstrong稍后测试了Erlang进程产生OS线程的可伸缩性 . 他在Erlang中编写了一个简单的Web服务器,并针对多线程Apache进行了测试(因为Apache使用的是OS线程) . 有一个旧网站,其数据可以追溯到1998年 . 我只设法找到该网站一次 . 所以我无法提供链接 . 但信息就在那里 . 这项研究的主要观点表明,Apache手写的时间超过8KErlang服务器处理了10K进程 .

  • 1

    有几个因素:

    • Erlang进程不是OS进程 . 它们由Erlang VM使用轻量级协作线程模型实现(在Erlang级别抢占,但在协同调度的运行时控制下) . 这意味着切换上下文要便宜得多,因为它们只在已知的受控点切换,因此不必保存整个CPU状态(正常,SSE和FPU寄存器,地址空间映射等) .

    • Erlang进程使用动态分配的堆栈,这些堆栈起始非常小并且在必要时增长 . 这允许产生数千甚至数百万的Erlang进程,而不会吸收所有可用的RAM .

    • Erlang曾经是单线程的,这意味着没有必要确保进程之间的线程安全 . 它现在支持SMP,但是同一调度程序/核心上的Erlang进程之间的交互仍然非常轻量级(每个核心有单独的运行队列) .

  • 0

    因为Erlang解释器只需要担心自己,操作系统还有许多其他需要担心的问题 .

  • 2

    Erlang进程(大约)与其他语言中的green threads对应;尽管Erlang比大多数人做得更好,但是保护得更少 . )因为它们的重量轻得多,所以它们可以更广泛地使用 .

    另一方面,OS线程可以简单地安排在不同的CPU内核上,并且(大部分)能够支持独立的CPU绑定处理 . 操作系统进程就像操作系统线程一样,但具有更强大的操作系统强制分离 . 这些功能的代价是操作系统线程和(甚至更多)进程更昂贵 .


    另一种理解差异的方法是这样的 . 假设您要在JVM之上编写Erlang的实现(不是特别疯狂的建议),那么您'd make each Erlang process be an object with some state. You'然后有一个Thread实例池(通常根据主机系统中的核心数量来确定大小; 's a tunable parameter in real Erlang runtimes BTW) which run the Erlang processes. In turn, that will distribute the work that is to be done across the real system resources available. It' sa相当简洁的做事方式,但完全依赖于每个Erlang进程当然没有的事实; Erlang的结构是不要求那些单独的进程是重量级的,因为它是执行程序的整体集合 .

    在许多方面,真正的问题是术语之一 . Erlang称之为进程的东西(并且与CSP,CCS中的相同概念强烈对应,尤其是π演算)与具有C遗产的语言(包括C,Java,C#和许多其他人)调用进程或线程 . 有一些相似之处(都涉及一些并发执行的概念),但肯定没有等价 . 所以当有人对你说“过程”时要小心;他们可能会理解它意味着完全不同的东西......

  • 99

    其中一个原因是erlang进程不是在OS中创建的,而是在evm(erlang虚拟机)中创建的,因此成本较小 .

相关问题