解惑

解己之惑,解人之惑

标签:性能 (第2页共2页)

Google面试题解说性能之三:不要小看循环中的任何一个语句

对于任何语言来讲,循环永远是非分布式系统的性能的最大杀手,循环中的任何一个简单的语句对性能都是有影响的,只是影响的大小不同而已。第一个例子中的影响是比较大的,不同的实现方法的时间开销不同,然后这个微小的差异被循环次数放大后就非常的明显(3倍),而第二个例子,其本质是减少了循环执行的次数,虽然总的循环次数是一样的,但是最耗时的操作的执行次数被减少到1/10,所以产生的差异是非常巨大的(8倍)。我们再来看一个很不起眼的微小差异带来的影响:
public class GoogleFn {
private static int MAX = 132000000;

private static int MAX2 = MAX / 10;

private static int count(int n) {
int count = 0;
while (n > 0) {
int mod = n % 10;
if (mod == 1)
count++;
n = n / 10;
}
return count;
}

阅读全文

Google面试题解说性能之二:分析问题

前面我们已经说了字符串运算和数学运算对性能的巨大影响,接下来我们看看分析程序,多思考给我们带来的好处。
如果我们做一个简单的分析就可以知道,在尾数从0到9的连续十个数字中,只有尾数为1的数字的1的个数比其它的数字多,那么我们可以以10个数为单位进行分隔,计算尾数为0的数字包含1的个数,其它的9个值就以此为基础计算:
public class GoogleFn {
private static int MAX = 13200000;

private static int MAX2 = MAX / 10;

private static int count(int n) {
int count = 0;
while (n > 0) {
int mod = n % 10;
if (mod == 1)
count++;
n = n / 10;
}
return count;
}

阅读全文

Google面试题解说性能之一:字符串运算VS数字运算

看到JavaEye上的几个人在讨论算法问题,其中一个就是Google的一个面试题,我也试了一下,而且可能比一般人试得程度更深一些,借这个题目简单的说说几个性能问题。这个是第一个,后面还会继续几个其它的讨论。讨论只是提点一下,主要还是要你自己读源代码并比较不同的实现为什么会有这么大的差别。
注意,程序的运行结果是在JDK1.4.2上的,其它版本的JDK的运行结果可能稍有不同。

先看代码:
public class GoogleFn {
private static int MAX = 13200000;

private static int count1(int number) {
int result = 0;
String target = number + “”;
int index = target.indexOf(“1”);
while (index >= 0) {
result++;
index = target.indexOf(“1”, index + 1);
}
return result;
}

阅读全文

使用分类提高速度

这个是我做这个单元测试框架最大的感受,原来的代码存在两个使用大List的地方,一个是MockEJB自己带的AspectSystemImpl,它把所有的Aspect放在一个List然后查找匹配的,后来我修改了实现,按照不同的Bean放到不同List再用一个Map分类,一举提高20倍速度,然后我又修改了原来Command队列的实现,使用同样的原理,也提高了30%的性能。
所以如果你的代码中也使用到了List之类的集合,添加到里面的东西比较多,要进行比较或者某种查找,那么进行简单的分类可能是提高性能的关键。分类功能简单的使用一个HashMap就可以了。

自主计算的超级计算机

人的大脑真的是自主计算的超级计算机,像前天一样,今天又是半宿没有睡着,半夜四点多醒了,然后就是在床上辗转反侧,到了五点的时候把老婆也吵醒了,不过还是一样,她很困,埋怨我不该把她闹醒,同时也不允许我起床用电脑,因为那样更吵。

这次同样还是性能问题,在成功的把性能提高20倍以后,我的大脑似乎并不满足,他还在寻找可以继续提高性能的地方,当然是有成果的了:

  • 性能的瓶颈在Aspect的查找(这个代码占了全部代码执行时间的一半,这个还是优化后的结果),自己实现了AspectSystem后,其实还可以以代理的方法作为Key把结果缓存起来。
  • 对Entity Bean之间的关系增加惰性初始化,因为其实很多时候代码使用的只是主要的那些属性(就测试用例代码而言),对其他的Entity Bean只是少数时候使用,为了保证每个测试用例之间是无关的,我必须每次都重新初始化,而且这个初始化的一系列的动作其实已经缓存了,但是在大部分情况下,这个关系没有必要初始化(这个从Entity Bean的一对多关系的BUG并没有导致我的测试用例全部失败就可以看出来),而且很多测试用例都是测试一部分的内容,下一个测试另外的部分,所以能够不做的尽量不做,实现也应该比较简单,增加一个BaseGetterAspect就可以了
  • 原来在决定那些方法是Setter的时候是遍历全部的方法,查找以set开头的,只有一个参数,并且类型是Wrapper类(Long、Integer、Boolean等)、String、Date、Collection、以及其他的Entity Bean,但是实际上从EJB的那些配置文件中已经解析到了所有的CMP的字段

大家常常说日有所思,夜有所梦,我做梦都在工作。但是平常做梦我都没有感觉(上大学以后到现在,每天的梦没有一个记得的),而这样的大脑自己高速运算推演把我也搞醒了。然后就是无法入睡,任凭大脑自己在那里写代码。

成功提高20倍性能

这两天其实一直在为性能问题发愁,忙得连日志都没有时间写,前天晚上想到一个解决性能问题的可能方案,结果半宿都没有睡着,脑袋一直在不由自主的想着代码怎么写,但是这个方案只是提高了一倍的性能,今天把代码覆盖的报告又仔细的看了一遍,发现有个方法竟然被调用了两千三百万次,虽然那个代码就是一个直接的return
一个成员变量的语句,但是拿到那个对象后MockEJB会和Aspect进行匹配,需要使用Apache ORO的正则表达式,这个不慢才怪。MockEJB默认使用AspectSystemImpl,它会遍历所有的Aspect,而我的某个测试用例可能会牵涉到20个以上的Entity Bean,每个Entity Bean有三个Aspect,然后调用EJB的每个方法都会这么遍历并且进行匹配,天啊!
没有办法,自己实现了一个AspectSystem,先把和我的工程无关的调用过滤掉,然后再根据调用的方法解析出对应的Entity Bean,从Map里面查找对应的Aspect(只有三个),这样速度一举提升10倍。刚才那个被调用两千三百万次的方法的调用次数也下降到四十万次左右。
还没有完,还是不满意,原来的那些Pointcut是使用的MockEJB自己带的一个实现类:MethodPatternPointcut,我写的那个表达式还是比较复杂的,想到我已经解析出来对应的Entity Bean了,我只关心方法名或者方法前缀就可以了,就自己实现了Pointcut。
这样修改以后性能又提升一倍。

代码如下:
阅读全文

深思熟虑后再开始行动

刚才还在沾沾自喜的说条条大路通罗马,就在我洗碗的时候还在考虑几种方案里面到底哪个更好一些,不曾想就是这个洗碗的时候的并行思考完全推翻了刚才的几种方案。那几种方案的一个最大的问题是它的前提不成立,因为我是为了解决性能问题,而解决性能问题最大的前提是找到性能的瓶颈,性能瓶颈最可能出的地方就是被执行次数最多的代码,我们的框架刚好也使用了代码覆盖工具Cobertura,Cobertura的报告中包含了代码行执行的次数,我需要先分析哪些代码执行的次数比较多,然后再想办法进行优化。
其实这个框架执行了很多重复的操作,我明天的主要工作就是根据代码执行的次数报告来决定缓存哪些结果,特别是程序运行中不可能变化的那些部分。

Java正则表达式的性能问题

在编写EJB-QL的解析器的时候偶然发现的。
假设要解析的EJB-QL的值为:
SELECT OBJECT(al) FROM AppLibraryConfig al WHERE al.id=?1 AND al.status=true

下面看看不同的表达式Pattern所需要的执行时间,解析的伪代码为:
long start=System.currentTimeMillis();
Pattern pattern = Pattern.compile(getPatternText(), Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(query);
if (matcher.matches()) {
        int groupCount = matcher.groupCount();
        for (int i = 0; i <= groupCount; i++)
        {
            System.out.println("Group " + i + " :[" + matcher.group(i) + "]");
        }
}
System.out.println(System.currentTimeMillis()-start);

现在看看不同的getPatternText()的值得到的不同结果。
阅读全文

更新的文章

© 2024 解惑

本主题由Anders Noren提供向上 ↑