OTP 21 中的 I/O 轮询选项

2018 年 4 月 11 日 · 作者:Lukas Larsson

Erlang/OTP 21 将引入一个全新的 IO 轮询实现。这个新的实现带有一组新的可调参数,可以用来最大化系统的性能。这篇博文将介绍这些参数,并试图描述它们应该用于什么。

erts 中的 I/O 轮询框架负责向端口和进程传递事件,这些端口和进程订阅了文件描述符上的事件。在 OTP 21 之前,由 Erlang 调度器线程来传递这些事件。在 OTP 21 中,使用专用线程来传递事件。

有关新实现的底层工作原理的信息,您可以查看 Kenneth Lundin 在 EUC 2017 上的演讲:关于脏调度器和 I/O 的 Erlang VM 新闻

内核空间 vs 用户空间轮询 #

在 OTP 21 中,+K 选项已被删除,因为不再可能在运行时选择是否使用内核空间轮询。相反,该决定是在编译时做出的,默认情况下将使用内核空间轮询。如果您想使用用户空间轮询,则必须在编译 Erlang/OTP 时将 --disable-kernel-poll 标志传递给配置程序。

在 OTP 21 之前,如果订阅的文件描述符倾向于快速删除,那么使用用户空间轮询是有意义的。例如,如果 HTTP 服务器只管理来自少数其他机器的短期连接,那么使用用户空间轮询可能是有益的。但是,如果连接开始变得长期存在,或者并发连接的数量增加,那么内核空间轮询会变得更好。

在 OTP 21 中,情况不再如此。由于轮询已移至另一个线程,因此几乎总是最好使用内核空间轮询。保留用户空间轮询实现是为了那些不支持内核空间轮询集并行更新的平台。此外,当由于某种原因无法将单个文件描述符放入内核空间轮询集时,也会使用用户空间轮询。

轮询线程和轮询集 #

OTP 21 引入了两个新的配置参数:+IOt 和 +IOp。

配置 +IOt #

+IOt 控制用于传递事件的线程数。默认值为 1,对于大多数应用程序来说应该足够了。但是,在非常繁忙且具有许多并发连接的系统上,增加此值可能是有益的。要了解您的系统是否可以从中受益的一种方法是使用 msacc。如果您简短地将其打开,并在检查 msacc:print() 输出时注意到线程类型 poll 的睡眠时间很短,则系统可能会受益于增加轮询线程的数量。

Eshell V9.3  (abort with ^G)
1> msacc:start(10000),msacc:print().
Average thread real-time    : 10000410 us
Accumulated system run-time :      937 us
Average scheduler run-time  :      897 us

        Thread      aux check_io emulator       gc    other     port    sleep

Stats per thread:
     async( 0)    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
       aux( 1)    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
dirty_cpu_( 1)    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
dirty_io_s( 1)    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
      poll( 0)    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
 scheduler( 1)    0.00%    0.00%    0.00%    0.00%    0.01%    0.00%   99.99%

Stats per type:
         async    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
           aux    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
dirty_cpu_sche    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
dirty_io_sched    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
          poll    0.00%    0.00%    0.00%    0.00%    0.00%    0.00%  100.00%
     scheduler    0.00%    0.00%    0.00%    0.00%    0.01%    0.00%   99.99%

在上面的示例中,轮询线程 100% 的时间都在睡眠,因此无需增加轮询线程的数量。

配置 +IOp #

+IOp 控制用于放置文件描述符的轮询集数量。此选项默认为 1,并且任何系统都很少需要更改此选项。到目前为止,我只看到在内核空间轮询实现无法在多个线程并行访问时很好地扩展时,这样做是有益的。因此,如果您在系统上运行 perf top(或类似工具),并注意到大量时间都花费在锁定内核空间轮询集上,那么增加使用的轮询集数量将是一个好主意。