Elixir / Erlang可以复制一个进程,包括其内存吗?

6

我正在考虑使用Elixir来解决一个问题,主要是因为它能够以较低的成本生成大量进程。

在我的场景中,我想创建几个“原始”进程,将特定的不可变数据加载到内存中,然后根据需要制作这些进程的副本。所有副本都使用相同的基础数据,但会执行不同的只读任务。例如,想象一下,“原始”进程中有《战争与和平》的文本,每个原始的副本对文本执行不同类型的分析。

我的问题:

  • 在Elixir / Erlang VM中是否可以复制现有进程及其全部内存内容?
  • 如果可以,每个副本是否像原始进程一样占用同样数量的内存,还是像Unix进程一样使用“写时复制”策略共享内存(在这种情况下,就不会进行后续的写操作)?

进程不共享,或者说只共享非常有限的特定类型的数据。大型二进制文件是共享的。 - rvirding
我想我可能问错了问题。回过头来看,我真正想问的是“我能否加载或构建一些只读数据,生成多个进程,并让它们都使用它?”答案是肯定的。例如,可以使用宏在编译期间将数据存储在Elixir模块属性或函数体中,并且生成的进程可以在运行时调用该函数或访问该属性。 - Nathan Long
3个回答

6

没有内置的方法来复制进程。最简单的方法是启动“原始”进程和“副本”,并将所有相关数据通过消息发送给副本。进程不共享数据,因此没有更有效的方法可以做到这一点。将数据放入ETS表中只能在共享方面部分地帮助解决问题,因为ETS表中的数据在使用时会被复制到进程中,但是你不需要将所有数据都存储在进程堆中。


5
一个Erlang进程除了存储在变量中的数据(和进程字典)之外,没有特定于进程的数据。因此,要复制进程的内存,只需将所有相关变量作为参数传递给函数并生成一个新的进程。

一般来说,进程之间不共享内存;一切都是复制的。例外情况是ETS表(尽管当进程读取它时,数据会从ETS表中复制),以及大于64字节的二进制文件。如果您将“战争与和平”存储在二进制文件中,并将其发送到每个工作进程(或在生成这些工作进程时将其传递),则进程将共享内存,只有在它们想修改二进制文件时才会复制它。有关更多详细信息,请参见Erlang效率指南中的二进制章节


1
有趣。我想要分享的实际数据更像是一个字典,其中键是数字数组,值是字典数组。这样的数据能否通过二进制方式共享和使用?(对于这些完全新手问题,我很抱歉,但在深入研究之前,我正在尝试确定可行性。) - Nathan Long
2
我会从ETS表开始。虽然当你从ETS表中读取数据时,数据会被复制,但如果你只同时查看其中的一部分,工作进程应该能够跟上垃圾回收。 - legoscia
2
我也会选择 ETS,或者按需仅将相关数据发送给其他进程。然而,如果您进行基准测试并且确实存在性能问题,可以将键值编码为大二进制文件,并让每个进程从中提取信息。如果我没记错,Kresten 在他的 HanoiDB 应用程序中使用了这种技术: https://github.com/krestenkrab/hanoidb - José Valim
2
使用ETS表的问题在于每个进程都没有自己的数据副本可供使用。此外,使用ETS意味着每次访问数据时都会不断地将数据复制到/从进程中复制。当然,这取决于您希望这些副本如何工作。 - rvirding
听起来二进制更现实一些;数据可能是几MB的JSON。使用Elixir的主要目的是为了速度,因此每次复制这些数据是行不通的。从二进制中“提取”数据会很昂贵吗?例如,我能否在几毫秒内将3MB的二进制转换为元组关键字列表的地图?(此时答案可能是“试一试”,但如果有人知道这是否有效,那将节省我很多时间。) - Nathan Long
1
@NathanLong 你可以使用term_to_binary/1从二进制中提取数据。是的,你可以分享发送二进制文件,但这仍然意味着你需要构建提取的数据,因此从长远来看,你并没有比直接发送数据获得任何优势,事实上,你可能会因为多次提取和重建相同的数据而损失更多。 - rvirding

1
你把Erlang / Elixir进程视为类似于Unix进程。它们根本不是,我真希望它们有一个不同的名称,因为它们在标准的Unix意义上既不是线程也不是进程。我花了一些时间来理解它们之间的差异。
您必须放弃关于进程的所有先入为主的想法,它们都是错误的。Eprocess具有以下特征。
- 它们便宜且快速。使用很多,总是有更多。 - 它们不共享任何资源[1]。(即使写入stdout也是对另一个Eprocess的消息。) - IPC(或消息)非常快,与标准Unix IPC相比,开销相对较低。
在您的情况下,我会尝试创建一个管理数据的服务器,并让每个分析工作者向服务器发送需要的数据块的消息。让Eprocess成为共享内存的管理器是完全可以接受的。
对我而言,最有用的思考Eprocess的方式是将其视为具有自己执行线程的对象。
[1] 好吧,有ETS表,但最好认为它们不共享资源,直到您绝对必须这样做。

它们是进程,只不过不是Unix进程。除了Unix之外还有其他系统。 :-) - rvirding
实际上,如果再学习一年如何使用BEAM后,我可能会以不同的方式回答那个问题。 - Fred the Magic Wonder Dog
是的,ETS表并不是真正共享的。它们更像是在访问时在表和进程之间复制数据,从而实现全局访问。从概念上来说,它们类似于进程状态,但实现方式不同。 - rvirding

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接