VPP tips

VPP tips

1.性能从何而来。

原文链接:

http://www.360doc.com/content/18/0428/20/53742993_749517107.shtml

https://steeven.iteye.com/blog/2347150 DPDK代码级别性能优化总结

https://www.jianshu.com/p/346bf99b2fb1

https://www.jianshu.com/p/ed914b24f6da

https://blog.csdn.net/Dgh19940/article/details/79603843 

架构角度:DPDK的巨页NUMAD-cache优化,VPP I-cache优化

算法角度:Bihash,查表lockless

代码角度:Vector宏构造函数结构体cacheline对齐线程绑核、指令预取、指令优化;

2.路由查找

https://blog.csdn.net/dog250/article/details/6596046

Internet路由之路由表查找算法概述-哈希/LC-Trie/256-way-mtrie

3. __constructor__修饰符

通过一个简单的例子介绍一下gcc__attribute__ ((constructor))属性的作用。gcc允许为函数设置__attribute__ ((constructor))__attribute__ ((destructor))两种属性,顾名思义,就是将被修饰的函数作为构造函数或析构函数。程序员可以通过类似下面的方式为函数设置这些属性:  

void funcBeforeMain() __attribute__ ((constructor));

void funcAfterMain() __attribute__ ((destructor));

4.unlikely

CPU是以流水线的方式执行程序指令。所谓流水线,可以简单理解为在执行一个指令的同时,读取下一条指令。对于程序中大量出现的if else while for ? :等含有条件判断的情景,CPU需要能够正确提取下一条指令以便流水线可以流畅执行下去。一旦提取的是错误分支的指令,虽然不影响程序运行的结果,但整条流水线都会被清空,再重新读入正确分支的指令,对程序运行效率影响颇大。

CPU一般都有硬件分支预测器,但我们也可以用likely()/unlikely()等方式显示指定,另外在设计程序的时候也以使分支判断具有一定的规律性为好,比如一组经过排序的输入数据。

为了最大限度减小Branch mispredication对性能带来的影响,可以将一些常见的分支判断转换为Branchless的形式。

链接:https://www.jianshu.com/p/ed914b24f6da

 

long __builtin_expect(long exp, long c)!!(c)的效果是得到一个布尔值,该函数的作用是更好的分支预测;使用likely() ,执行if后面的语句的机会更大,使用unlikely(),执行else后面的语句的机会更大,原理“It optimizes things by ordering the generated assembly code correctly, to optimize the usage of the processor pipeline. To do so, they arrange the code so that the likeliest branch is executed without performing any jmp instruction (which has the bad effect of flushing the processor pipeline).”

主要是分支预测失误,指令的跳转带来的性能会下降很多。为什么呢?从《深入理解计算机系统》书上摘取,p141:“另一方面,错误预测一个跳转要求处理器丢掉它为该跳转指令后所有指令已经做了的工作,然后再开始用从正确位置处起始的指令去填充流水线,大约会浪费2040个时钟周期;从汇编代码层面理解参考文末第一个引用。

 

链接:https://www.jianshu.com/p/346bf99b2fb1

 

5.巨页hugepage

https://www.cnblogs.com/small-office/p/9766536.html  绑核与巨页

https://www.cnblogs.com/031602523liu/p/10537694.html UIO、巨页、CPU亲和性、NUMA简介

https://blog.csdn.net/qq_33611327/article/details/81738195 mmap分析

系统能否支持大页,支持大页的大小为多少是由其使用的处理器决定的。

大页预留之后,接下来则涉及使用的问题。DPDK使用HUGETLBFS来使用大页。首先,它需要把大页mount到某个路径 比如 /mnt/huge,或者/dev/hugepages/接下来,DPDK运行的时候,会使用mmap()系统调用把大页映射到用户态的虚拟地址空间,然后就可以正常使用了。

由于DPDK是运行在用户空间,而巨页是在内核态的,因此通过mmap实现了用户空间到内核空间的快速访问。

6. cpu亲和性

https://www.cnblogs.com/031602523liu/p/10537694.html

pthread_setaffinity_np 设置线程运行在特定CPU核上

CPU的亲和性也就是cpu affinity机制,指的是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器, 通过处理器关联可以将虚拟处理器映射到一个物理处理器上 ,也就是说把一个程序绑定到一个物理CPU上。

而且在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了。当一个进程或线程绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,减少了cache miss,提高性能和效率。

7. D-cache & I-cache

7.1 I-Cache

VPP按组处理报文,解决I-cache抖动问题。

7.2 D-Cache

原文链接:https://blog.csdn.net/Dgh19940/article/details/79603843 DPDK中的Cache优化

1)写接受描述符到内存,填充数据缓冲区指针,网卡接收到报文后就根据该地址把报文内容填进去。

2)从内存中读取接收描述符(到接收到报文时,网卡会更新该结构)(内存读),从而确认是否收到报文。

3)从接收描述符确认收到报文时,从内存中读取控制结构体的指针,再从内存中读取控制结构体,把从接收描述符中读取的信息填充到该控制结构体(内存读)。

4)更新接收队列寄存器,表示软件接收到了新的报文。

5)从内存读取报文头部(内存读),决定转发端口。

6)从控制结构体把报文信息填入到发送队列发送描述符中,更新发送队列寄存器。

7)从内存中读取发送描述符(内存读),检查是否有包被硬件发送出去。

8)如果有的话,则从内存中读取相应控制结构体(内存读),释放数据缓冲区。

可以看出处理一个报文的过程中,需要6次读取内存(上文(内存读))。

换句话说要保证在80个时钟周期处理完一个报文DPDK就必须保证要读取的数据Cache命中,否则一旦Cache不命中,性能会严重下降。

7.3 Cache一致性

当定义的数据结构或者分配了数据缓冲区之后,内存中就有了一个地址和其相对应,然后程序进行读写。在读的过程中,首先是内存加载到Cache,随后送到处理器内部的寄存器;在写操作的时候则是从寄存器送到Cache,最后由总线回写到内存。

这样会出现两个问题:

1)数据结构/数据缓冲区对应的Cache Line是否对齐?如果不是的话,即使数据区域小于Cache Line的话也会占用两个Cache Line;另外假如上一个CacheLine属于另一个数据结构且被另一个处理器核处理,数据如何同步呢?

答案:结构体对齐__rte_cache_aligned;

Doing this can often make copy operations more efficient, because the compiler can use whatever instructions copy the biggest chunks of memory when performing copies to or from the variables or fields that you have aligned this way.

2)假设数据结构/缓冲区的起始地址是CacheLine对齐的,但是有多个核同时对该内存进行读写,如何解决冲突?

答案:DPDK解决方案很简单,首先避免多个核访问同一个内存地址或者数据结构。每个核尽量避免与其他核共享数据,从而减少因为错误的数据共享导致的Cache一致性开销。

8. Bihash

9. 数据预取

__builtin_prefetch()

https://www.cnblogs.com/dongzhiquan/p/3694858.html

VPP中大量报文处理的数据处理操作

p4 = vlib_get_buffer(vm, from[4]);

vlib_prefetch_buffer_header (p4, LOAD);

CLIB_PREFETCH (p4->data, CLIB_CACHE_LINE_BYTES, STORE);

10.指令优化

猜你喜欢

转载自www.cnblogs.com/sunnypoem/p/11368500.html