云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

网站空间_百度云视频怎么加速播放_免费1年

小七 141 0

如何利用10Gbps以太网实现低延迟

早上好!在最近的一篇博客文章中,我们解释了如何调整一个简单的UDP应用程序以最大化吞吐量。这一次我们将优化UDP应用程序的延迟。与延迟抗争是讨论多队列NIC现代特性的一个很好的借口。本文介绍的一些技术也在缩放.txt内核文档。CC BY-SA 2.0图片作者:邓晓军我们的实验设置如下:我们将有两个物理Linux主机:"客户机"和"服务器"。它们使用简单的UDP-echo协议进行通信。客户机发送一个小UDP帧(32字节的有效负载)并等待应答,测量往返时间(RTT)。服务器在收到数据包后立即回显这些数据包。两台主机都有2GHz的Xeon CPU,有两个6核插槽,并启用了超线程(HT),因此每个主机有24个CPU。客户端有一个Solarflare 10Gb网卡,服务器有一个Intel 82599 10Gb网卡。两个卡都有光纤连接到10Gb交换机。我们要测量往返时间。由于数字很小,所以在计算平均值时会有很多抖动。取而代之的是,取一个稳定的值——一秒钟内多次运行的最低RTT。和往常一样,这里使用的代码可以在GitHub上找到:udpclient.c、udpserver.c。先决条件首先,让我们显式地分配IP地址:客户端$ip addr add 192.168.254.1/24 dev eth2服务器$ip addr add 192.168.254.30/24 dev eth3确保iptables和conntrack不会干扰我们的流量:客户端$iptables-I输入1--src 192.168.254.0/24-j ACCEPT客户端$iptables-t raw-I PREROUTING 1--src 192.168.254.0/24-j NOTRACK服务器$iptables-I输入1--src 192.168.254.0/24-j ACCEPT服务器$iptables-t raw-I PREROUTING 1--src 192.168.254.0/24-j NOTRACK最后,确保多队列网卡的中断在CPU之间均匀分布。irqbalance服务停止,中断被手动分配。为了简单起见,让我们将RX队列#0固定到CPU#0,将RX队列#1固定到CPU#1,依此类推。客户机$(让CPU=0;cd/sys/class/net/eth2/device/msi_irqs/;对于IRQ in*;执行echo$CPU>/proc/irq/$irq/smp\u关联列表设CPU+=1完成)服务器$(让CPU=0;cd/sys/class/net/eth3/device/msi_irqs/;对于IRQ in*;执行echo$CPU>/proc/irq/$irq/smp\u关联列表设CPU+=1完成)这些脚本将每个接收队列触发的中断分配给选定的CPU。最后,一些网卡默认启用以太网流控制。在我们的实验中,我们不会推太多的数据包,所以这并不重要。在任何情况下,一般用户都不太可能真正想要流量控制,它会在较高负载期间引入不可预测的延迟峰值:客户端$sudo ethtool-A eth2 autoneg off rx off tx off服务器$sudo ethtool-A eth3 autoneg off rx off tx off天真的往返这是我们的客户代码草图。没什么特别的:发送一个包,测量时间,直到我们听到回应。fd=插座.插座(插座, 承插式插座)fd.绑定(("0.0.0.0",65400))#引脚源端口,以减少不确定性fd.连接("192.168.254.30",4321)如果是真的:t1=时间。时间()发送消息("\x00"*32)fd.readmsg公司()t2=时间。时间()打印"rtt=%.3fus"((t2-t1)*1000000)服务器也同样简单。它等待数据包并将其回显到源:fd=插座.插座(插座, 承插式插座)fd.绑定("0.0.0.0",4321)如果是真的:数据,客户端地址=fd.recvmsg公司()发送消息(数据,客户地址)让我们运行它们:服务器$./udpserver客户美元/UDP客户192.168.254.30:4321[*]发送到192.168.254.30:4321,src_port=65500pps=16815平均值=57.224us偏差=24.970us最小值=45.594uspps=15910平均值=60.170us偏差=28.810us最小值=45.679uspps=15463平均值=61.892us偏差=33.332us最小值=44.881us在幼稚的运行中,我们每秒可以进行大约15k次往返,平均RTT为60微秒(us),最小为44us。标准差非常可怕,表明存在高抖动。内核轮询-SO峈BUSY峈POLLLinux3.11增加了对sobusy\u POLL socket选项的支持。其思想是要求内核在给定的时间内轮询传入的数据包。当然,这会增加计算机上的CPU使用率,但会减少延迟。这样做的好处是在收到数据包时避免了主要的上下文切换。在我们的实验中,启用sou BUSY_POLL可以将最小延迟降低7us:服务器$sudo./udpserver--busy poll=50客户端$sudo./udpclient 192.168.254.30:4321--busy poll=50pps=19440平均值=49.886us偏差=16.405us最小值=36.588uspps=19316平均值=50.224us偏差=15.764us最小值=37.283uspps=19024平均值=50.960us偏差=18.570us最小值=37.116us虽然我对这些数字并不感到兴奋,但对于somusy_POLL可能有一些有效的用例。据我所知,与其他方法不同,它与中断合并(rx usecs)一起工作得很好。用户空间忙轮询我们可以在应用程序中进行轮询,而不是在内核中使用sobusy-POLL进行轮询。让我们避免在recvmsg上阻塞,并在繁忙的循环中运行一个非阻塞变量。服务器伪代码如下所示:如果是真的:如果是真的:数据,客户端地址=fd.recvmsg公司(邓先生)如果数据:打破发送消息(数据,客户地址)这种方法非常有效:server$./udpserver—轮询client$/udpclient 192.168.254.30:4321--轮询pps=25812平均值=37.426us偏差=11.865us最小值=31.837uspps=23877平均值=40.399us偏差=14.665us最小值=31.832uspps=24746平均值=39.086us偏差=14.041us最小值=32.540us不仅最短时间又减少了4us,而且平均值和偏差看起来更健康。引脚工艺到目前为止,我们允许Linux调度程序为繁忙的轮询应用程序分配CPU。一些抖动来自于被移动的进程。让我们尝试将它们固定到特定的核心:服务器$taskset-c3./udpserver--polling客户端$taskset-c3./udpclient 192.168.254.30:4321--轮询pps=26824平均值=35.879us偏差=11.450us最小值=30.060uspps=26424平均值=36.464us偏差=12.463us最小值=30.090uspps=26604平均值=36.149us偏差=11.321us最小值=30.421us这又减少了1美元。不幸的是,在"坏"的CPU上运行我们的应用程序实际上可能会降低数字。为了理解为什么我们需要重新检查包是如何在RX队列中被调度的。RSS-接收端缩放在上一篇文章中,我们提到了NIC散列数据包,以便将负载分散到多个RX队列中。这种技术称为RSS接收端缩放。我们可以通过使用软性.sh脚本:这是有意义的-因为我们只从udpclient发送一个流(连接),所有的包都会到达同一个RX队列。在本例中,它是RX queue#1,它绑定到CPU#1。实际上,当我们在这个CPU上运行客户端时,延迟大约会增加2us:客户端$taskset-c1./udpclient 192.168.254.30:4321--轮询pps=25517平均值=37.615us偏差=12.551us最小值=31.709uspps=25425平均值=37.787us偏差=12.090us最小值=32.119uspps=25279平均值=38.041us偏差=12.565us最小值=32.235us结果表明,在到达时保持进程在同一个核心上会稍微降低延迟。但是如果CPU运行不好怎么知道呢?一种方法是让应用程序查询内核。内核3.19引入了sou-INCOMING-ung-CPU套接字选项。有了它,进程就可以知道数据包最初被传送到哪个CPU。另一种方法是确保数据包只发送到我们想要的cpu。我们可以通过使用ethtool调整NIC设置来做到这一点。间接表RSS旨在跨RX队列分配负载。在我们的例子中,一个UDP流,RX队列由以下公式选择:RX_queue=INDIR[散列(src_ip,dst_ip)%128]如前所述,UDP流的哈希函数在Solarflare NICs上是不可配置的。幸运的是INDIR表是!但什么是INDIR?好吧,这是一个间接表,它将哈希的最低有效位映射到一个RX队列号。要查看间接表,请运行ethtool-x:这读作:散列值等于72的包将进入RX队列#6,散列126将进入RX队列#5,依此类推。可以配置此表。例如,为了确保所有流量只流向CPU#0-#5(我们设置中的第一个NUMA节点),我们运行:客户端$sudo ethtool-X eth2 weight 1 1 1 1 1 0 0 0 0 0服务器$sudo ethtool-X eth3权重1 1 1 1 1 0 0 0 0 0调整后的间接表:通过这个设置,我们的延迟数没有太大变化,但至少我们可以确定数据包总是只命中第一个NUMA节点上的cpu。缺少NUMA地区通常花费2美元左右。我应该提到intel82599支持为UDP流调整RSS散列函数,但是驱动程序不支持间接表操作。为了让它运行,我用了这个补丁。流量控制还有另一种确保数据包命中特定CPU的方法:流控制规则。流控制规则用于在间接表的顶部指定异常。它们告诉NIC将特定的流发送到特定的RX队列。例如,在我们的示例中,我们可以将流固定到服务器和客户机上的RX queue#1:客户端$sudo ethtool-N eth2流类型udp4 dst ip 192.168.254.1 dst端口65500操作1添加了ID为12401的规则服务器$sudo ethtool-N eth3流类型udp4 dst端口4321操作1添加了ID为2045的规则流量控制可以用于更神奇的事情。例如,我们可以通过指定action-1请求NIC丢弃所选流量。这在DDoS数据包泛滥期间非常有用,并且通常是将数据包丢弃到路由器防火墙上的可行替代方案。一些管理员还使用流控制来确保SSH或BGP之类的东西即使在服务器负载很高的情况下也能继续工作。它可以通过操纵间接表来关闭生产流量来实现,例如,RX queue#0和CPU#0。除此之外,它们显式地将数据包转发到目的端口22或179,以便始终使用流控制规则仅命中CPU 0。不管服务器上的网络负载有多大,SSH和B