Linux主机上联网的CPU开销

2020-05-15 22:59:47

在评估主机的网络时,重点通常是延迟、吞吐量或每秒数据包数(PPS),以查看系统在给定配置下可以处理的最大负载。虽然这些指标很重要,而且通常很能说明问题,但此类基准测试的结果并不能告诉您处理这些数据包对在该系统上运行的工作负载有什么影响。

这篇文章着眼于从主机上运行的进程窃取的CPU周期方面的网络成本。

Linux将在处理IRQ的时刻在CPU上运行的上下文中处理相当数量的数据包。系统记帐会将这些CPU周期归因于当时运行的任何进程,即使该进程没有代表其执行任何工作。例如,';top';可以显示一个进程似乎使用了99%+%的CPU,但实际上60%的时间用于处理数据包,这意味着该进程实际上只使用了40%的CPU来处理其工作负载。

NET_RX_ACTION是网络Rx流量的处理程序,通常运行非常快-就像在25个USECS[1]下一样-在推迟到另一个软RQ周期之前,每个NAPI实例(NIC和RPS)一次最多处理64个数据包。在休息之前,软中断周期可以是背靠背的,最多10次或2毫秒(参见__do_softirq)。如果在达到最大循环数或时间之后,softirq向量仍有更多工作要做,它会将进一步的工作推迟到该CPU的ksoftirqd线程。当发生这种情况时,系统在网络开销方面更加透明,因为可以监视CPU使用情况(尽管假设它是数据包处理,而不是其他软件)。

SUDO性能记录-a\-e irq:irq_handler_entry,irq:irq_handler_exit-e irq:softirq_entry--filter=";vec==3";\-e irq:softirq_exit--filter=";vec==3";\-e napi:napi:napi_poll\--睡眠1sudo性能脚本。

swapper 0[005]176146.491879:irq:irq_handler_entry:irq=152 name=mlx5_comp2@pci:0000:d8:00.0swapper 0[005]176146.491880:irq:irq_handler_exit:irq=152ret=handledswapper 0[005]176146.491880:irq:softirq_entry:vec=3[ACTION=NET_RX]swapper 0[005]176146.491942:napi:napi。176146.491943:irq:SOFTTIRQ_ENTRY:vec=3[ACTION=NET_RX]SWAPPER 0[005]176146.491971:nAPI:nAPI_POLL:NAPI轮询设备eth0的NAPI结构0xff9d3d53863e88 27预算64swapper 0[005]176146.491971:irq:SOFTTIRQ_EXIT:VEC=3[ACTION=NET_RX]swapper 0[005]176146.492200:irq。

在这种情况下,CPU空闲(因此进程的交换器),为CPU5上的Rx队列触发IRQ,软中断处理循环两次,处理64个分组,然后处理27个分组,然后退出,下一个IRQ在229微秒之后触发,并再次开始循环。

以上内容是在空闲系统上记录的。通常,任何任务都可以在CPU上运行,在这种情况下,上述一系列事件通过中断该任务、执行irq/softirq舞蹈以及系统记帐将周期归因于中断的进程来进行。因此,处理数据包通常对通常的CPU监控是隐藏的,因为它是在一些随机的受害进程的上下文中完成的,那么如何查看或量化进程处理数据包中断的时间呢?您如何比较两种不同的网络解决方案,以确定哪一种对工作负载的干扰较小?

使用RSS、RPS和流控制,数据包处理通常跨核心分布,因此上述数据包处理顺序都是按CPU进行的。随着数据包速率的增加(考虑100,000个pps以上),负载意味着每个CPU每秒处理1000到10,000个数据包。处理如此多的数据包将不可避免地影响在这些系统上运行的工作负载。

首先,让撤消分布式处理的方法是禁用RP并安装流规则,以强制在单个已知CPU上处理特定MAC地址的所有数据包。我的系统在802.3ad配置中绑定了2个NIC,网络负载指向主机上运行的单个虚拟机。

对于eth0 eth1中的d;请查找/sys/class/net/${d}/queue-name rps_cpu|同时读取f;执行回显0|sudo tee${f}donedone。

接下来,添加流规则以将测试虚拟机的数据包推送到单个CPU。

dmac=12:34:de:ad:ca:fesudo ethtool-N eth0流动型以太dst${dmac}操作2sudo ethtool-N eth1流动型以太dst${dmac}操作2。

总而言之,缺少RPS+流规则可确保在同一CPU上处理所有发往虚拟机的数据包。您可以使用ethq[3]这样的命令来验证数据包是否定向到预期队列,然后使用/proc/interrupts将该队列映射到CPU。在我的例子中,队列2在CPU5上处理。

我可以使用perf或BPF程序来跟踪网络Rx的softirq进入和退出,但这很快就会变得复杂,而且观察结果肯定会影响结果。一种更简单、更直观的解决方案是使用众所周知的工作负载(如OpenSSL速度)推断网络开销,并查看其实际获得的CPU访问量与感知获得的CPU访问量(认识到进程记帐的笨拙)。

openssl速度几乎是100%的用户空间命令,当固定到CPU上时,将在其测试期间使用该CPU的所有可用周期。该命令的工作方式是在给定的时间间隔内设置警报(例如,这里为10秒,用于简单的数学计算),启动到其基准测试,然后使用触发警报时的time()作为检查实际给出了多少CPU时间的一种方式。从系统调用的角度来看,它看起来如下所示:

报警(10)=1726601344次({tms_utime=0,tms_stime=0,tms_cutime=0,tms_cstime=0})=1726601344-信号{si_signo=信号,si_code=SI_内核}-rt_sigaction(信号,.)=0rt_sigreturn({掩码=[]})=2782545353次({tms_utime=1000,tms。

所以在报警和检查结果之间很少有系统调用time()。在没有/很少中断的情况下,tms_utime将匹配测试时间(在本例中为10秒)。

因为它是一个纯粹的用户空间基准,所以出现在time()中的任何系统时间都是开销。OpenSSL可能是CPU上的进程,但CPU实际上正在做其他事情,比如处理数据包。例如:

报警(10)=1726617896次({tms_utime=0,tms_stime=0,tms_cutime=0,tms_cstime=0})=1726617896-信号{si_signo=信号,si_code=SI_kernel}-rt_sigaction(信号,.)=0rt_sigreturn({掩码=[]})=4079301579次({tms_utime=178,tms。

显示OpenSSL在CPU上运行了7.49秒(以0.01为增量为178+571),但其中5.71秒在系统时间内。因为OpenSSL在内核中不做任何事情,所以这5.71秒是所有开销-从这个过程中窃取的时间用于“系统需求”。

了解了OpenSSL SPEED的工作原理后,让我们来看看接近空闲的服务器:

$taskset-c 5 openssl速度-秒10 aes-256-cbc;/dev/null对16个大小的块执行aes-256cbc 10s:9.99中的66675623 aes-256cbc;对64个大小的块执行aes-256cbc:10.00s中的18096647 aes-256cbc;对256个大小的块执行10s的aes-256cbc:10.00sDos中的4607752 aes-256cbc;在10.00s中对8192个大小的块执行AES-256CBC 10s:145251个AES-256CBC在10.00s中执行10s在16384个大小的块上执行AES-256CBC:在10.00s中执行72831个AES-256cbc。

因此,在本例中,OpenSSL报告每个块大小的运行时间为9.99到10.00秒,确认没有争用CPU。让添加网络负载,从两个来源获取netperf tcp_stream,然后重新进行测试:

$taskset-c 5 openssl速度-秒10aes-256cbc;/dev/null对16个大小的块执行aes-256cbc 10s:1.96s中的12061658 aes-256cbc;对64个大小的块执行10s的aes-256cbc:2.10s中的3457491 aes-256cbc;对256个大小的块执行10s的aes-256cbc:2.01s中的893939 aes-256cbc;s in 1.86s对8192个大小的块执行aes-256cbc 10s:25117 aes-256cbc in 1.78s对16384个大小的块执行aes-256cbc 10s:13859 aes-256cbc在1.89s内。

结果大相径庭。每个块大小测试都希望运行10秒,但是Times()报告的实际用户时间介于1.78秒和2.10秒之间。因此,另外7.9到8.22秒用于处理数据包-无论是在OpenSSL上下文中还是通过ksoftirqd。

PID用户PR NI VIRT分辨率SHR S%CPU%MEM TIME+命令P 8180libvirt+20 033.269G 1.649G 1.565G S 279.9 0.918:57.81qemu-system-x86 758374 root 20 0 00 0 R 99.4 0.0 2:57.97 vhost-8180 89 1684 dahern 20 0 17112 44003892 R 73.6 0.0 0:09.91 opensssl 5 38 root 20 0 0 0 R 26.2 0.0 0。

有人会认为OpenSSL占用了大约73%的CPU 5,ksoftirqd占用了其余的,但实际上,在OpenSSL环境中处理的数据包太多了,以至于它实际上只有18-21%的CPU时间来处理其工作负载。

如果我将网络负载降到只有1个流,OpenSSL似乎在99%的CPU上运行:

PID用户PR NI VIRT分辨率SHR S%CPU%内存时间+命令P8180libvirt+20 033.269G 1.722G 1.637G S 325.1 0.9166:38.12qemu-system-x86 2944218 dahern 20 0 17112 44883996 R 99.2 0.0 0 0:28.55openssl 58374 root 20 0 0 0 R 64.7 0.0 60:40.50vhost-8180 55 38 root 20 0 0 0 S 1.0 0.0 4:51.98。

对16个大小的块执行10s的AES-256CBC:4.01中的26596388个AES-256CBC对64个大小的块执行10s的AES-256CBC:4.14中的7137481个AES-256CBC对256个大小的块执行10s的AES-256CBC:4.31中的1844565个AES-256CBC对10s的10s执行1024个大小的块:4.14中的472687个AES-256CBC。在16384个大小的块上执行10s的AES-256CBC:在4.16s内执行28569个AES-256CBC。

同样,监视工具显示大量CPU访问,但实际情况大不相同,55-80%的CPU用于处理数据包。吞吐量数字看起来很大(25G链路为22+Gbps),但对流程的影响是巨大的。

在本例中,被剥夺CPU周期的进程是一个愚蠢的基准。在完全填充的主机上,中断的进程可以是任何东西-VM的虚拟CPU、VM的仿真器线程、VM的vhost线程或对这些进程和系统的性能有不同程度影响的主机级系统进程。

这篇文章是后续文章的基础,在后续文章中,我将讨论VM主机上“全栈”与“XDP”开销的比较。

[2]假设网络堆栈和驱动程序中没有错误。我检查过一些系统,在这些系统中,由于NIC驱动程序(ARFS路径)和OVS(雷鸣群唤醒)中的错误的组合,net rxaction处理少于64个数据包所需的时间远远超过20,000 Usec。