Java循环效率

前端之家收集整理的这篇文章主要介绍了Java循环效率前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我比较 Java中嵌套for,while和do-while循环的效率,我遇到了一些我需要帮助理解的奇怪的结果.
  1. public class Loops {
  2. public static void main(String[] args) {
  3. int L = 100000; // number of iterations per loop
  4. // for loop
  5. double start = System.currentTimeMillis();
  6. long s1 = 0;
  7. for (int i=0; i < L; i++) {
  8. for (int j = 0; j < L; j++) {
  9. s1 += 1;
  10. }
  11. }
  12. double end = System.currentTimeMillis();
  13. String result1 = String.format("for loop: %.5f",(end-start) / 1000);
  14. System.out.println(s1);
  15. System.out.println(result1);
  16.  
  17. // do-while loop
  18. double start1 = System.currentTimeMillis();
  19. int i = 0;
  20. long s2 = 0;
  21. do {
  22. i++;
  23. int j = 0;
  24. do {
  25. s2 += 1;
  26. j++;
  27. } while (j < L);
  28. } while (i < L);
  29. double end1 = System.currentTimeMillis();
  30. String result2 = String.format("do-while: %.5f",(end1-start1) / 1000);
  31. System.out.println(s2);
  32. System.out.println(result2);
  33.  
  34. // while loop
  35. double start2 = System.currentTimeMillis();
  36. i = 0;
  37. long s3 = 0;
  38. while (i < L) {
  39. i++;
  40. int j = 0;
  41. while (j < L) {
  42. s3 += 1;
  43. j++;
  44. }
  45. }
  46. double end2 = System.currentTimeMillis();
  47. String result3 = String.format("while: %.5f",(end2-start2) / 1000);
  48. System.out.println(s3);
  49. System.out.println(result3);
  50. }
  51. }

所有循环各自的计数器达到100亿;结果令我困惑:

for循环:6.48300

do-while:0.41200

而:9.71500

为什么do-while循环要快得多?这种性能差距与L的任何变化并行扩展.我独立运行这些循环,并且执行相同.

解决方法

我已经运行了你提供的代码,也惊讶地发现这些性能差异.由好奇心引导我开始调查,发现确实尽管这些循环似乎在做同样的事情,但是它们之间有一些重要的区别.

第一次运行这些循环后的结果是:

  1. for loop: 1.43100
  2. do-while: 0.51300
  3. while: 1.54500

但是,当我运行这三个循环至少10次时,这些循环的性能几乎相同.

  1. for loop: 0.43200
  2. do-while: 0.46100
  3. while: 0.42900

JIT可以随时间优化这些循环,但是必须有一些不同之处,导致这些循环具有不同的初始性能.其实实际上有两个区别:

> do-while循环的执行次数比for循环和while循环更少

为简单起见,假设L = 1

  1. long s1 = 0;
  2. for (int i=0; i < L; i++) {
  3. for (int j = 0; j < L; j++) {
  4. s1 += 1;

外环:0 < 1
内循环:0 < 1
内环:1 < 1
外环:1 < 1 4次比较

  1. int i = 0;
  2. long s2 = 0;
  3. do {
  4. i++;
  5. int j = 0;
  6. do {
  7. s2 += 1;
  8. j++;
  9. } while (j < L);
  10. } while (i < L);

内环:1 < 1
外环:1 < 1 2次比较
>不同的生成字节码

为了进一步调查,我已经稍稍改变了你的班级,不会影响到班级的工作.

  1. public class Loops {
  2. final static int L = 100000; // number of iterations per loop
  3.  
  4. public static void main(String[] args) {
  5. int round = 10;
  6. while (round-- > 0) {
  7. forLoop();
  8. doWhileLoop();
  9. whileLoop();
  10. }
  11. }
  12.  
  13. private static long whileLoop() {
  14. int i = 0;
  15. long s3 = 0;
  16. while (i++ < L) {
  17. int j = 0;
  18. while (j++ < L) {
  19. s3 += 1;
  20. }
  21. }
  22. return s3;
  23. }
  24.  
  25. private static long doWhileLoop() {
  26. int i = 0;
  27. long s2 = 0;
  28. do {
  29. int j = 0;
  30. do {
  31. s2 += 1;
  32. } while (++j < L);
  33. } while (++i < L);
  34. return s2;
  35. }
  36.  
  37. private static long forLoop() {
  38. long s1 = 0;
  39. for (int i = 0; i < L; i++) {
  40. for (int j = 0; j < L; j++) {
  41. s1 += 1;
  42. }
  43. }
  44. return s1;
  45. }
  46. }

然后编译它并调用javap -c -s -private -l Loop来获取字节码.

首先是doWhileLoop的字节码.

  1. 0: iconst_0 // push the int value 0 onto the stack
  2. 1: istore_1 // store int value into variable 1 (i)
  3. 2: lconst_0 // push the long 0 onto the stack
  4. 3: lstore_2 // store a long value in a local variable 2 (s2)
  5. 4: iconst_0 // push the int value 0 onto the stack
  6. 5: istore 4 // store int value into variable 4 (j)
  7. 7: lload_2 // load a long value from a local variable 2 (i)
  8. 8: lconst_1 // push the long 1 onto the stack
  9. 9: ladd // add two longs
  10. 10: lstore_2 // store a long value in a local variable 2 (i)
  11. 11: iinc 4,1 // increment local variable 4 (j) by signed byte 1
  12. 14: iload 4 // load an int value from a local variable 4 (j)
  13. 16: iload_0 // load an int value from a local variable 0 (L)
  14. 17: if_icmplt 7 // if value1 is less than value2,branch to instruction at 7
  15. 20: iinc 1,1 // increment local variable 1 (i) by signed byte 1
  16. 23: iload_1 // load an int value from a local variable 1 (i)
  17. 24: iload_0 // load an int value from a local variable 0 (L)
  18. 25: if_icmplt 4 // if value1 is less than value2,branch to instruction at 4
  19. 28: lload_2 // load a long value from a local variable 2 (s2)
  20. 29: lreturn // return a long value

现在的whileLooP的字节码:

  1. 0: iconst_0 // push int value 0 onto the stack
  2. 1: istore_1 // store int value into variable 1 (i)
  3. 2: lconst_0 // push the long 0 onto the stack
  4. 3: lstore_2 // store a long value in a local variable 2 (s3)
  5. 4: goto 26
  6. 7: iconst_0 // push the int value 0 onto the stack
  7. 8: istore 4 // store int value into variable 4 (j)
  8. 10: goto 17
  9. 13: lload_2 // load a long value from a local variable 2 (s3)
  10. 14: lconst_1 // push the long 1 onto the stack
  11. 15: ladd // add two longs
  12. 16: lstore_2 // store a long value in a local variable 2 (s3)
  13. 17: iload 4 // load an int value from a local variable 4 (j)
  14. 19: iinc 4,1 // increment local variable 4 (j) by signed byte 1
  15. 22: iload_0 // load an int value from a local variable 0 (L)
  16. 23: if_icmplt 13 // if value1 is less than value2,branch to instruction at 13
  17. 26: iload_1 // load an int value from a local variable 1 (i)
  18. 27: iinc 1,1 // increment local variable 1 by signed byte 1
  19. 30: iload_0 // load an int value from a local variable 0 (L)
  20. 31: if_icmplt 7 // if value1 is less than value2,branch to instruction at 7
  21. 34: lload_2 // load a long value from a local variable 2 (s3)
  22. 35: lreturn // return a long value

为了使输出更加可读,我已经附加了描述每个指令基于‪Java bytecode instruction listings执行的注释.

如果仔细观察,您将看到这两个字节码之间存在重大差异.
while循环(对于for循环也是如此)在字节码末尾定义了if语句(if_icmplt指令).这意味着要检查第一个循环的退出条件,必须调用到第26行的转换,并且类似地,转到第二个循环的第17行.

上述字节码是在Mac OS X上使用javac 1.6.0_45生成的.

概要

我认为不同量的比较加上在while和for循环字节码中存在goto指令是这些循环之间的性能差异的原因.

猜你在找的Java相关文章