除了网络协议栈完全在用户态处理,内存数据访问的命中率也有50%+的提升,上下文切换大幅度减少。
性能提升机制主要包括: 提升IPC,内存索引,通过软件预期降低cache未命中,通过巨页内存降低TLB未命中,通过用户空间驱动降低上下文切换次数。
参考:
rte_sched.h文件包含port,subport和pipe的配置功能。
Port调度入队API非常类似于DPDK PMD TX功能的API。
Port调度入队API非常类似于DPDK PMD RX功能的API。
内部数据结构示意图,详细内容如下。
多核缩放策略如下:
上面强调过,同一个端口的出队和入队需要由同一个线程执行。因为,在不同core上对同一个输出端口执行出队和入队 *** 作,可能会对调度程序的性能造成重大影响,因此不推荐这样做。
同一端口的入队和出队 *** 作共享以下数据结构的访问权限:
可能存在使性能下降的原因如下:
当调度程序入队和出队 *** 作必须在同一个线程运行,允许队列和位图 *** 作非线程安全,并将调度程序数据结构保持在同一个core上,可以很大程度上保证性能。
扩展NIC端口数量只需要保证用于流量调度的CPU内核数量按比例增加即可。
每个数据包的入队步骤:
应该注意到这些步骤之间具有很强的数据依赖性,因为步骤2和3在步骤1和2的结果变得可用之前无法启动,这样就无法使用处理器乱序执行引擎上提供任何显着的性能优化。
考虑这样一种情况,给定的输入报文速率很高,队列数量大,可以想象得到,入队当前数据包需要访问的数据结构不存在于当前core的L1或L2 data cache中,此时,上述3个内存访问 *** 作将会产生L1和L2 data cache miss。就性能考虑而言,每个数据包出现3次L1 / L2 data cache miss是不可接受的。
解决方法是提前预取所需的数据结构。预取 *** 作具有执行延迟,在此期间处理器不应尝试访问当前正在进行预取的数据结构,此时处理器转向执行其他工作。可用的其他工作可以是对其他输入报文执行不同阶段的入队序列,从而实现入队 *** 作的流水线实现。
下图展示出了具有4级水线的入队 *** 作实现,并且每个阶段 *** 作2个不同的输入报文。在给定的时间点上,任何报文只能在水线某个阶段进行处理。
由上图描述的入队水线实现的拥塞管理方案是非常基础的:数据包排队入队,直到指定队列变满为止;当满时,到这个队列的所有数据包将被丢弃,直到队列中有数据包出队。可以通过使用RED/WRED作为入队水线的一部分来改进,该流程查看队列占用率和报文优先级,以便产生特定数据包的入队/丢弃决定(与入队所有数据包/不加区分地丢弃所有数据包不一样)。
从当前pipe调度下一个数据包的步骤如下:
为了避免cache miss,上述数据结构(pipe,queue,queue array,mbufs)在被访问之前被预取。隐藏预取 *** 作的延迟的策略是在为当前pipe发出预取后立即从当前pipe(在grinder A中)切换到另一个pipe(在grinderB中)。这样就可以在执行切换回pipe(grinder A)之前,有足够的时间完成预取 *** 作。
出pipe状态机将数据存在处理器高速缓存中,因此它尝试从相同的pipe TC和pipe(尽可能多的数据包和信用)发送尽可能多的数据包,然后再移动到下一个活动TC pipe(如果有)或另一个活动pipe。
输出端口被建模为字节槽的传送带,需要由调度器填充用于传输的数据。对于10GbE,每秒需要由调度器填充12.5亿个字节的槽位。如果调度程序填充不够快,只要存在足够的报文和信用,则一些时隙将被闲置并且带宽将被浪费。
原则上,层次调度程序出队 *** 作应由NIC TX触发。通常,一旦NIC TX输入队列的占用率下降到预定义的阈值以下,端口调度器将被唤醒(基于中断或基于轮询,通过连续监视队列占用)来填充更多的数据包进入队列。
调度器需要跟踪信用逻辑的时间演化,因为信用需要基于时间更新(例如,子流量和管道流量整形,流量级上限执行等)。
每当调度程序决定将数据包发送到NIC TX进行传输时,调度器将相应地增加其内部时间参考。因此,以字节为单位保持内部时间基准是方便的,其中字节表示物理接口在传输介质上发送字节所需的持续时间。这样,当报文被调度用于传输时,时间以(n + h)递增,其中n是以字节为单位的报文长度,h是每个报文的成帧开销字节数。
调度器需要将其内部时间参考对齐到端口传送带的步速。原因是要确保调度程序不以比物理介质的线路速率更多的字节来馈送NIC TX,以防止数据包丢失。
调度程序读取每个出队调用的当前时间。可以通过读取时间戳计数器(TSC)寄存器或高精度事件定时器(HPET)寄存器来获取CPU时间戳。 当前CPU时间戳将CPU时钟数转换为字节数:time_bytes = time_cycles / cycles_per_byte,其中cycles_per_byte是等效于线上一个字节的传输时间的CPU周期数(例如CPU频率 2 GHz和10GbE端口,* cycles_per_byte = 1.6 *)。
调度程序维护NIC time的内部时间参考。 每当分组被调度时,NIC time随分组长度(包括帧开销)增加。在每次出队调用时,调度程序将检查其NIC time的内部引用与当前时间的关系:
调度器往返延迟(SRTD)是指调度器在同一个pipe的两次连续检验之间的时间(CPU周期数)。
为了跟上输出端口(即避免带宽丢失),调度程序应该能够比NIC TX发送的n个数据包更快地调度n个数据包。
假设没有端口超过流量,调度程序需要跟上管道令牌桶配置的每个管道的速率。这意味着管道令牌桶的大小应该设置得足够高,以防止它由于大的SRTD而溢出,因为这将导致管道的信用损失(带宽损失)。
当满足以下所有条件时,从(subport S,pipe P,traffic class TC,queue Q)发送下一个分组的调度决定(分组被发送):
如果满足所有上述条件,则选择分组进行传输,并从子接口S,子接口S流量类TC,管道P,管道P流量类TC中减去必要的信用。
23.2.4.6.2. 帧开销
由于所有数据包长度的最大公约数为1个字节,所以信用单位被选为1个字节。传输n个字节的报文所需的信用数量等于(n + h),其中h等于每个报文的成帧开销字节数。
Subport和pipe的流量整形使用每个subport/pipe的令牌桶来实现。令牌桶使用一个饱和计数器实现,该计数器跟踪可用信用数量。
令牌桶通用参数和 *** 作如下表所示。
为了实现上述的令牌桶通用 *** 作,当前的设计使用表23.8中所示的数据结构,而令牌桶 *** 作的实现在表23.9中描述。
桶速率(以字节为单位)可以用以下公式计算:
同一管道内流量级别的严格优先级调度由管理出队状态机实现,该队列按升序选择队列。因此,12..15(TC 3,最低优先级TC),队列8..11(TC 2),队列4..7(TC 1,比TC 0的优先级低),队列0..3(TC 0,最高优先级TC相关联)。
Pipe和Subport级别的流量类别不是流量整形,因此在此上下文中不存在令牌桶。通过周期性地重新填充subport/pipe流量信用计数器来执行subport和pipe级别的流量类别的上限,每次为该subport/pipe调度数据包时消耗信用量,如表23.10所述 和表23.11。
能提升40倍。DPDK只是单纯的从驱动拿数据,然后组织成数据块给人用,跑在用户态。功能相当于linux的设备无关接口层,处于socket之下,驱动之上。只不过linux协议栈的这部分在核心态。包处理器,很多时候是不用linux内核协议栈的,而是用专用包处理程序,类似于DPDK加上层应用处理。通常会有些硬件加速,包处理效率更高些。缺点是一旦用不上某些功能,那些加速就白费了。而纯软件处理就非常灵活,不过代价就是功耗和性能。
纯DPDK性能非常高,intel自己给出的数据是,处理一个包80时钟周期。一个3.6Ghz的单核双线程至强,64字节小包,纯转发能力超过90Mpps,也就是每秒9千万包。如果加上linux
socket协议栈,比如跑个纯http包反d,那么根据测量,会掉到3000-4000周期处理一个包,单核双线程在2.4Mpps,每秒两百四十万包,性能差40倍。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)