CBench是一个基于OpenFlow协议的控制器性能测试工具,由于笔者在控制器开发过程需要测试控制器的性能,因而研究了CBench的相关代码,并做了一些开发和优化。下面是笔者使用CBench测试控制器性能及控制器性能调优总结。
1. CBench原理及其使用
1.1 CBench原理
CBench通过伪造交换机(fakeswitch)与控制器握手,每个交换机与控制器建立一个socket连接,使用poll方式实现I/O多路复用,整个程序是一个单线程程序。CBench可以工作在以下两种模式:
Lantency模式
每次发送一个PacketIn消息,并统计回应,不断重复这样的步骤,统计1秒(默认是1秒)内控制器响应的次数。
Throughput模式
每次用PacketIn消息填满消息队列(outbuf,长度是65536),然后将该消息队列的消息由socket发往控制器,并统计回应,重复这样的步骤,统计1秒(默认是1秒)内控制器响应的次数。
需要注意的是,CBench和控制器建立的是TCP连接,在通信带宽不受限的情况下,CBench在单位时间内能够发送的请求的个数,不仅和CBench自身性能有关,还和控制器的处理能力有关。当控制器处理能力受限时,TCP会自动调整接收窗口(rwnd)和拥塞控制窗口(cwnd)的大小,CBench发送请求的速率也会降低。
1.2 CBench的使用
CBench的使用也很简单,下载源码直接编译就可以了。由于这个代码比较老旧,这个project在新版本的操作系统中编译可能会出现问题(亲测在Ubuntu 14.04系统编译可以通过,但在Ubuntu 16.04中编译就有问题)。不过CBench只是这个oflops的一个子模块,因此笔者重构了CBench部分的代码,从而使得CBench可以单独编译,见:重构后的CBench源码和使用, 该版本的代码可以很容易的使用cmake和gcc编译,并用与OpenFlow控制器的测试。另外,由于笔者在控制器开发过程中需要使用其它的南向协议,因而修改了CBench的协议栈相关的代码,见:源码。
2. CBench测试控制器性能
笔者使用的是基于Java开发的控制器,因此Java虚拟机内存的分配对控制器性能的测试影响很大。同时为了避免带宽资源的限制,CBench和控制器都在同一机器上运行。参考:
3. 控制器性能调优
笔者在要完成的是基于ONOS的POF控制器的性能测试。但在测试的过程中发现,基于ONOS的OpenFlow控制器的响应速率可以达到90w/s,而相同环境下基于ONOS的POF控制器的性能只有4w/s。于是笔者尝试使用Java程序的性能分析工具VisuaVM来分析程序的性能,关于VisualVM的使用,参考:使用 VisualVM 进行性能分析及调优。
下面是使用VisualVM观测到在测试过程中CPU和内存的使用情况:
从图中可以发现,限制控制器性能的主要还是CPU资源,于是进一步使用VisualVM测试了关键方法的耗时:
可以发现,大量CPU资源不是消耗在了与io相关的操作,而是消耗在了与io无关的object.toString方法。检查程序发现在与程序io相关的decode方法中使用了log.debug方法,而该方法虽然不会在正常模式下输出日志,但是该方法及其内部字符串拼接操作都会执行,造成大量的CPU资源浪费。下面一段简单的验证代码:
1 | int x = 0; |
以上日志的输出为:
1 | test for inf, x: 0 |
这个例子说明log.debug中的test函数也执行了,只是日志没有输出。这也提示我们使用log日志时一定要谨慎,避免使用没有必要日志输出!!!
下面是去掉debug相关的日志方法后的关键方法耗时分析:
可以发现,现在CPU主要用于io相关的操作了,而测试结果也从4w/s上升到了130w/s。:)
另外,笔者也发现当请求或响应数据包的长度增加时,测试过程中控制器的Java虚拟机GC活动也会增加,造成测试的控制器性能下降,这在控制器测试或性能调优的过程中也是需要注意的。
4. 总结
本文分析了CBench工作原理,介绍了CBench使用方法和一些需要注意的问题。另外,本文也从控制器测试的实战经验出发,介绍了Java程序的性能分析和调优方法,以飨读者。