erlang VM(OTP 22.3) 参数调优
内存
erlang:memory(total) 报告的内存用量是 active used 用量,
这个值与操作系统报告(如 htop)的 RES 不同, 有时差异很大
原因是 erlang VM 从操作系统通过 mmap(mseg_alloc) 或 malloc(sys_alloc) 分配的内存由 VM 自行管理
mseg_alloc
mseg_alloc 分配的 carrier(称为 mbc: multi block carrier, 即一大块内存, 如 8 MB, 用于满足大量小块的内存需求),
当需要使用内存时, 再从 carrier 内部找到一块能够满足需要的内存(称为 block)
sys_alloc
sys_alloc 分配的 carrier(称为 sbc: single block carrier, 这种 carrier 内只会有一个 block, 用于满足单个大块内存需求)
记一次内存参数调优
erlang:memory(total) 是 6G
linux report 的 RES 竟然是 12G
分析发现利用率低的原因在于 binary_alloc , erlang:memory(binary) 是 3G,
问题
1 | > {:ok, {_, l}} = :instrument.carriers(%{allocator_types: [:binary_alloc]}); l |> Enum.map(fn i -> elem(i, 1)end) |> Enum.sum |
这里可以看出, 仅 binary_alloc 就从 OS 那里拿了 9G 多, 只使用了 3G 多, 利用率仅 30% 多
绝大多数 carrier 都是 8M 大小
recon_alloc:fragmentation
1 | > for {{:binary_alloc, _}, _} = i <- :recon_alloc.fragmentation(:current), do: i |
这里的数据和上面的数据相似, 只是按各个 scheduler 分开了, 各个 scheduler 因忙碌程度不同数据有所不同
这里是无法直视的 0.117 的 usage
另外各 scheduler 都没有 sbc , 100% mbc
惨不忍睹的 cache_hit_rates
1 | > :recon_alloc.cache_hit_rates |
根据该函数的doc来看, 该值应该在 0.80(80%)以上才正常
instrument:allocations
1 | > :instrument.allocations |
如下写了一个小函数来计算 histogram 里的总内存量1
2
3
4
5
6> f = fn(start, histogram) ->
:erlang.tuple_to_list(histogram)
|> Enum.reduce({start, 0}, fn(i, {size_level, acc}) -> {size_level * 2, i * size_level + acc} end)
end
> f.(128, {2, 260, 3928, 37343, 4713, 575, 2, 0, 0, 0, 3257, 5871, 0, 0, 0, 0, 0, 0})
{33554432, 2018289920}
显示绝大部分都是 tcp_inet 占用的, < 2018289920 (约2G)
这里看起来是正常的
当时使用的内存参数
1 | > :erlang.system_info(:allocator) |
尝试 disable binary_alloc
1 | +MBe false |
disable 之后连 erlang:memory 都返回 :notsup 的错误, 看不了了.... :(
压力测试下来比原参数 RES 占用可能少 20% 多的样子
但是看不了 erlang:memory 等数据无法接受
关于 background gc
这个 background gc 每 10 分钟自动对几乎所有 process 做一次 fullsweep gc1
2
3for p <- :erlang.processes(),
{:status, :waiting} == :erlang.process_info(p, :status),
do: :erlang.garbage_collect(p)
发现改成 1 天一次以后, memory fragmentation 有逐渐改善(3 天后降下来 2G 左右)
fullsweep gc 与 memory fragmentation 之间的关系还需要研究
reference
erts_alloc, instrument docs by erlang
http://erlang.org/doc/man/erts_alloc.html
http://erlang.org/doc/apps/erts/CarrierMigration.html
http://blog.erlang.org/Memory-instrumentation-in-OTP-21/
https://erlang.org/doc/man/instrument.html
memory management: battle stories by Lukas Larsson
https://www.youtube.com/watch?v=nuCYL0X-8f4
https://www.erlang-factory.com/static/upload/media/139454517145429lukaslarsson.pdf
https://www.cnblogs.com/zhengsyao/p/erts_allocators_speech_by_lukas_larsson.html
memory fragmentation case, and recon_alloc tool, erlang-in-anger book written by Fred Hebert
https://blog.heroku.com/archives/2013/11/7/logplex-down-the-rabbit-hole
https://github.com/ferd/recon/blob/master/src/recon_alloc.erl
https://www.erlang-in-anger.com
RabbitMQ default flags
https://www.rabbitmq.com/runtime.html#allocators
https://groups.google.com/forum/#!msg/rabbitmq-users/LSYaac9frYw/LNZDZUlrBAAJ
CPU
reference
+sbwt
https://stressgrid.com/blog/beam_cpu_usage/
+stbt Rabbit MQ default
https://www.rabbitmq.com/runtime.html#scheduler-bind-type
someone says using +stbt ts
reduced context switching for 4 times
https://github.com/rabbitmq/rabbitmq-server/issues/612
Whatsapp use tnnps
https://www.youtube.com/watch?v=tW49z8HqsNw&feature=youtu.be&t=11m2s
Inter-node Communication Buffer Size
+zdbbl
Rabbit MQ explain
https://www.rabbitmq.com/runtime.html#distribution-buffer
最终选用的参数
经过本地压力测试(10k websocket connections + 10k nsq message consumption per second, send those nsq binary to websocket)
可能因为时间不够长(30 minutes, 然而根据 ferd 的博客, memory fragmentation 要数周才体现出来), 压力不够大,
测试结果: 以下参数对比默认参数, RES 减少 10 % 多
1 | +P 1048576 |
erl flags doc
http://erlang.org/doc/man/erl.html
TODO
- +M
ramv - +M
acul|de - carrier pool
- carrier header size