Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6551411
  • 博文数量: 915
  • 博客积分: 17977
  • 博客等级: 上将
  • 技术积分: 8846
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-26 09:59
个人简介

一个好老好老的老程序员了。

文章分类

全部博文(915)

文章存档

2022年(9)

2021年(13)

2020年(10)

2019年(40)

2018年(88)

2017年(130)

2015年(5)

2014年(12)

2013年(41)

2012年(36)

2011年(272)

2010年(1)

2009年(53)

2008年(65)

2007年(47)

2006年(81)

2005年(12)

分类: Java

2012-01-15 21:27:22

最近调研一个数据分析模块中的性能问题,花费将近两周时间。期间做了许多Enhancement,而最后关于总线的性能瓶颈定位和处理值得记录一下。
首先是场景描述,一个典型的生产者消费者环境。总线Bus接收来自不同方向的数据,根据数据的分类,发送给相关订阅者。在数据交互过程中,使用JDK中的阻塞队列ArrayBlockingQueue:生产者的提供Object存入总线中的BlockingQueue;总线经计算判断后将Object存入对应消费者的BlockingQueue。为了不丢失数据,当某个消费者队列满时,总线的put操作会阻塞直到该消费者可用。


性能分析
通过JRat(http://blog.csdn.net/chifengxin/article/details/6750619)、JProbe工具显示,CPU利用率主要消耗在消费者处。耗费大量精力优化消费者,包括增加多线程处理等。当消费者提高到极限后,通过Unix命令发现总线Bus线程居然占满到一个核(top命令后,shift+h查看单核线程列表,找到占用cpu最多的java线程,将该id用计算器转换成16进制。然后执行JDK的jstack得到堆栈信息,在堆栈中查找该16进制线程id对应的线程)。
于是Focus在总线Bus。最后结果是,此处占用CPU的竟然不是普通处理操作,而是在BlockingQueue的put方法。在Queue总是未满的情况,put方法仍然占用大量的资源。
验证:
写了个模拟线程验证,单线程占满一核CPU后,BlockingQueue极限写速度为1500K/s。因而加上其它处理,导致本程序中Bus的性能极限只有300K/s。
解决:
1、 在JDK提供的BlockingQueue中,ArrayBlockingQueue是效率最高和消耗最少的。尝试换用双缓冲队列(参照:),提高很小
2、 考虑到DougLea对锁的优化,放弃重写queue
3、 最终解决方案-Pack包裹
封装了一个简单数组容器继承原Object接口(或者使用其它任意的JDK容器):
public interface IPackObject extends IObject {
       public boolean add(IObject obj);
       public IObject get(int index);
       public int size();
       public boolean full();
}
该容器的构造方法需要传入一个capacity参数作为数组values的初始大小;实现数组写入和读取的方法。使用时,在往BlockingQueue放入对象前,先把对象放入该容器,直到容器满后才将容器put到BlockingQueue,从而避免在put过程中的资源消耗。消费者取到后,加上判断IObject是否是instanceof IPackObject,如果是容器则循环取出对象后再操作。
理论上,当容器容量为100时,在BlockingQueue上的资源消耗将减少99%。
注:需要考虑当容器处于不满状态的超时。最简单的方法是定期(如每秒)将所有未满的容器put到queue。

http://blog.csdn.net/chifengxin/article/details/6968355

阅读(6121) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~