Erlang/OTP中gen_server调用的开销是多少?

3

这篇关于Erlang可扩展性的文章指出,对于每个对gen_server的调用、广播或消息传递都会产生一定的开销。那么这个开销有多大,以及它是用来做什么的呢?

1个回答

6
被引用的成本是对外部模块进行(相对盲目的)函数调用的成本。这是因为gen_*抽象中的所有内容都是对外部定义函数的回调(您在回调模块中编写的函数),而不是可以由编译器在单个模块内进行优化的函数调用。其中一部分成本是调用的解析(查找正确的代码以执行——Python或Java中每个“点”在.a.long.function.or.method.call中提高了解析成本的同样原因),另一部分成本是实际调用本身。
但是,这不是您可以计算为简单数量并乘以以获得有关系统操作成本的有意义答案的内容。
在像Erlang这样的大规模并发系统中,存在太多的变量、约束点和意外便宜的元素,其中最难处理的并发部分已被抽象化(与调度相关的问题),并且并行处理中最昂贵的元素变得极其便宜(上下文切换、进程生成/终止和进程:内存比率)。
唯一真正了解大规模并发系统的方法是编写一个并在实际操作中进行测量,因为这种系统本质上会表现出新兴行为。您可以先使用纯 Erlang 编写完全相同的程序,然后再使用 gen_* 抽象作为 OTP 应用程序重新编写,并以此方式测量性能差异,但基准测试数据仅对该特定程序有意义,并且可能仅在该特定负载配置文件下有意义。
所有这些都需要考虑到……当我们开始在 Erlang 领域中深入探讨时,真正重要的数字是调度器考虑到的减少预算成本。Erlang Solutions 的 Lukas Larsson 之前发布了一段关于调度器的视频,详细介绍了这些成本如何影响系统、它们是什么以及如何在特定情况下调整值 (了解 Erlang 调度器)。除了与 Erlang/OTP 无关的外部资源 (iops 延迟、网络问题、NIF 疯狂等),压倒性的因素是调度器的行为,而不是“函数调用的成本”。但无论如何,唯一真正了解的方法就是编写一个原型,代表您在实际系统中期望的基本行为,并进行测试。

不仅是对外部模块的调用会产生开销——与gen_server和其他OTP行为的交互通常涉及进程之间的消息传递。例如,gen_server:cast/2会将消息从调用者发送到gen_server进程,而gen_server:call/3则意味着向gen_server发送一条消息,然后回复一条消息返回给调用进程。gen_server:call/3还设置了一个监视器来监视gen_server进程,以便如果它死亡,调用者不会因等待永远不会到达的回复而挂起。 - Steve Vinoski
@SteveVinoski 的确如此。但是无论这是否是 OTP 行为,消息和链接开销都将存在--唯一的不同之处在于外部调用,特别是在链接的上下文中。 (该帖子表明可以编写自定义进程代码以比 OTP 行为更少的开销)。嗯,我想一个可以编写同步调用而不链接,但邀请更大(和更奇怪)的问题。 - zxq9

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