这问题, 仅仅是特定业务场景下的。 希望别纠缠。
今天看到, 某个string转integer的代码性能非常有问题。 这个代码被极大数量的使用。 代码如下:
[pre] public Object convert(Object source) { if (source == null) { return new Integer(0); } String strValue = (String) source; try { return new Integer(strValue); } catch (Exception e) { try { return new Integer(strValue.trim()); } catch (Exception ex) { return new Integer(0); } } }[/pre]刚看上去, 我觉得比较正常, 因为我知道业务数据99%的情况下, 不会有非法的情况。 我测试下1000W次转换的性能:
conv.convert("1234") 550ms
conv.convert(" 1234 ") 26846ms
conv.convert(" ") 51013ms
居然相差了快50
倍的性能。 这个代码仔细看了下, 如果source=" "; source=" 1234 "
这样的情况下, 性能变的特别恶劣, 特别是"", " "
的情况下, 几乎是100
倍的性能差异。 这让我意识到, Java形成异常的过程是非常消耗cpu/mem
的。 我改写了这个代码,做了一下几点改进:
1. "", " " 处理过程中不再产生异常。
2. 测试了下String.trim()性能, 可以直接使用。
3. 居然 new Integer(String s)比 Integer.valueOf(String s) 性能高一点点
[pre] static Integer ZERO_INTEGER = Integer.valueOf(0); public Object convert(Object source) { String strValue = (String) source; if (strValue == null) { return ZERO_INTEGER; } strValue = strValue.trim(); if (strValue.length() == 0) { return ZERO_INTEGER; } Integer ret = null; try { ret = new Integer(strValue); } catch (NumberFormatException e) { ret = ZERO_INTEGER; } return ret; }[/pre]我测试下了1000W次性能:
conv.convert("1234") 660ms
conv.convert(" 1234 ") 860 ms
conv.convert(" ") 260ms
虽然, 我们调整了代码, 但是, 正常情况代码的性能是下降了。 这是没有任何办法了。 当然, 为了避免正常情况下trim()的操作可以加上,这要根据生产具体情况了:
[pre] if (strValue.charAt(0) == ' ' || strValue.charAt(strValue.length() - 1) == ' '){ strValue = strValue.trim(); }[/pre]事情应该到此了, 但是,我还是很怀疑, 这个变化怎么能在生产环境中, 产生如此大的影响,以现在CPU这点性能变化,应该不至于非常致命。应该不同的栈深度的, 造成了更大的性能障碍。我测试了下:
[pre]public Object convert2(Object source) { if (source == null) { return new Integer(0); } String strValue = (String) source; try { return new Integer(strValue); } catch (Exception e) { try { return new Integer(strValue.trim()); } catch (Exception ex) { return new Integer(0); } } }//使用递归构造栈深度public void rescue(String source, int depth) { if (depth > 0) { rescue(source, --depth); } else { convert2(source); } }[/pre]100万次测试的结果如下:
conv.rescue(" 1234", 0) 2779ms
conv.rescue(" 1234", 1) 3001ms
conv.rescue(" 1234", 5) 3258ms
conv.rescue(" 1234", 10) 3751ms
conv.rescue(" 1234", 50); 7611ms
很明显栈的程度,对于异常的构造, 还是需要不少时间的。
现在我们可以获得几个结论:
1. 在一个框架级应用上, 尽量检查数据后, 再进行类型转换, 尽量避免使用"无谓"的异常, 否则,在高度覆盖的代码上, 可能造成严重的性能问题。特别使用特定的异常进行条件跳转。
2. 尽量少在系统底层抛出异常。 否则在异常栈的构造随着深度, 消耗的时间增加。
这个问题,造成生产系统速度下降了100%+。 别怀疑, 这是非常特殊的应用。 大量的解析xml数据, 然后形成JavaBean对象。 大约有60%的类型是string=>integer转化。 另外声讨下xml, 下一节, 我们会讲述一个恐怖的xml性能故障点, 大部分解析器都存在的问题。
PS: 我刚想了下, 好像我这么测试栈深度是不对的。 会不会是这个条件引起的。 谁有办法测试?
[pre] if (depth > 0) { rescue(source, --depth); }[/pre]
[/td][/tr][/table] |
|