ODP/DPDK代码级性能优化总结Tips
以下过程基于ARM 64位CPU, 仅供参考
ODP是Linaro基金下面的开源框架,类似于DPDK。最近用ODP程序DEMO公司SOC性能,性能不理想,优化了一圈又一圈,发现驱动水分很大,包括ODP框架本身。中间不听Architect的建议,自作主张用DPDK+ODP来展示一下我们的多样化驱动, 找到方案,开发中发现DPDK驱动性能也不理想,自己吹的牛,含着泪也要优化完。
前提:
这里主要用64B小包简单反射仪表进来的数据,目的是确认驱动性能最优。进一步读取报文内容会导致加载额外cacheline,性能会下降一些。10G网口小包线速14.88Mpps, 对于2G CPU来说134个时钟周期,平均到每个报文的指令数是关键。
Perf性能检测工具:
如果不能用ubuntu直接安装,比如自己编译的Kernel源码,到tools/perf下make, 生产的perf就是了,复制到usr/bin下面去。
perf list 可以列出你当前cpu支持的性能参数
perf stat -p 'pidof your_app` -e task-clock,...用来检测程序的性能参数
注意-p参数附加到现有进程,可以避免看到程序启动过程的影响。
-e 中要有task-clock这样可以看到更多的%和M/s的统计参数
如果你的cache miss rate大于5%, 输出会有颜色标识
Perf在跟踪cache load miss rate和write miss rate特别有用,没有被cache到的数据访用它能明显看出差别。
高精度时钟:
千万别用系统函数来去时间评估性能,系统开销太大。rte_rdtsc() 是个很好的实现,一条汇编指令。在我的环境下对性能影响微乎其微。用全局变量分别累计batch处理中rx/rd/tx/free时间,每个局部需要统计的代码段的累加时间都除以这个总数,以%显示出来,这样一两个指令的节省都能明显看到%在变化。下面是我用到的几个宏,哪段代码怀疑有问题马上加上去
PERF_VAR xxx; //定义 全局performance counter
PERF_START(); //用在需要统计的代码段开头
PERF_COUNT(xxx); //统计某计数器
PERF_DUMP(xxx, sum); //命令行调用或者exit时打印性能百分比
注意:x86下面貌似RTDSC命令非常耗时,如果对每个包做性能统计时间会非常离谱,建议对批做统计,需要统计的代码段分开。
Eclipse:
保存时自动编译,甚至ssh到设备kill & run,节省很多时间。性能调优需要不停的修改对比,过程自动化能节省很多时间。
Gdb:
ODP/DPDK类用户态框架就是调试方便,这点比bare metal和kernel类程序强很多,有问题马上用"-g -O0"编译调试。
有空用GDB对程序逐步跟踪一下,说不定有惊喜
GIT:
曾经周末在家改了两天code, 一次checkin, 结果因为改了太多关键地方,出错了要调试很久。后来老老实实改一点提交一点,这样哪一步出错很容易追溯。Git的本地分支和提交,应该好好利用起来。
Assert:
当年面试Java, 在白板上写个简单api, 写完之后两个面试官热泪盈眶,我们终于看到会用assert的人" 。底层api编写尽量少用if else检查,上面要判断返回值,整个代码会很难看。底层调用自己给自己用,这些约束在调试时满足就可以,调试期间通过宏使能检查。更要命的是这些额外指令会严重拖累性能,特别是每个包都会调用的函数。运行期间要用宏关闭。
在优化ODP Ring/Pool操作时,很多底层函数因为没有assert, 程序出错只能单步跟踪,费时费力。
编译:
除了-O3还要打开-mtune。有时候perf看到iTLB miss很大,用-Os会明显减小代码尺寸,可以尝试。最后还是要加上-g反汇编看看结果。
Batch:
Batch处理是新网络处理框架的精华之一,也是性能优化的关键。Batch要用在各个方面。比如ring填充,分配一个报文写一个就不如一次分配一批然后批量写进去。虽然Pool里面用了hugepage, 又用了per core的缓存,每单次操作还是要判断并更新指针,最少几个指令。如果一次分批一批,平均到每个报文就一两个而已。
CPU可以缓存数据和指令,D-cache和i-Cache, 一般d-cache可以人为prefetch, i-cache则不行,所以对于几十K的i-cache尽量趁热把它利用好,batch操作完再进入下一段代码。
Pool:
Pool还能优化。比如大家排队打饭,每人递过去一个容器(数组),大师傅给你装进去。养猪的算法更高效,每头猪分配一段空间自己吃去。也就是把内存段的位置返回,不需要再一个个复制到接收数组里面。Pool cache是数组,快用完了再去大池子里分配,用这种返回指针方式更高效,少一次内存复制。
dpdk缺省的pool是ring实现,cons和prod在两个方向上,如果操作频繁相当于要不停遍历整个pool, cache利用不好。如果使用stack pool实现,放进来的马上分配出去会很好的利用缓存。stack设计没有ring的四中组合,只有一种加锁方式。
For循环:
一般常用的For循环对简单的循环体来说效率不高,指令都被浪费在i++和判断上,空间换时间的算法参考:DEQUEUE_PTRS()和rte_memcpy(), ODP pool里面更是一次switch 32个来优化内存复制
Inline和函数指针:
-O3基本上会帮你自动inline,不过最好还是objdump看看汇编,有没有surprise. 函数指针会影响性能,比如dpdk里面的callback, 如果不用建议在.config里面关掉。
Likely/Unlikely:
虽然cpu分支预测已经做得很好,自己预测的分支更准。
Prefetch
记得以前写过一个顺序大内存访问程序,步长小于cacheline的时候性能基本一样,大于cacheline的时候性能严重恶化。这里有三个个有趣因素:同一cacheline访问时间消耗很小,因为数据已经在L1里面。超过一定数量的连续内存访问后,d-cache会通过预测提前加载后序内存,性能很好。超过一定步长预测程序就傻掉。所以要提高性能,一定要减小cache miss! 用Perf经常记录cache miss百分比。Prefetch用的不好,性能不仅没提升,D-cache反而因挤出有用数据,不如prefetch_non_tempral。
内存对齐:
一个struct包含u64, u8, u8,那么这个结构的数组操作会快吗?No, 改成u64, u32, u32更快。
Struct写:
还是上面那个结构数组,写入前两个字段。这也能优化?能:把不用的那个字段写0下去。What,加了一条指令,你有病吗?你有药吗?因为写内存是write-back,cache line(64B)读上来,合并再回写内存,如果全覆盖就不用读了吧。PS, 俺家丫头生病的时候就可以理直气壮的问:这回知道谁有病了吧?我有药哦。。。
寄存器变量:
一些经常读写的内存可以存放到register变量,比L1还快。。
记得ARM有16B寄存器,一直没试试在赋值清零的时候会不会节省指令。
if分支:
能少尽量少,特别是主分支上
mbuf字段顺序:
meta里面有128B, 两个cacheline大小,把收发包常用的字段的集中到前面,只操作一个CL。perf观测到cache r/w miss rate明显下降。
指针:
64位系统里面的8B内存指针真是浪费,因为现在都是hugepage, 很多地址都是连续的,特别是pool里面,所以mbuf可以用idx来替代,而且可以很短。一般网卡的回送数据用来查找对应的mbuf, 短地址就可以减少一次内存查找。
Pool cache size:
这是每个core专有的cache, 访问快,容量不够要去大池访问,所以大小要能容纳常用存取,尽量不要touch大池子。
Rx/tx队列不能贪多,够用就好否则超出Cache能力反而降低性能
少用内存:
特别是per packet的内存,能cache也不好,有些数据可以合并到mbuf里面,或者作为网卡的回送数据。上面提到短索引替代64位指针,如果用在mbuf里面可以节省很多字节。
这里有个很好的文章关于内存访问时间:
CPU与内存的那些事
CPU利用率统计:
有个简单办法,没收到报文时进入一个空转,指令数和普通报文处理差不多。否则性能统计上看RX会占用很多时间。还要统计一下Batch没填满的比例,能大概看出负载状况。
DPDK配置:
这些是gdb单步出来的
如果你的应用没那么复杂可以用spsc, 或者手工调用api
CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_sp_sc"
CONFIG_RTE_MBUF_REFCNT_ATOMIC=n
CONFIG_RTE_PKTMBUF_HEADROOM=0?
CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=n
参考链接(刚找到的,应该早点搜搜看):
http://dpdk.org/doc/guides/prog_guide/writing_efficient_code.html
http://events.linuxfoundation.org/sites/events/files/slides/DPDK-Performance.pdf
https://software.intel.com/en-us/articles/dpdk-performance-optimization-guidelines-white-paper
结果:
经过优化的ODP/DPDK在ARM SOC上达到线速,但是即使只读报文中的一个字节,因为加载了一个CL, 性能还是会降低一些,但是比没优化之前还是提高了很多。X86本身比较彪悍,公司网卡没优化也能跑到线速,看不出差别,后面会找25G双口网卡测试
功耗:
这种PMD模式框架本质上是个死循环,不知道能不能在没收到数据的时候进入WFI/WFE, 等待硬件唤醒
展望:
Intel开源做的很好,代码精炼,越来越多的公司在用DPDK做逐渐复杂的事情,大多数还在网络应用层面。个人觉得应该把这种编程模型和性能压缩思想上升到应用层面,比如数据库、存储、Web、再成熟就进FPGA,最终固化为ASIC,成本应该逐数量级降低。
注意:mtcp+dpdk的应用不应该局限为模拟socket接口,用DPDK里面的批量处理思想,对上层应用包括web server做批量化改造. 刚看到fd.io的TLDK, intel参与的开源udp/tcp框架
DPDK性能优化没有那么神秘,分享一下笔记,欢迎交流经验:steeven@gmail.com 微信号: steeven_li
2016平安夜
12/28补充:X86下的性能结果也出来了,about 50% improvement! 同时也看到x86 cpu性能确实比ARM至少好处两倍以上。
- 大小: 39.5 KB
分享到:
相关推荐
该项目把alibaba LVS-FULLNAT移植到了OpenFastPath(base on odp-dpdk)LVS-FULLNA 源代码:https://github.com/alibaba/LVSOpenFastPath源代码:https://github.com/lvsgate/ofp.git使用:1.获取并编译...
总的来说,《Pro ODP.NET for Oracle Database 11g》这本书详细介绍了如何使用ODP.NET开发高效、可靠的Oracle数据库应用,涵盖了从基础的数据库连接到复杂的PL/SQL调用、事务管理和性能优化等多个方面,对于.NET...
ODP.NET是Oracle公司专为.NET平台设计的数据库访问接口,用于提高Oracle数据库的访问效率和性能。相较于.NET Framework中的其他内置数据提供者(如System.Data.OracleClient、System.Data.Odbc和System.Data.OleDb)...
ODP.NET(Oracle Data Provider for .NET)是Oracle数据库的官方.NET数据提供程序,它提供了全面的数据访问功能,包括连接管理、事务控制、数据类型映射以及性能优化等。ODP.NET Managed Driver是其一部分,它作为一...
AS400应用系统性能优化主要集中在数据库层面,特别是针对基于DB2 UDB AS/400版本的数据库管理系统。本文将深入探讨如何通过优化索引、查询设计和数据库应用程序设计来提升系统的运行效率。 首先,优化索引是提高大...
1. **高性能**:ODP.NET通过利用Oracle数据库的高级特性(如连接合并、分区和并行查询)来优化性能。 2. **类型映射**:提供强大的类型映射支持,确保.NET数据类型与Oracle数据类型的准确转换。 3. **无缝集成**:与...
6. **性能优化**:了解并使用ODP.NET的批处理和池化特性可以显著提高性能。例如,通过批量执行SQL命令减少网络往返,或者利用连接池避免频繁创建和销毁数据库连接。 7. **高级功能**:ODP.NET支持高级特性,如PL/...
8. 性能优化:讨论如何通过ODP优化API性能,如分页、过滤和排序策略。 9. 测试与调试:介绍ODP接口的测试工具和方法,以及如何进行有效的故障排查。 10. 持续进化:探讨ODP在系统演化中的角色,如何适应变化的需求...
总结来说,ODP.NET Managed ODAC12cR4是Oracle与.NET开发者的桥梁,使得C#开发者能够充分利用Oracle数据库的强大功能,同时享受.NET Framework的便利。通过熟练掌握和应用这个组件,开发者可以构建高效、稳定的企业...
虽然ODP.NET是针对.NET环境优化的,但了解OLE DB可以帮助开发者理解数据访问的不同层次和技术。 总结来说,ODP.NET Managed ODAC122cR1是Oracle公司为.NET开发者提供的一种高效、便捷的数据库访问解决方案,尤其...
ODP提供了工具和机制来管理和控制分布式环境,包括服务的生命周期管理、故障恢复和性能优化。 在"chapter2_ODP.ppt"这个文件中,可能包含了ODP模型的深入讲解,如服务的分类、服务间交互的细节、ODP模型与OSI模型的...
ODP.NET提供了丰富的功能,包括事务管理、游标、存储过程、批处理、性能优化等。同时,由于是非托管版本,它不需要完整的Oracle客户端,只需要Instant Client,因此可以在不增加太多系统负担的情况下实现与Oracle...
8. **优化性能**:根据需求,你可以配置ODP.NET连接池,以提高应用的并发性能。 9. **错误处理和异常**:在处理Oracle相关的操作时,注意捕获和处理可能抛出的OracleException,这些异常可能包含关于数据库状态、...
ODP.NET Managed 121020 是Oracle公司为.NET开发者提供的一个数据库连接组件,主要用于在C#等.NET环境中与Oracle数据库进行交互。这个压缩包包含了一系列用于安装、卸载以及查阅信息的文件,下面将详细介绍这些文件...
2. **性能优化**:Oracle优化了Managed ODP.NET的性能,使其在大多数情况下能提供与非托管版本相当的执行速度。 3. **平台兼容性**:支持跨平台.NET框架,包括.NET Framework和.NET Core,使得应用程序能在多种操作...
它是一个完全托管的数据提供者,允许.NET应用程序直接与Oracle数据库进行交互,提供了丰富的特性和优化的性能。相比传统的`Data.OracleClient`组件,ODP.NET在性能、稳定性和功能上都有显著的优势。 1. **安装与...
此外,ODP.NET还提供了连接池功能,通过复用已存在的连接,减少数据库连接的创建和销毁开销,从而提高应用性能。 总的来说,ODP.NET—DLL是.NET开发者与Oracle数据库进行高效、稳定、安全通信的重要工具,它的使用...
ODP.NET支持多种.NET开发工具,包括Visual Studio 2005,使得开发者能够轻松地在.NET环境中开发高性能的企业级应用。 - **连接管理**:ODP.NET支持连接池管理和会话共享,有助于提高应用程序性能。 - **数据操作**...