Java基准测试-为什么第二个循环更快?
我对此很好奇 我想检查哪个函数更快,所以我创建了一些代码,执行了很多次Java基准测试-为什么第二个循环更快?,java,performance,benchmarking,Java,Performance,Benchmarking,我对此很好奇 我想检查哪个函数更快,所以我创建了一些代码,执行了很多次 public static void main(String[] args) { long ts; String c = "sgfrt34tdfg34"; ts = System.currentTimeMillis(); for (int k = 0; k < 10000000; k++) { c.getBytes();
public static void main(String[] args) {
long ts;
String c = "sgfrt34tdfg34";
ts = System.currentTimeMillis();
for (int k = 0; k < 10000000; k++) {
c.getBytes();
}
System.out.println("t1->" + (System.currentTimeMillis() - ts));
ts = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
Bytes.toBytes(c);
}
System.out.println("t2->" + (System.currentTimeMillis() - ts));
}
publicstaticvoidmain(字符串[]args){
长ts;
字符串c=“sgfrt34tdfg34”;
ts=系统.currentTimeMillis();
对于(int k=0;k<10000000;k++){
c、 getBytes();
}
System.out.println(“t1->”+(System.currentTimeMillis()-ts));
ts=系统.currentTimeMillis();
对于(int i=0;i<10000000;i++){
字节。toBytes(c);
}
System.out.println(“t2->”+(System.currentTimeMillis()-ts));
}
“第二个”循环更快,所以,我认为hadoop中的Bytes类比String类中的函数更快。然后,我改变了循环的顺序,然后c.getBytes()变得更快。我执行了很多次,我的结论是,我不知道为什么,但是在第一个代码执行之后,我的VM中发生了一些事情,所以第二个循环的结果会更快 这是一个典型的java基准问题。Hotspot/JIT/etc将在您使用代码时编译代码,因此在运行过程中会更快
首先围绕循环运行至少3000次(在服务器上或64位上运行10000次),然后进行测量。最有可能的情况是,在第一个循环运行时代码仍在编译或尚未编译 将整个方法包装在一个外部循环中,这样您就可以运行几次基准测试,您应该会看到更稳定的结果
阅读:。可能是这样的情况:您通过调用getBytes()为对象分配了太多空间,JVM垃圾收集器启动并清理未使用的引用(清除垃圾)。您知道有问题,因为
Bytes.toBytes
在内部调用c.getBytes
:
public static byte[] toBytes(String s) {
try {
return s.getBytes(HConstants.UTF8_ENCODING);
} catch (UnsupportedEncodingException e) {
LOG.error("UTF-8 not supported?", e);
return null;
}
}
来源于。这告诉您,调用不可能比直接调用快-最好(即,如果它内联),它将具有相同的时间。但是,通常情况下,由于调用函数的开销很小,您会期望它会稍微慢一点
这是在具有任意时间运行的组件(如垃圾收集器)的已解释、垃圾收集环境中进行微基准测试的典型问题。除此之外,还有一些硬件优化,比如缓存,这会使情况变得更糟。因此,了解正在发生的事情的最佳方法通常是查看源代码
“第二个”循环更快,所以
当您执行一个方法至少10000次时,它将触发编译整个方法。这意味着您的第二个循环可以是
- 速度更快,因为第一次运行时它已经编译好了
- 速度较慢,因为优化后,它没有关于代码执行方式的良好信息/计数器
for(int i=0;i<3;i++){
long-time1=doTest1();//使用System.nanoTime()计时;
longtime2=doTest2();
System.out.printf(“Test1平均占%,Test2平均占%,d平均占%n”,
时间1/次,时间2/次);
}
再观察几次
- 正如上面@dasblinkenlight所指出的,Hadoop的
在内部调用Bytes.toBytes(c)
字符串.getBytes(“UTF-8”)
- 采用字符集作为输入的variant方法
比不采用任何字符集的方法快。因此,对于给定的字符串,String.getBytes()
将比getBytes(“UTF-8”)
快。我已经在我的机器(Windows8、JDK 7)上测试过了。以相等的迭代顺序运行两个循环,一个使用getBytes()
,另一个使用getBytes(“UTF-8”)
getBytes()
即使更改循环的执行顺序,结果也是相同的。为了不考虑任何JIT优化,我建议用不同的方法运行测试以确认这一点(正如上面@Peter Lawrey所建议的)long ts; String c = "sgfrt34tdfg34"; ts = System.currentTimeMillis(); for (int k = 0; k < 10000000; k++) { c.getBytes("UTF-8"); } System.out.println("t1->" + (System.currentTimeMillis() - ts)); ts = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { c.getBytes(); } System.out.println("t2->" + (System.currentTimeMillis() - ts));
- 因此,
应该总是比Bytes.toBytes(c)
String.getBytes()快。
。@guille。我只是遇到了同样的行为,但当我在调试模式下而不是在发行版中启动它时,我注意到了它。您是否在调试中进行了测试?请参阅Aleksey Shipilev的重复问题,以深入分析此类代码的性能。System.nanoTime()
在内部调用Bytes.toBytes()
,但输入为编码的那一个。c.getBytes(“UFT-8”)
sString
比getByte()
因此速度较慢。简而言之getByte()多一点开销(“UTF-8”)
更快。谢谢您的评论,我已经知道了一点。@Santosh您正在寻找的选项是-XX:CompileThreshold从未将其设置为0或1。这是一个方法符合JIT条件后的方法调用数。默认情况下,它是3000,-server选项从Oracle文档中将其覆盖为10000,看起来像是default client值是1500,可能3000在某些新版本中被修改:)同样,当您使用64位JRE时,服务器选项是隐式默认值。这并不能解决第二个循环总是更快的事实,不管其中包含哪些代码。@OrangeDog-Hmmm,这正是我要解决的问题以及如何修复它。我不知道如何使它成为任何c学习者。Bytes.toBytes()
long ts; String c = "sgfrt34tdfg34"; ts = System.currentTimeMillis(); for (int k = 0; k < 10000000; k++) { c.getBytes("UTF-8"); } System.out.println("t1->" + (System.currentTimeMillis() - ts)); ts = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { c.getBytes(); } System.out.println("t2->" + (System.currentTimeMillis() - ts));
t1->1970 t2->2541
- 因此,