[DPDK] 转发 DPDK分析
前半部分,对于背景和需求的分析,写的相当好啊!
原文地址:https://www.jianshu.com/p/0ff8cb4deaef
背景分析
前10年中,网络程序性能优化的目标主要是为了解决C10K问题,其研究主要集中在如何管理数万个客户端并发连接,各种I/O框架下如何进行性能优化,以及操作系统参数的一些优化。当前,解决C10K问题的服务器已经非常多。Nginx和Lighttpd两款非常优秀的基于事件驱动的web服务框架,Tornado和Django则是基于python开发的非阻塞的web框架这些软件使得C10K已经不再是问题了。
- 从整体上看
为了满足日益增长的需求主要采用分布式集群来分担负荷,应对大量的用户请求.
-
从结构上来看一个节点的服务器框架包含
-
网络模块
-
事件驱动模块
-
隔离,多核业务分发模块
-
业务层
-
在单个节点上,核的使用来看,主要包括
-
单线程服务器
优点是无竞争,缺点是没有充分利用系统资源 -
多进程模型
隔离性好,利用了系统更多的资源,缺点是进程间资源共享难 -
多线程模型
充分利用系用资源,竞争需要小心处理
需求分析
综合分析
- 在应对网络密集型的巨大数据量时,一般选择是横向扩展节点,但是节点的增多会变相的增加设备成本和技术风险,且当集群节点到一定的量后,节点之间的交互成本本身就会成为瓶颈。
- 在特定场合下,譬如嵌入式设备上的后台服务,服务器不可能搭建集群。
因此提升服务器本身性能同样重要。
具体分析
传统服务器可能有下面的潜在问题
-
异步模式的弊端
一般我们使用epoll来高效的处理网络读写事件。在基于多线程的服务器设计框架中,在没有请求到来的时候,线程将会休眠,当数据到来时,将由操作系统唤醒对应的线程,也就是说内核需要负责线程间频繁的上下文切换,我们是在依靠操作系统调度系统来服务网络包的调度。在网络负载很大的场景下只会造成核满转且不断相互切换,进一步增加负荷.那么就需要回到最原始的方式,使用轮询方式来完成一切操作,来提升性能。 -
协议栈的扩展性
Linix诞生之初就是为电话电报控制而设计的,它的控制平面和数据转发平面没有分离,不适合处理大规模网络数据包。并且为了全面的支持用户空间的各个功能,协议栈中嵌入了大量用于对接的接口,如果能让应用程序直接接管网络数据包处理、内存管理以及CPU调度,那么性能可以得到一个质的提升。为了达到这个目标,第一个要解决的问题就是绕过Linux内核协议栈,因为Linux内核协议栈性能并不是很优秀,如果让每一个数据包都经过Linux协议栈来处理,那将会非常的慢。像Wind River和6 Wind Gate等公司自研的内核协议栈宣称比Linux UDP/TCP协议栈性能至少提高500%以上,因此能不用Linux协议栈就不用。
不用协议栈的话当然就需要自己写驱动了,应用程序直接使用驱动的接口来收发报文。PF_RING,Netmap和intelDPDK等可以帮助你完成这些工作,并不需要我们自己去花费太多时间。
Intel官方测试文档给出了一个性能测试数据,在1S Sandbridge-EP 8*2.0GHz cores服务器上进行性能测试,不用内核协议栈在用户态下吞吐量可高达80Mpps(每个包处理消耗大约200 cpu clocks),相比之下,使用Linux内核协议栈性能连1Mpps都无法达到。 -
多核的可扩展性
多核的可扩展性对性能提升也是非常重要的,因为服务器中CPU频率提升越来越慢,纳米级工艺改进已经是非常困难的事情了,但可以做的是让服务器拥有更多的CPU和核心,像国家超级计算中心的天河二号使用了超过3w颗Xeon E5来提高性能。在程序设计过程中,即使在多核环境下也很快会碰到瓶颈,单纯的增加了处理器个数并不能线性提升程序性能,反而会使整体性能越来越低。一是因为编写代码的质量问题,没有充分利用多核的并行性,二是服务器软件和硬件本身的一些特性成为新的瓶颈,像总线竞争、存储体公用等诸多影响性能平行扩展的因素。那么,我们怎样才能让程序能在多个CPU核心上平行扩展:尽量让每个核维护独立数据结构;使用原子操作来避免冲突;使用无锁数据结构避免线程间相互等待;设置CPU亲缘性,将操作系统和应用进程绑定到特定的内核上,避免CPU资源竞争;在NUMA架构下尽量避免远端内存访问 -
内存的可扩展性
内存的访问速度永远也赶不上cache和cpu的频率,为了能让性能平行扩展,最好是少访问。
从内存消耗来看,如果每个用户连接占用2K的内存,10M个用户将消耗20G内存,而操作系统的三级cache连20M都达不到,这么多并发连接的情况下必然导致cache失效,从而频繁的访问内存来获取数据。而一次内存访问大约需要300 cpuclocks,这期间CPU几乎被空闲。因此减少访存次数来避免cachemisses是我们设计的目标。
指针不要随意指向任意内存地址,因为这样每一次指针的间接访问可能会导致多次cache misses,最好将需要访问的数据放到一起,方便一次性加载到cache中使用。
按照4K页来计算,32G的数据需要占用64M的页表,使得页表甚至无法放到cache中,这样每次数据访问可能需要两次访问到内存,因此建议使用2M甚至1G的大页表来解决这个问题。
解决方案
- 控制层留给Linux做,其它数据层全部由应用程序来处理。
减少系统调度、系统调用、系统中断,上下文切换等 - 摒弃Linux内核协议栈,将数据包传输到用户空间定制协议栈
- 使用多核编程技术替代多线程,将OS绑在指定核上运行
- 针对SMP系统,使CPU尽量使用所在NUMA系统节点的内存,减少内存刷写
- 使用大页面,减少访问
- 采用无锁技术解竞争
而DPDK恰好为我们提供了解决问题的脚手架。