0x01 缘由
最近也在尝试写一个DPDK应用程序,也是对之前的学习做一下检验。然而在编写过程中,发现自己陷入到一个码API的思路中,完全没有考虑到DPDK的设计思想,于是放开编程指南,学习下。
0x02 写高效的代码
Intel® 64 and IA-32 指导手册。
1.内存
内存拷贝:建议不要使用libc中的memcpy函数,而用rte_memcpy(),dpdk中做了一些优化。
内存分配:建议使用固定的内存池,减少动态回收内存碎片带来的性能开销,dpdk中的内存池库:librte_mempool、rte_malloc()。
并发访问同一块内存区域:避免多个核并发的访问同一块内存区域,减少Cache misses 应该有如下两种方式避免:
用RTE_PER_LCORE 变量;用一个结构表,每个结构必须Cache对齐;
NUMA系统:尽量访问本地内存,不访问远端内存。DPDK中很多结构中创建函数中都需要制定对应的socket。以空间换时间的思路。
跨存储器通道分配:现代内存控制器有多个内存通道。
2.逻辑核间的通讯(通常是线程):DPDK建议用ring结构。
3.PMD驱动(DPDK轮询模式驱动)
避免局部写。
更低的包延迟:为了获得更高的吞吐量,DPDK尝试用突发的方式合并每个包的处理。
4.锁和原子操作
原子操作意味着在指令之前有一个锁定前缀,导致处理器的LOCK#信号在执行下一条指令时被断言。Read-Copy-Update (RCU) 算法代替rwlocks。
5.代码编写上考量
内联函数(inline):小的函数可以在头文件静态内联函数。
分支预测:intel C/C++编译器(icc/gcc)内建了likely() unlikely()函数。
6.设定CPU类型
DPDK通过配置文件中的CONFIG_RTE_MACHINE选项支持CPU微体系结构特定的优化。优化程度取决于编译器针对特定微架构进行优化的能力,因此,只要有可能,最好使用最新的编译器版本。除了编译器优化之外,一组预处理器定义会自动添加到构建过程中(不管编译器版本如何)。 这些定义对应于目标CPU应该能够支持的指令集。 例如,为任何支持SSE4.2的处理器编译的二进制文件将定义RTE_MACHINE_CPUFLAG_SSE4_2,从而为不同的平台启用编译时代码路径选择。
0x03 性能检测
VTune Performance Analyzer Essentials
0x04 总结
DPDK不仅仅学理论得学解决方案和实践操作。