关于dpdk:dperf系列5使用dperf进行性能测试
通过后面的筹备,dperf曾经运行起来了,本文阐明用dperf如何进行性能测试。 未完待续
通过后面的筹备,dperf曾经运行起来了,本文阐明用dperf如何进行性能测试。 未完待续
这是一篇dperf的编译领导,并且做一个http-server的试验,帮忙您疾速上手。 筹备工作环境要求操作系统:CentOS 7.9(举荐)或者 内核4.x/5.x内存:>= 16GB(举荐)网口:两个(两个VF也能够)CPU核数/线程数:>= 2DPDK:版本19.11或20.11已编译实现(编译办法参考之前的文章)配置大页编辑/boot/grub2/grub.cfg linux16 /vmlinuz-xxx ... nopku transparent_hugepage=never default_hugepagesz=1G hugepagesz=1G hugepages=8留神: vmware虚拟机中要设置nopku,物理机能够疏忽;如果服务器内存比拟多,hugepages能够设置更大,实在场景倡议设置40G以上。配置大页后,须要重启失效dpdk绑定网卡Mellanox网卡无需此操作,能够跳过本节。 DPDK-19.11绑定网卡modprob uio#ARM环境下,目录有差异insmod /root/dpdk-stable-19.11.10/x86_64-native-linuxapp-gcc/kmod/igb_uio.koDPDK-20.11绑定网卡modprob uioinsmod /root/dpdk-kmods/linux/igb_uio/igb_uio.ko编写配置文件试验启动dperf客户端申请敞开dperf
dperf是百度近日开源的基于DPDK与用户态协定栈技术的一款高性能HTTP压测工具。本文领导DPDK老手们如何胜利迈出应用dperf的第一步:编译DPDK。DPDK大牛可疏忽此文或者看一眼build.md即可。 筹备工作操作系统:举荐CentOS 7.9,内核版本4.X,5.X也能够,低版本内核反对Mellanox驱动比拟麻烦DPDK下载:dpdk-20.11.2igb_uio驱动下载地址:http://git.dpdk.org/dpdk-kmods 装置编译工具: yum install meson ninja-build查看网卡厂商与驱动型号(命令参考): lspci | grep Ethernetethtool -i eth0Mellanox网卡驱动编译(可选)参考【dperf系列-2】如何编译DPDK-19.11 配置与编译DPDKcd /root/dpdk/dpdk-stable-20.11.2#dperf不必kni,能够不必-Denable_kmods=truemeson build --prefix=/root/dpdk/dpdk-stable-20.11.2/mydpdk -Denable_kmods=trueninja -C build install编译igb_uio.kocd /root/dpdk-kmods/linux/igb_uiomake#编译后果是igb_uio.ko
dperf是百度近日开源的基于DPDK与用户态协定栈技术的一款高性能HTTP压测工具。本文领导DPDK老手们如何胜利迈出应用dperf的第一步:编译DPDK。DPDK大牛可疏忽此文或者看一眼build.md即可。 因为从DPDK-20开始,编译有较大的变动,DPDK编译系列分为两篇文章介绍。本篇文章介绍DPDK-19.11编译,DPDK-19以前版本的编译办法与DPDK-19.11雷同;另一篇文章介绍DPDK-20.11编译。 筹备工作操作系统:举荐CentOS 7.9,内核版本4.X,5.X也能够,低版本内核反对Mellanox驱动比拟麻烦DPDK:下载DPDK-19.11.10 查看网卡厂商与驱动型号(命令参考): lspci | grep Ethernetethtool -i eth0Mellanox网卡驱动编译(可选)如果网卡不是Mellanox的,请跳过本节。下载社区驱动rdma-core-stable-v22编译 cd /root/rdma-core-stable-v22/build.sh设置环境变量 RDMA_CORE_BUILD_DIR=/root/rdma-core-stable-v22/buildexport C_INCLUDE_PATH=$RDMA_CORE_BUILD_DIR/includeexport LIBRARY_PATH=$RDMA_CORE_BUILD_DIR/libexport LD_LIBRARY_PATH=$RDMA_CORE_BUILD_DIR/lib配置DPDK编辑config/common_base,视状况开启如下开关 #应用Mellanox网卡:CONFIG_RTE_LIBRTE_MLX5_PMD=y#应用华为网卡:CONFIG_RTE_LIBRTE_HNS3_PMD=yCONFIG_RTE_LIBRTE_HINIC_PMD=y#KVM虚拟机CONFIG_RTE_VIRTIO_USER=y#vmware虚拟机CONFIG_RTE_LIBRTE_VMXNET3_PMD=y编译DPDK#X86处理器上编译DPDKmake install T=x86_64-native-linuxapp-gcc -j16#ARM64处理器编译DPDKmake install T=arm64-armv8a-linuxapp-gcc -j16
dperf是一款面向四层网关(如四层负载平衡,L4LB)的高性能的压测工具。近日已由百度开源,依照apache license v2公布。 通常,对四层负载平衡的测试都要用商用测试仪。如思博伦、IXIA等,价格差不多是1 HTTP CPS = 1RMB。对于网络设备厂商来说,商用测试仪是标配;然而对于很多科研单位的研究者来说,买一个思博伦是不太事实的;据我理解,很多互联网厂家也不太喜爱买商用测试仪,苦了一堆做网关的开发者。 我也是被逼得没方法,就写了一个测试仪,毕竟这是一个掉头发的事件,当初悔恨也来不及了,不说也罢。 dperf基于dpdk实现的,外面有一个高度优化、高度定制的TCP协定栈(前面再写文章介绍),实际上是一个超级简略的TCP协定栈,简单的协定栈咱也写不进去;不过外面的优化是做的能够的,也有专利技术(还没批下来),前面细说。 dperf新建性能确实很强。用两个核,一个核运行dperf客户端,一个核运行dperf服务器,在X86(Intel(R) Xeon(R) Silver 4214)上可能测出230万HTTP CPS新建,在鲲鹏902上能够测出210万。如果用ab去测试nginx,CPS只有2.5万,性能相差1百倍。 dperf并发也很强。因为dperf一个连贯只耗费了64个字节,nginx可能要耗费64K,四层负载平衡可能要耗费几百个字节。对于dperf来说,几十个亿的并发几乎就是小菜一碟。 dperf的带宽受制于网卡。带宽这个事就不是个事,上个好的网卡就能够了。 下面的这些性能,都不是我最喜爱的,因为性能太强了,很多时候用不上,花了很多工夫去优化,白白掉了很多头发不说,也就义了很多陪娃的工夫。 dperf有一项我最喜的性能:重传统计(丢包统计)。对传统tcp协定栈来说,丢个包是失常的,丢了就丢了,重传一个呗。这怎么能够呢,我预计第一个不批准的是做交换机的敌人,第二个不批准的是做四层负载平衡的敌人,哪里都能够丢包,我这里就是不能,丢包的锅我不能背。dperf也认为重传是不对的,它把重传做了计数,同门通过重传计数,就可能实时观测到,链路上是否丢包了,如果丢包了,再去看交换机,被测设施等,如果测试下来,没有丢一个包,那就妥妥的了。 用Linux协定栈上工具(ab, wrk)做性能测试敌人就没方法测出丢包数,糊里糊涂就过来了;如果你切实是看不惯,就是要和丢包过不过来,就试用一下dperf做测试吧。 如果你曾经开始应用了dperf,请通知我,在应用dperf的过程中有什么问题,请在dperf的issue上提问题,也能够给我发邮件,谢谢。 我的项目以后是110个star。圣诞节许个愿,我心愿dperf在2022年可能达到1000个star。祝大家圣诞快乐。
https://www.bilibili.com/vide...
一、 前言 才开始接触到DPDK,发现概念很多,很难以下理解,在这文章中记录下要害的内容,做到对dpdk的根本货色真正理解了。 这样前面用它来写程序才可能顺利,不能赶进度啊,越赶进度反而可能越慢,慢慢来比拟快。 本文次要是本人了解,参考很多文章,有哪里不了解的就查,做不到精湛,只理解含意。 文章算是汇编,参考多篇文章,如有侵权,请告知,谢谢! 二、 整体了解 历史: 随着计算机核数的减少,网络带宽的减少,对主机进行网络包的解决性能要求越来越高,然而当初的操作系统对网络包解决的形式很低效。 低效体现在: 1)网络数据包来了之后通过中断模式进行告诉,而cpu解决中断的能力是肯定的,如果网络中有大量的小数据包,造成了网络的拥挤,cpu解决不及时。 【以前cpu的频率远高于网络设备,所以中断很无效】 2)操作系统的协定栈是单核解决,没方法利用当初操作系统的多核。 3)网络数据包从网卡到内核空间,再到用户空间,进行了屡次数据拷贝,性能比拟差。 DPDK 全称 Data Plane Development Kit 专一于数据面的软件开发套件,是专为Intel的网络芯片开发,运行于Linux和FreeBsd上。 DPDK扭转了传统的网络数据包的解决形式,在用户空间间接解决,图示如下: 传统VSDPDK抓包形式 三、 重要概念了解 这外面阐明DPDK文档外面的次要概念,另外如何将概念与理论的咱们本人的机器上参数对应起来。 3.1 PPS:包转发率 即1s能够发送多个frame、在以太网外面为以太帧,咱们常说的接口带宽为1Gbits/s 、10Gbits/s 代表以太接口可能传输的最高速率,单位为(bit per second 位/秒) 实际上,传输过程中,帧之间有间距(12个字节),每个帧后面还有前导(7个字节)、帧首界定符(1个字节)。 帧实践转发率= BitRate/8 / (帧前导+帧间距+帧首界定符+报文长度) 以太帧传输中构造 依照10Gbits/s (没记错的话是万兆光纤)来计算下64个字节下的包的转发率。 最短帧大小 101024102410241024/(12+7+1+64) 8 约等于 1000M10 /(12+7+1+64) *8 = 14.880952380952381 M/PPS (百万数据包) 也就是1s能够发送 1千400万个数据包。 留神,这外面的Data长度是在46-1500个字节之间,所以最小的帧的长度为 : 6+6+2+46+4 = 64个字节。 线速:网卡或网络反对的最极限速度。 汇总数据: ...
TAILQ队列是FreeBSD内核中的一种队列数据结构,在一些著名的开源库中(如DPDK,libevent)有广泛的应用。TAILQ队列的定义TAILQ队列有HEAD和ENTRY两种基本的数据结构 #define TAILQ_HEAD(name, type) \struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \}#define TAILQ_ENTRY(type) \struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev;/* addr of previous next element*/ \} 注意:数据结构中的filed都是type类型的指针(或者是二级指针),这里的type是用户的队列元素类型,,将ENTRY结构内嵌到用户的QUEUE_ITEM结构中: struct QUEUE_ITEM{ int value; TAILQ_ENTRY(QUEUE_ITEM) entries; }; TAILQ_HEAD(headname,QUEUE_ITEM) queue_head; 这和Linux中list的组织方式不一样,后者是单纯地将struct list_head作为链表的一个挂接点,并没有用户的信息,具体差别可以看下图: TAILQ队列的操作TAILQ提供了多种操作队列的API,比如: TAILQ_HEAD(name, type)TAILQ_ENTRY(type)TAILQ_EMPTY(head)TAILQ_FIRST(head)TAILQ_FOREACH(var, head, field) TAILQ_INIT(head)TAILQ_INSERT_AFTER(head, listelm, elm, field)TAILQ_INSERT_BEFORE(listelm, elm, field)TAILQ_INSERT_TAIL(head, elm, field).....这些接口的实现和更多的操作接口可以参考 FreeBSD queue ...
mTCP 是一款面向多核系统的用户态网络协议栈内核态协议栈的缺陷互联网的发展,使得用户对网络应用的性能需求越来越高。人们不断挖掘CPU处理能力加强,添加核的数量,但这并没有使得网络设备的吞吐率线性增加,其中一个原因是内核协议栈成为了限制网络性能提升的瓶颈。互斥上锁引起的开销互斥上锁是多核平台性能的第一杀手。现在的服务器端应用为了尽可能的实现高并发,通常都是采用多线程的方式监听客户端对服务端口发起的连接请求。首先,这会造成多个线程之间对accept队列的互斥访问。其次,线程间对文件描述符空间的互斥访问也会造成性能下降。报文造成的处理效率低下内核中协议栈处理数据报文都是逐个处理的, 缺少批量处理的能力。频繁的系统调用引起的负担频繁的短连接会引起大量的用户态/内核态模式切换,频繁的上下文切换会造成更多的Cache Miss用户态协议栈的引入用户态协议栈-即是将原本由内核完成了协议栈功能上移至用户态实现。通过利用已有的高性能Packet IO库 (以DPDK为例)旁路内核,用户态协议栈可以直接收发网络报文,而没有报文处理时用户态/内核态的模式切换。除此之外,由于完全在用户态实现,所以具有更好的可扩展性还是可移植性。mTCP 介绍mTCP作为一种用户态协议栈库的实现,其在架构如下图所示:mTCP以函数库的形式链接到应用进程,底层使用其他用户态的Packet IO库。总结起来,mTCP具有以下特性:良好的多核扩展性批量报文处理机制类epoll事件驱动系统BSD风格的socket API支持多种用户态Packet IO库传输层协议仅支持TCP多核扩展性为了避免多线程访问共享的资源带来的开销。mTCP将所有资源(如flow pool socket buffer)都按核分配,即每个核都有自己独有的一份。并且,这些数据结构都是cache对齐的。从上面的架构图可以看到,mTCP需要为每一个用户应用线程(如Thread0)创建一个额外的一个线程(mTCP thread0)。这两个线程都被绑定到同一个核(设置CPU亲和力)以最大程度利用CPU的Cache。批量报文处理机制由于内部新增了线程,因此mTCP在将报文送给用户线程时,不可避免地需要进行线程间的通信,而一次线程间的通信可比一次系统调用的代价高多了。因此mTCP采用的方法是批量进行报文处理,这样平均下来每个报文的处理代价就小多了。类epoll事件驱动系统对于习惯了使用epoll编程的程序员来说,mTCP太友好了,你需要做就是把epoll_xxx()换成mtcp_epoll_xxx()BSD 风格的 socket API同样的,应用程序只需要把BSD风格的Socket API前面加上mtcp_ 就足够了,比如mtcp_accept()支持多种用户态Packet IO库在mTCP中, Packet IO库也被称为IO engine, 当前版本(v2.1)mTCP支持DPDK(默认)、 netmap 、onvm、 psio 四种IO engine。mTCP的一些实现细节线程模型如前所述mTCP需要会为每个用户应用线程创建一个单独的线程,而这实际上需要每个用户应用线程显示调用下面的接口完成。mctx_t mtcp_create_context(int cpu);这之后,每个mTCP线程会进入各自的Main Loop,每一对线程通过mTCP创建的缓冲区进行数据平面的通信,通过一系列Queue进行控制平面的通信每一个mTCP线程都有一个负责管理资源的结构struct mtcp_manager, 在线程初始化时,它完成资源的创建,这些资源都是属于这个核上的这个线程的,包括保存连接四元组信息的flow table,套接字资源池socket pool监听套接字listener hashtable,发送方向的控制结构sender等等用户态 Socket既然是纯用户态协议栈,那么所有套接字的操作都不是用glibc那一套了,mTCP使用socket_map表示一个套接字,看上去是不是比内核的那一套简单多了!struct socket_map{ int id; int socktype; uint32_t opts; struct sockaddr_in saddr; union { struct tcp_stream *stream; struct tcp_listener *listener; struct mtcp_epoll *ep; struct pipe pp; }; uint32_t epoll; / registered events / uint32_t events; / available events */ mtcp_epoll_data_t ep_data; TAILQ_ENTRY (socket_map) free_smap_link;};其中的socketype表示这个套接字结构的类型,根据它的值,后面的联合体中的指针也就可以解释成不同的结构。注意在mTCP中,我们通常认为的文件描述符底层也对应这样一个socket_mapenum socket_type{ MTCP_SOCK_UNUSED, MTCP_SOCK_STREAM, MTCP_SOCK_PROXY, MTCP_SOCK_LISTENER, MTCP_SOCK_EPOLL, MTCP_SOCK_PIPE, };用户态 EpollmTCP实现的epoll相对于内核版本也简化地多,控制结构struct mtcp_epoll如下:struct mtcp_epoll{ struct event_queue *usr_queue; struct event_queue *usr_shadow_queue; struct event_queue mtcp_queue; uint8_t waiting; struct mtcp_epoll_stat stat; pthread_cond_t epoll_cond; pthread_mutex_t epoll_lock;};它内部保存了三个队列,分别存储发生了三种类型的事件的套接字。MTCP_EVENT_QUEUE表示协议栈产生的事件,比如LISTEN状态的套接字accept了,ESTABLISH的套接字有数据可以读取了USR_EVENT_QUEUE 表示用户应用的事件,现在就只有PIPE;USR_SHADOW_EVENT_QUEUE表示用户态由于没有处理完,而需要模拟产生的协议栈事件,比如ESTABLISH上的套接字数据没有读取完.TCP流mTCP使用tcp_stream表示一条端到端的TCP流,其中保存了这条流的四元组信息、TCP连接的状态、协议参数和缓冲区位置。tcp_stream存储在每线程的flow table中typedef struct tcp_stream{ socket_map_t socket; // code omitted… uint32_t saddr; / in network order / uint32_t daddr; / in network order / uint16_t sport; / in network order / uint16_t dport; / in network order / uint8_t state; / tcp state */ struct tcp_recv_vars *rcvvar; struct tcp_send_vars sndvar; // code omitted… } tcp_stream;发送控制器mTCP使用struct mtcp_sender完成发送方向的管理,这个结构是每线程每接口的,如果有2个mTCP线程,且有3个网络接口,那么一共就有6个发送控制器struct mtcp_sender{ int ifidx; / TCP layer send queues */ TAILQ_HEAD (control_head, tcp_stream) control_list; TAILQ_HEAD (send_head, tcp_stream) send_list; TAILQ_HEAD (ack_head, tcp_stream) ack_list; int control_list_cnt; int send_list_cnt; int ack_list_cnt;};每个控制器内部包含了3个队列,队列中元素是 tcp_streamControl 队列:负责缓存待发送的控制报文,比如SYN-ACK报文Send 队列:负责缓存带发送的数据报文ACK 队列:负责缓存纯ACK报文例子:服务端TCP连接建立流程假设我们的服务端应用在某个应用线程创建了一个epoll套接字和一个监听套接字,并且将这个监听套接字加入epoll,应用进程阻塞在mtcp_epoll_wait(),而mTCP线程在自己的main Loop中循环本机收到客户端发起的连接,收到第一个SYN报文。mTCP线程在main Loop中读取底层IO收到该报文, 在尝试在本线程的flow table搜索后,发现没有此四元组标识的流信息,于是新建一条tcp stream, 此时,这条流的状态为TCP_ST_LISTEN将这条流写入Control队列,状态切换为TCP_ST_SYNRCVD,表示已收到TCP的第一次握手mTCP线程在main Loop中读取Control队列,发现其中有刚刚的这条流,于是将其取出,组装SYN-ACK报文,送到底层IOmTCP线程在main Loop中读取底层收到的对端发来这条流的ACK握手信息,将状态改为TCP_ST_ESTABLISHED(TCP的三次握手完成),然后将这条流塞入监听套接字的accept队列由于监听套接字是加入了epoll的,因此mTCP线程还会将一个MTCP_EVENT_QUEUE事件塞入struct mtcp_epoll的mtcp_queue队列。此时用户线程在mtcp_epoll_wait()就能读取到该事件,然后调用mtcp_epoll_accept()从Control队列读取到连接信息,就能完成连接的建立。参考资料mTCP: a Highly Scalable User-level TCP Stack for Multicore Systems ...
写在前面本系列记录了作者在项目过程中由于好奇心驱使而了解到的部分DPDK实现细节。比较适合有同样好奇心的DPDK的初学者,通过本系列文章您可以学习到DPDK的整体工作原理以及部分实现细节您不能学习到应用DPDK进行性能调优如果对DPDK的起源不是很清楚的话,可以先浏览下 绝对干货!初学者也能看懂的DPDK解析,重点就是Linux + x86网络IO瓶颈 这部分,总结一句话就是Linux内核协议栈太慢了,为了突破这种性能瓶颈,DPDK的方案是绕过(bypass)内核,直接从网卡把数据抓到用户空间。一些基本的概念EAL首先必须明白的一点就是,DPDK是以若干个lib的形式提供给应用链接使用,其中最终要的一个lib就是EAL了,EAL的全称是(Environment Abstraction Layer, 环境抽象层),它负责为应用间接访问底层的资源,比如内存空间、线程、设备、定时器等。如果把我们的应用比作一个豪宅的主人的话,EAL就是这个豪宅的管家。lcore & socket这两个概念在 DPDK的代码中随处可见,注意这里的 socket 不是网络编程里面的那一套东西,而是CPU相关的东西。具体的概念可以参看Differences between physical CPU vs logical CPU vs Core vs Thread vs Socket 或者其翻译版本physical CPU vs logical CPU vs Core vs Thread vs Socket(翻译)。对我们来说,只要知道可以DPDK可以运行在多个lcore上就足够了.DPDK 如何知道有多少个lcore呢 ? 在启动时解析文件系统中的特定文件就可以了, 参考函数eal_cpu_detectedDPDK的运行形式大部分DPDK的代码是以lib的形式运行在用户应用的进程上下文.为了达到更高的性能。应用通常都会多进程或者多线程的形式运行在不同的lcore上多线程的场景:多进程的场景:多进程的场景下,多个应用实例如何保证关键信息(比如内存资源)的一致性呢? 答案是不同进程将公共的数据mmap同一个文件,这样任何一个进程对数据的修改都可以影响到其他进程。Primary & Secondary多进程场景下,进程有两种角色Primary或者Secondary,正如其名字,Primary进程可以create 资源,而Secondary进程只能 attach已存在的资源。一山不容二虎,一个多进程的应用,有且只有一个Primary进程,其余都是Secondary进程。应用可以通过命令行参数 –proc-type 来指定应用类型。DPDK的入口如同main函数在应用程序中的地位,rte_eal_init函数便是DPDK梦开始的地方(其实前面的图已经画出来了!),我们来看看它做了什么事。/* Launch threads, called at application init(). */intrte_eal_init(int argc, char *argv){ thread_id = pthread_self(); rte_eal_cpu_init(); eal_parse_args(argc, argv); rte_config_init(); rte_eal_intr_init(); rte_mp_channel_init(); rte_eal_memzone_init(); rte_eal_memory_init(); rte_eal_malloc_heap_init() eal_thread_init_master(rte_config.master_lcore); RTE_LCORE_FOREACH_SLAVE(i) { / create a thread for each lcore / ret = pthread_create(&lcore_config[i].thread_id, NULL, eal_thread_loop, NULL); ….. } / * Launch a dummy function on all slave lcores, so that master lcore * knows they are all ready when this function returns. */ rte_eal_mp_remote_launch(sync_func, NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); ……} 总结起来就是检测lcore解析用户的命令行参数内存资源等子模块初始化在其他lcore上启动线程 ...