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

时间:2010-04-25 11:30:12

标签: multithreading erlang green-threads lightweight-processes

Erlang的特征

来自Erlang Programming(2009):

  

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

答案 0 :(得分:103)

有几个因素:

  1. Erlang进程不是OS进程。它们由Erlang VM使用轻量级协作线程模型实现(在Erlang级别抢占,但在协同调度的运行时控制下)。这意味着切换上下文要便宜得多,因为它们只在已知的受控点切换,因此不必保存整个CPU状态(正常,SSE和FPU寄存器,地址空间映射等)。
  2. Erlang进程使用动态分配的堆栈,这些堆栈的启动非常小并且会根据需要增长。这样就可以产生数千甚至数百万的Erlang进程而不会占用所有可用的RAM。
  3. Erlang过去是单线程的,这意味着没有必要确保进程之间的线程安全。它现在支持SMP,但是同一个调度程序/核心上的Erlang进程之间的交互仍然非常轻量级(每个核心都有单独的运行队列)。

答案 1 :(得分:60)

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

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

  

[Erlang]是一种并发语言 - 我的意思是线程是编程语言的一部分,它们不属于操作系统。这就是Java和C ++等编程语言的真正问题。它的线程不是编程语言,线程是操作系统中的东西 - 它们继承了操作系统中的所有问题。其中一个问题是内存管理系统的粒度。 操作系统中的内存管理保护整个内存页面,因此线程的最小大小是页面的最小大小。 / b> 实际上太大了。

     

如果你为你的机器添加更多内存 - 你有相同的位数来保护内存,所以页面表的粒度上升 - 你最终使用64kB表示一个你知道以几百字节运行的过程。

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

答案 2 :(得分:42)

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

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

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

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

答案 3 :(得分:32)

Erlang进程在其他语言中对应于(大约)green threads;这些进程之间没有操作系统强制分离。 (可能存在语言强制分离,但尽管Erlang比大多数人做得更好,但这种保护较少。)因为它们的重量轻得多,所以可以更广泛地使用它们。

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


理解差异的另一种方法是这样。假设您要在JVM之上编写Erlang的实现(不是特别疯狂的建议),那么您将使每个Erlang进程成为具有某种状态的对象。然后,您将拥有一个Thread实例池(通常根据主机系统中的核心数量确定大小;这是实际Erlang运行时BTW中的可调参数),它运行Erlang进程。反过来,这将分配可用于实际系统资源的工作。这是一种非常巧妙的做事方式,但完全依赖完全这个事实,即每个Erlang进程都没有做很多事情。当然没关系; Erlang的结构是不要求那些单独的进程是重量级的,因为它是执行程序的整体集合。

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

答案 4 :(得分:2)

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

答案 5 :(得分:1)

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

答案 6 :(得分:0)

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