【DPDK】【CPU usage】DPDK应用如何计算当前系统的压力

【前言】linux

  使用DPDK开发的朋友应该都了解使用dpdk的fwd线程的工做模式是polling模式,即100%轮询的方式去加速网络IO,这样咱们在操做系统层面上来观察目标processer会发现usage一直为100%,可是这真的是系统的真实负载么?很显然并非,本文给出一种方法来计算dpdk的fwd线程的真实负载的方法。api

【场景】缓存

  使用DPDK头痛的一点就是DPDK的fwd线程工做在polling模式,会直接消耗一整个processer的计算资源,有的时候为了性能考虑,每每还会给当前processer设置isolcpus,将当前processer从内核的CFS调度器中“剥离”出来,防止有其余的task被“不长眼”的CFS调度器调度到和fwd线程同一个processer上,出现context switch,引发性能降低。网络

  而工做在polling模式的fwd线程会出现很是蛋疼的一点就是面临“没法有效的感知当前processer的压力”的问题。查看操做系统的相关信息,会发现这个processer的usage一直处于100%,可是真实状况真的是这样么?并非,在流量处于低谷的时候,这个processer每每会出现空转的状况,就是调用dpdk的api收包函数调100次,次次收包个数都是0,由于根本就没有流量,因此须要一种新的方法来计算使用dpdk fwd线程的负载状况。app

  额外多说一点,为了防止fwd线程出现空转,目前有不一样种方法来“尽可能”解决这种空转问题,主流的一般有两种:函数

  1. 利用sleep函数,简单粗暴,结合内核的NAPI策略,设定一个指望收包个数值,当实际收包个数小于这个数值就判断当前流量不大,sleep一下。
  2. 利用dpdk的rsc中断来解决,因为uio驱动只有一个中断号,所以这种方法在uio驱动基本无法用,只能在vfio场景下用。

  固然怎么防止dpdk fwd线程出现空转的解决方法不是这篇文章想讨论的主题,我后续会写我我的的解决方案,本篇文章更多的会聚焦在如何估算负载状况。性能

【分析】测试

  这里首先说明一下,这个方法里面有一部分是来自于dpdk社区的一篇文章,由intel专家ilia所写,这里是文章原文地址,本文中会有很多部分来自于这篇文章原文,可是本篇文章在实际应用中仍然是有些地方须要注意的。ui

https://software.intel.com/en-us/vtune-cookbook-core-utilization-in-dpdk-appsspa

  接下来我会结合实际应用和ilia的文章来阐述怎么“估算”dpdk fwd线程的负载状况。

  先直接说结论:

在不一样的流量压力下,dpdk fwd线程对某个网卡队列的收包行为其实是存在必定的分布的

  这句话怎么理解呢?实际上这句话就是intel专家ilia那篇文章的主要思想。这句话完整的解释是这样的:

在不一样的流量压力下,dpdk fwd线程对某个网卡的某条队列进行收包操做,单位时间内的收包次数中,收到数据包的个数存在必定的分布

  举个例子:

  我在10s内,执行了1000次收包操做,在满载的流量压力下,1000次收包可能次次收包都能收上来32个数据包;在50%压力下,我可能只有300次收包是一次调用收上来32个包;在10%的压力下,我可能只有不到100次的收包是一次性收上来32个包。

  若是能明白这个例子,就能明白前面所说的结论,关于这个结论能够直接看【测试结果】一章,观察测试结果是否符合结论。

  以收包个数为0的次数为基准,那么能够推导出一个公式:

 

 

  此公式也正是ilia的文章中提到的公式。

  公式的解释就是在循环必定次数的收包状况下,收包个数为0的次数占比,即为dpdk rx spin time,直译就是dpdk 收包空转次数,那么再将此值用1减去,便可获得dpdk fwd的压力状况。

【测试结果】

   以新建来测试上述公式的结果(新建比较吃cpu...)

 

测试结果1

 

 测试结果2

 

 测试结果3

【结论】

   根据测试结果来看,能够清晰的看到,在不一样的系统压力下,DPDK在单位时间内的收包个数为0的次数占单位时间内总的收包次数的比重是存在必定分布的,在压力越高的状况下,收包为0的次数越少,在100%满载压力的状况下,收包个数为0的次数与收包个数为32的次数比的差距已经很是巨大,这也是ilia那篇文章中主要说的内容,可是是存在如下几个问题。

  1. 在真实场景下,一个fwd线程每每不止对单独的某个网卡队列进行收包,可能会有多个网卡队列,这个时候怎么来评估负载呢?
  2. 这种估算方法实际上依赖于DPDK的收包队列,可是若是某个fwd线程没有队列呢?这种状况每每发生在设备processer的数量大于全部的网卡队列之和的状况。就是所谓的“僧多粥少”状况,面对这种状况,常见的处理方法是接管网卡驱动的processer收上来数据包后,经过计算RSS将数据包经过ring发送到其余没有接管网卡队列的processer上,那么面对这些没有“抢到”网卡队列的processer来讲,怎么计算出负载呢?
  3. fwd线程使用了sleep方法来休眠减小空转,理论上通过sleep后会影响最终估算的“负载”,使计算的负载值偏大。这个也很好理解,利用进程sleep的方法去减小压力,最多见的影响就是包的时延会增大,数据包会在进程睡眠的时候在rx ring中“积压”,那么每次收数据包的时候收到的数据包的个数就会偏多,出现0的次数就会少。
  4. fwd线程开了rxq中断,这种状况也不须要用本文的方法去计算负载了,直接pidstat就能够看了...

  面对上述几个问题:

  1. 多队列的状况下,我我的认为能够采起方案是取压力最大的队列的压力值,实际测试以后发现效果还不错,所以最后采用了这种方案。
  2. 这种状况下,我我的以为能够经过和收网卡队列相同的思路,在没有接管到网卡队列的fwd线程去收ring中的数据包时,也采用这种计算策略,当时因为需求问题,我本人并无验证,有兴趣的道友能够去尝试一下。
  3. sleep的这种状况比较难以免,须要根据实际状况去分析,因为采用sleep下降空转的策略中经常会有两个参数,一个是sleep的时间,一个是指望的权重,我本人实际测试,这二者的参数设置的不一样对负载的估算是不一样的,一样须要实际场景你测试调整。

【另外一个问题:DPDK应用怎么“预见”即将可能发生的流量过载】

  在估算出实际fwd线程的压力后,会发现有这样的一个问题,系统能够感知逼近的压力,可是没法得知压力的具体大小,举个例子,当上述公式计算出最后fwd线程压力为100%时,此时是过载仍是满载呢?在性能测试时,这两种状态虽然在表现上是压力皆为100%,可是满载的状况下,并不会发生丢包,而过载的状况下网卡会发生无差异丢包。

  相信搞过性能的朋友经常会遇到一种丢包状况:

rx_missed

  rx_missed,这种错误在性能测试时测试系统上线会经常预见,在传统linux场景下,利用ethtool -S [port name]便可观察到此种丢包。这种丢包经常是因为网卡的rx队列被数据包“打爆”了,cpu收包的速度比不上实际数据包来的速度,那么就会造成相似于“漏斗”同样的流程,漏斗上方的进水量大于漏斗下方的出水量,那么只要时间足够,漏斗上方溢水是早晚的事情。在网卡收包时,cpu从rx ring中收取数据包,可是当流量压力过大时,rx ring会充满待处理的数据包,此时网卡没法再将数据包扔到rx ring中,那么网卡会将接下来来临的数据包进行无差异丢弃,并在rx_missed计数上进行增长。

  那么面对这种场景,有没有方法能够尽量的预见到即将可能来临的流量高峰呢?通过上述的叙述相信心中已经有了一种答案,那就是查看网卡rx ring,查看rx ring中还有多少待处理的描述符(description)便可,这里须要对processer怎么从网卡上收包有必定了解,不了解的话也不要紧,我大概介绍一下原理接口,先上图

图4.收包原理图

P.S.这个图一样来自ilia专家的那篇文章中,感谢专家...

  上图是一个网卡和processer常见的收包协做图(发包就是反过来),一般网卡的rx ring上会有两个index变量,一个叫作Head,一个叫作Tail,网卡会将收取的数据包push到Head指向的包描述符的内存中(不知道描述符是啥的童鞋就当作网卡收包向Head指针指向的空间去扔就好了),而后Head++,一样,processer从Tail指向的包描述符的内存中去收数据包,这样一个环形队列,网卡做为producer,而processer做为consumer演出了一场网卡收包的协奏。这种场景下,而且网卡在将数据包扔到rx ring时,会将对应位置的包描述符回写一个0x01的状态位,以ixgbe驱动为例:代码目录drivers/net/ixgbe/base/ixgbe_type.h

/* Receive Descriptor bit definitions */
#define IXGBE_RXD_STAT_DD    0x01 /* Descriptor Done */
#define IXGBE_RXD_STAT_EOP    0x02 /* End of Packet */
#define IXGBE_RXD_STAT_FLM    0x04 /* FDir Match */
#define IXGBE_RXD_STAT_VP    0x08 /* IEEE VLAN Packet */

  也就是上述代码中的IXGBE_RXD_STAT_DD标志,那综上所述,咱们只须要统计Tai -> Head之间有多少个0X01状态的描述符就能够肯定(也就是上述图4的右侧Tail和Head之间的方块数),目前网卡rx ring中“积存”了多少数据包。

  可是,上述在操做,在DPDK的代码中都已经实现啦!

uint32_t
ixgbe_dev_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id) { #define IXGBE_RXQ_SCAN_INTERVAL 4 volatile union ixgbe_adv_rx_desc *rxdp; struct ixgbe_rx_queue *rxq; uint32_t desc = 0; rxq = dev->data->rx_queues[rx_queue_id]; rxdp = &(rxq->rx_ring[rxq->rx_tail]); while ((desc < rxq->nb_rx_desc) && (rxdp->wb.upper.status_error & rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD))) { desc += IXGBE_RXQ_SCAN_INTERVAL; rxdp += IXGBE_RXQ_SCAN_INTERVAL; if (rxq->rx_tail + desc >= rxq->nb_rx_desc) rxdp = &(rxq->rx_ring[rxq->rx_tail + desc - rxq->nb_rx_desc]); } return desc; }

  上述代码位置:drivers/net/ixgbe/ixgbe_rxtx.c中,可是上述函数有个很大的问题就是时间复杂度很高...最极端的状况下要循环4096/4 = 1024次(网卡rx ring最大4096,4是因为上述函数遍历的步长为4)才能够算出有多少个待处理的包。因此上述函数能够利用二分法来加速获取计算的过程,最极端的状况下也只须要循环12次就能够算出网卡队列中积存的数据包个数,关键就在于IXGBE_RXDADV_STAT_DD这个标志,这里就不说了,有兴趣的能够思考一下。

  那获得了网卡队列中积存的数据包个数以后咱们怎么才能判断出是否将要出现“过载流量”呢?

  这个也很简单,只要网卡队列中积存的数据包处于一个较低的水平,那么就不会出现丢包的可能;若是网卡队列中积存的数据包数量忽然上升,那么颇有可能网卡的rx ring直接被流量打爆,在高端设备中,数据包量很是庞大的场景下,打爆最大4096长度的网卡队列(最多只能缓存4096个数据包)就是一瞬间的事情,这个一样也很好理解,仍是以漏斗举例,实际上,若是注水的速度小于出水的速度,不管注水的时间长短,漏斗中积存的水量一定为一个较低的水平,或者是根本不会有积存的水量;而当注水的速度大于出水的速度,那么将漏斗打满只是时间问题。

  通过个人实际测试,满载的状况下,网卡队列中积存的数据包一直处于300个如下的水平,可是只要测试机的网络流量超过了系统的处理能力,网卡队列中积存的数据包很快就上了1000以上.....

【一点想法】

单单靠ilia专家的公式只能计算出当前系统的压力状况(且有条件限制),没法预知即将到来的流量高峰;单单靠网卡rx ring中积存的数据包个数只能判断出即未来流量高峰,可是却没法得知fwd线程压力,那么咱们须要一套组合拳:

if (fwd_rx_load >= 90 && rte_get_rx_queue_count() >= 512)
    //须要采起措施,流量极可能即将过载
相关文章
相关标签/搜索