Elixir进程并没有共享堆内存

Ath*_* V. 2 heap erlang process elixir

Elixir进程有自己的堆. 如果进程想要与另一个进程共享数据结构,那怎么可能呢?我想到的一个答案是,该进程向包含数据结构的其他进程发送消息.这是否意味着整个数据结构从一个堆复制到另一个堆?如果这是真的,那不是效率低下吗?

zxq*_*xq9 11

TL; DR:

是的,这是低效的.但是你在实践中几乎从未注意到这一点.欢迎来到更安全的编程世界.你可能会使用基于Erlang的语言的大多数东西将与网络相关,并且网络是迄今为止更大的约束(有时是磁盘或端口IO).

此外,替代方案是一个疯狂的噩梦.无论如何,如果你进行大规模的并发编程.

讨论

在考虑"效率"时,需要考虑两种截然不同的背景:

  • 机器在时间,空间和锁定资源方面执行任务是否有效?是否有明显的快捷方式不会引入漏洞抽象?
  • 人类写作,理解和维护是否有效?

当你考虑效率的这两个方面时,你最终必须将问题归结为时间和金钱 - 因为在有用的工具方面,事情真的很重要.

人文背景

这个效率论证非常类似于"Python比汇编程序效率低"的论点.我曾经争论同样的事情 - 直到我负责几项大型开发工作.我仍然认为JavaScript,XML和其他一些明显不好的语言和数据表示是恶魔,但在一般情况下(定义为"你没有精确知识和控制中断时序的情况,因为它与总线读取有关)/write和CPU周期")语言提供的基本抽象越大(语言越小)越好.

Erlang在现代大规模并发系统的背景下赢得了各种衡量标准,在简单性和语法限制方面甚至破坏了大多数其他EVM语言(除了LFE - Richard得到了正确的,imo).

例如,考虑Elixir 的句法复杂性.无论如何,这都不是一种糟糕的语言(恰恰相反).但是,虽然对于许多新手来说,在熟悉度方面它更容易,但实际上要复杂几倍,并且比任何初始学习曲线都要长得多."简单"与"简单"完全不同 ; "缓和"是一个熟悉的问题,而不是实用价值.

机器背景

范式是否在执行中有效取决于几乎完全取决于底层实现中的引用传递("通过指针")VS消息传递("按值")的上下文.

通过的东西有多大?是否采用了混合方法,不会破坏按值传递消息的抽象?

在Erlang(以及扩展的Elixir和LFE)中,在进程之间传递的大多数消息都非常小.事实上,真的很小.大的,不可变的消息几乎总是Erlang二进制文件 - 这些实际上通过引用传递的(稍后将详细介绍).

大型消息更为罕见,但考虑到实现复制的方式,即使这也不是一个大问题.为了允许进程自行崩溃允许每个进程拥有自己的垃圾收集计划(与不可预测的"停止世界"垃圾收集的噩梦场景相反),每个Erlang进程都有自己的堆.

这是两种方式的整体优化:

  • 这允许每个进程崩溃而不会影响任何进程.
  • 它还允许以某种方式编写每个进程,这样一般来说,每个赋值都是一个不可变的标签声明而不是一个可变的赋值(而不是一个疯狂的危险和/或非常复杂的管理和调度共享数据对象声明).

所有这些都是每个进程实现隔离垃圾收集的原因,而这种单一的差异使得Erlang 感觉它具有增量垃圾收集功能,同时实际上在下面实现了一个非常普通的GC模型(只是将每个进程拆分).

但是有一些地方我们确实希望以牺牲底层复杂性(以及程序员的认知开销方面的难度)为代价进行一些传递.

"大型"二进制文件是典型的示例案例.默认情况下,任何大于64字节的二进制文件都是共享对象,通过引用(指针)传递,而不是通过值(复制)传递.当然,它们仍然是不可改变的,这是安全的唯一原因.问题是,如果不使用binary:copy/1,2较大二进制文件子节的任何引用,就会成为对整个二进制文件的引用,因此您可以在全局堆中使用大量的底层数据,因为二进制引用了较大的二进制文件内存中的整体二进制对象.这是有问题的,但这是在安全并发环境中实现性能攻击(如共享内存对象)的代价.

结论(一些无法量化的基于轶事的指导...)

就个人而言,我从来没有真正将按值复制作为瓶颈.不止一次.我写了很多 Erlang程序.

您真正的瓶颈几乎总是共享访问外部资源,如磁盘/存储/网络(概念上是相同的).这是便宜,从任何角度,以支付额外的核心或额外的VM /实例,而不是支付程序员追查情况下,binary:copy/1,2应使用-与内存和CPU时间的速度只得到更快,更便宜,因此,无论你认为今天的"性能损失",明年都会出现一个微不足道的抱怨,相比之下,让昂贵的程序员追踪未来代码中愚蠢的速度攻击的实际成本.

(如果你的程序员并不比你的计算资源贵得多,你为什么雇用这些可怕的程序员?!?!?ZOMG!)

关于未来的说明......

未来只会越来越多核,并且在大多数情况下更加平行更加并发.既然AMD正在执行其将1000多个核心系统带入桌面的愿景,我预测下一个重大争议将是总线速度,通道,缓存管理以及核心内存大小的大幅增加.这是所有这些核心才能看到就业的唯一方式.

能够利用它的唯一语言将是像Erlang那样实现通过值传递消息作为主要方法的语言,由大型二进制引用传递和全局堆对象的显式复制等混合情况支持.在这种世界中,卫生范式将变得更加重要,语言简单性将成为拯救我们免受并行性和并发性所带来的复杂性爆炸的元素.

考虑一下"微服务架构"甚至是Docker的驱动力 - 人们无意识地磕磕绊绊,然后解决了Erlang最初设计解决的许多相同问题,只是以一种特殊的方式.

在大规模多核,大规模并发环境中,考虑到与核心,主轴,存储和内存相比有多昂贵的优秀程序员,按值传递并且每个进程具有堆似乎是一个整体优化.(顺便说一句,我认为将来会有更少的程序员使用并发语言中更持久的软件,而猴子军队的方法将继续产生基本上短暂的代码库.)