关于 BEAM CPU 使用率的奇特案例 (2019)
关于 BEAM CPU 使用率的奇特案例
阅读需要 6 分钟 修改日期: 2019年12月6日
在进行 Go vs Elixir vs Node 基准测试时,我们发现运行在 BEAM 虚拟机上的 Elixir 比 Go 的 CPU 使用率高得多,但其响应能力仍然非常出色。我们的一些读者认为忙等待可能是造成这种现象的原因。
事实证明,BEAM 中的忙等待是一种优化,可确保最高的响应速度。
本质上,当等待某个事件发生时,虚拟机首先会进入一个 CPU 密集型的紧密循环,在该循环中,它会持续检查所讨论的事件是否已经发生。
处理此问题的标准方法是让操作系统内核以某种方式管理同步,以便在等待事件时,其他线程有机会运行。但是,如果所讨论的事件在进入等待状态后立即发生,则与内核的这种协调可能会造成浪费。
忙等待方法的一个有趣的副作用是,操作系统报告的 CPU 利用率会产生误导。由于内核等待(即使以忙等待的方式实现)未计入 CPU 利用率结果,因此 BEAM 的忙等待肯定会这样做。
为了确认 BEAM 忙等待导致我们的基准测试中 CPU 利用率高的理论,我们使用修改后的虚拟机重新运行了测试。
修改是通过使用 --with-microstate-accounting=extra 参数在我们的构建上运行 ./configure 来启用额外的微状态统计。然后,我们使用 msacc 在 c5.9xlarge AWS 实例上的 10 万连接测试的持续阶段的 10 分钟内收集微状态统计。
Average thread real-time : 600000658 us
Accumulated system run-time : 21254177063 us
Average scheduler run-time : 586759264 us
Thread alloc aux bifbusy_wait check_io
async 0.00% 0.00% 0.00% 0.00% 0.00%
aux 0.02% 0.06% 0.00% 0.00% 0.02%
dirty_cpu_sche 0.00% 0.00% 0.00% 0.00% 0.00%
dirty_io_sched 0.00% 0.00% 0.00% 0.00% 0.00%
poll 1.99% 0.00% 0.00% 0.00% 19.50%
scheduler 4.89% 2.63% 6.41% 56.28% 2.82%
Thread emulator ets gc gc_full nif
async 0.00% 0.00% 0.00% 0.00% 0.00%
aux 0.00% 0.00% 0.00% 0.00% 0.00%
dirty_cpu_sche 0.00% 0.00% 0.00% 0.00% 0.00%
dirty_io_sched 0.00% 0.00% 0.00% 0.00% 0.00%
poll 0.00% 0.00% 0.00% 0.00% 0.00%
scheduler 12.83% 0.18% 1.69% 0.44% 0.00%
Thread other port send sleep timers
async 0.00% 0.00% 0.00% 100.00% 0.00%
aux 0.00% 0.00% 0.00% 99.90% 0.00%
dirty_cpu_sche 0.00% 0.00% 0.00% 99.99% 0.00%
dirty_io_sched 0.00% 0.00% 0.00% 100.00% 0.00%
poll 0.00% 0.00% 0.00% 78.52% 0.00%
scheduler 4.25% 4.25% 0.64% 2.21% 0.48%
这些统计数据表明,利用率最高的两种线程类型是 poll 和 scheduler。poll 线程 78% 处于空闲状态,19% 检查 IO 状态。scheduler 线程 12% 在模拟器中(运行代码),56% 在忙等待中。
我们的理论得到了证实:真实的利用率实际上低于操作系统报告的 CPU 利用率。
当机器未完全专用于运行 BEAM 时,会发生忙等待方法的另一个副作用。
在这种情况下,其他(非 BEAM)内核线程可能获得不公平的 CPU 时间片。此外,在云中的突发性能实例上运行 BEAM 时,忙等待可能会导致花费不必要的 CPU 积分。
为了缓解这种情况,BEAM 虚拟机有一组选项,用于调节虚拟机执行的忙等待量。这些选项是 +sbwt、+sbwtdcpu 和 +sbwtdio。当设置为 none 时,它们分别禁用主调度器、dirty CPU 调度器和 dirty IO 调度器中的忙等待。
重要的问题是,如果禁用忙等待,是否会对响应能力产生任何影响。为了找到答案,我们针对使用 Cowboy Web 服务器提供 HTTP 请求的有限用例运行了测试。
测试
为了运行测试,我们使用了 Stressgrid 负载测试框架和 10 个 c5.2xlrage 生成器。Stressgrid 监视生成器的 CPU 利用率,因此我们可以避免由于生成器过度饱和而导致的结果偏差。在我们的测试中,所有生成器的 CPU 利用率均保持在 80% 以下。
我们使用了一个合成工作负载,该工作负载由 10 万个客户端设备组成。每个客户端设备打开一个连接,并发送 100 个请求,每个请求之间间隔 900±5% 毫秒。服务器通过休眠 100±5% 毫秒来处理请求,以模拟后端数据库请求,然后返回 1 kB 的有效负载。在没有额外延迟的情况下,这将导致平均连接生存期为 100 秒,平均负载为每秒 10 万个请求。
为了比较不同条件下的 BEAM 行为,我们针对 c5.9xlarge、c5.4xlarge 和 c5.2xlarge 目标 EC2 实例运行了测试。我们基于以下假设选择了这些实例类型:
- c5.9xlarge 将允许有意义的 CPU 容量余量,以使虚拟机在没有任何压力的情况下运行;
- 在 c5.4xlarge 上,虚拟机将几乎超过 CPU 容量;
- c5.2xlarge 将没有足够的 CPU 容量来处理工作负载,从而导致虚拟机在高压下运行。
对于每种实例类型,我们都在启用和禁用忙等待的情况下运行了测试。为了禁用忙等待,我们将以下行添加到 vm.args:
+sbwt none
+sbwtdcpu none
+sbwtdio none
查看每秒请求数,我们看到 c5.9xlarge 和 c5.4xlarge 处理了完整的每秒 10 万个请求的目标工作负载,而 c5.2xlarge 没有,在每秒 58k 个请求时达到饱和。忙等待设置对此指标没有影响。
另一方面,忙等待设置对 CPU 利用率图显示出非常显着的影响。对于所有实例类型,启用忙等待会导致 CPU 饱和超过 95%。禁用忙等待会导致 CPU 利用率随工作负载而扩展。
为了获得更详细的图片,我们还收集了两个延迟指标,以及相应的分布特征:打开连接的时间和 HTTP 响应标头的时间。
同样,c5.9xlarge 和 c5.4xlarge 实例在响应能力方面显示出相似的结果,并且在忙等待设置方面没有明显的差异。
为 c5.2xlarge 实例收集的延迟指标确认了 BEAM 虚拟机在高压下工作,延迟和标准误差增加。然而,我们再次看到启用和禁用忙等待之间没有明显的差异。
结论
在我们的测试中,我们发现 BEAM 的忙等待设置确实对 CPU 使用率产生了显着影响。
在具有最多可用 CPU 容量的实例上观察到最大的影响。同时,我们没有观察到启用和禁用忙等待的虚拟机之间的性能有任何有意义的差异。
我们必须注意到,我们的用例仅限于使用 Cowboy Web 服务器提供 HTTP 请求,并且可以想象,在不同的情况下,会有明显的差异。毕竟,这可能就是最初将忙等待引入 BEAM 的原因。
当在专用硬件上使用 Cowboy 运行 HTTP 工作负载时,保留默认设置(启用忙等待)是有意义的。当在与其他软件共享的操作系统内核上运行 BEAM 时,关闭忙等待以避免从非 BEAM 进程窃取时间是有意义的。
在云中的突发性能实例上运行时,不使用忙等待也是有意义的。
想要随时了解我们的进展吗?
在下面输入您的电子邮件地址
注册
发布者:Stressgrid,2019 年 2 月 9 日,发布在 分析 下,并使用 1159 个单词标记为 cpu、elixir、erlang 和 sbwt。
相关内容
- Web 服务器基准测试:Erlang vs Go vs Java vs NodeJS – 5 分钟
- Cowboy Web 服务器性能调查 - 第 2 部分 – 5 分钟
- Cowboy Web 服务器性能调查 - 第 1 部分 – 4 分钟
- 使用 Elixir 实现每秒 10 万个连接 – 6 分钟
- Go vs Node vs Elixir 基准测试 – 6 分钟







