【发布时间】:2011-03-25 07:12:55
【问题描述】:
演员消息传递语义的忠实实现意味着消息内容是从逻辑的角度深度复制的,即使对于不可变类型也是如此。消息内容的深度复制仍然是 Actor 模型实现的瓶颈,因此为了提高性能,一些实现支持零复制消息传递(尽管从程序员的角度来看它仍然是深度复制)。
在 Erlang 中完全实现了零拷贝消息传递吗?在节点之间显然不能这样实现,但是在同一节点上的进程之间呢? This question 相关。
【问题讨论】:
演员消息传递语义的忠实实现意味着消息内容是从逻辑的角度深度复制的,即使对于不可变类型也是如此。消息内容的深度复制仍然是 Actor 模型实现的瓶颈,因此为了提高性能,一些实现支持零复制消息传递(尽管从程序员的角度来看它仍然是深度复制)。
在 Erlang 中完全实现了零拷贝消息传递吗?在节点之间显然不能这样实现,但是在同一节点上的进程之间呢? This question 相关。
【问题讨论】:
我认为你的断言根本不正确 - 进程间消息的深度复制不是 Erlang 的瓶颈,并且使用默认的 VM 构建/设置,这正是所有 Erlang 系统正在做的事情。
Erlang进程堆之间是完全分开的,消息队列位于进程堆中,所以消息必须被复制。这对于将数据传入和传出 ETS 表也是如此,因为它们的数据存储在与进程堆不同的分配区域中。
然而,有许多共享数据结构。大型二进制文件(>64 字节长)通常分配在节点范围的区域中并进行引用计数。 Erlang 进程只存储对这些二进制文件的引用。这意味着如果您创建一个大型二进制文件并将其发送到另一个进程,那么您只是发送了引用。
在分配大小方面,在进程之间发送数据实际上比您想象的要糟糕 - 在复制期间不会保留术语内的共享。这意味着如果您仔细构造一个带有共享的术语以减少内存消耗,它将在其他进程中扩展到其非共享大小。您可以在 OTP Efficiency Guide 中看到一个实际示例。
正如 Nikolaus Gradwohl 所指出的,VM 有一个实验性的混合堆模式,它确实允许进程之间的术语共享并启用零拷贝消息传递。据我了解,这并不是一个特别有前途的实验——它需要额外的锁定并使进程独立垃圾收集的现有能力复杂化。因此,复制进程间消息不仅不是 Erlang 系统中常见的瓶颈,而且实际上会降低性能。
【讨论】:
AFAIK 使用 -shared 或 -hybrid 模型对 erlang 中的零拷贝消息传递提供了实验性支持。我在 2009 年读过一篇博文,声称它在 smp 机器上已损坏,但我不知道当前状态
【讨论】:
正如这里和其他问题所提到的,当前版本的 Erlang 基本上复制了除了较大的二进制文件之外的所有内容。在 SMP 之前的旧时代,不复制但传递引用是可行的。虽然这导致消息传递非常快,但它在实现中产生了其他问题,主要是它使垃圾收集更加困难和复杂的实现。我认为今天传递引用和共享数据可能会导致过度锁定和同步,这当然不是一件好事。
【讨论】:
我为您引用的其他问题编写了已接受的答案,并在其中为您提供了指向这行代码的直接指针:
message = copy_struct(message, msize, &hp, &bp->off_heap);
这是在 Erlang 运行时系统需要发送消息时调用的函数中,它不在任何可能导致它被跳过的“if”中。所以,据我所知,答案是“是的,它总是被复制的”。 (严格来说这不是真的——有一个“如果”,但它似乎是在处理例外情况,而不是正常的代码流路径。)
(我忽略了 Nikolaus 提出的混合堆选项。看起来他是对的,但由于这不是 Erlang 通常的构建方式并且它有其自身的惩罚,我认为它不值得考虑作为回答您的问题的一种方式。)
不过,我不知道您为什么认为 10 GByte/sec 是瓶颈。寄存器或CPU缓存在计算机中运行速度更快,而这些内存很小,因此本身就构成了一种瓶颈。除此之外,您提出的零拷贝想法需要在多核系统中跨CPU消息传递的情况下进行锁定,这也是一个瓶颈。我们已经在这个函数中支付了一次锁定惩罚,将消息复制到另一个进程的消息队列中;为什么稍后当该过程开始阅读消息时再次支付?
归根结底,我认为您关于如何加快速度的想法实际上并没有多大帮助。
【讨论】: