2010-11-15 3 views
3

Все,Счетчик циклов в Java API

При переходе через некоторые файлы в Java API, я заметил много случаев, когда цикл встречного уменьшенных, а не приращение. то есть в for и while петлях в классе String. Хотя это может быть тривиально, есть ли какое-либо значение для уменьшения счетчика, а не увеличения?

+1

Я посмотрел на http://www.docjar.com/html/api/java/lang/String.java.html и видел только декрации, используемые в lastIndexOf, что имеет смысл. Можете ли вы указать нам на источник, на который вы смотрите? – Synesso

+0

Это странно. Я смотрю исходный код JDK 1.6, и метод equals() отличается от ссылки, о которой вы упоминали. Я не могу найти эти строки в указанной вами ссылке. –

+0

@Synesso @ darkie15: Это похоже на версию Apache Harmony «String». – ColinD

ответ

7

Я скомпилировал две простые петли с eclipse 3.6 (java 6) и посмотрел на байтовый код, есть ли у нас некоторые отличия. Вот код:

for(int i = 2; i >= 0; i--){} 
for(int i = 0; i <= 2; i++){} 

И это байткод:

// 1st for loop - decrement 2 -> 0 
0 iconst_2 
1 istore_1  // i:=2 
2 goto 8 
5 inc 1 -1  // i+=(-1) 
8 iload_1 
9 ifge 5  // if (i >= 0) goto 5 

// 2nd for loop - increment 0 -> 2 
12 iconst_0 
13 istore_1  // i:=0 
14 goto 20 
17 inc 1 1  // i+=1 
20 iload_1 
21 iconst 2 
22 if_icmple 17 // if (i <= 2) goto 17 

Операция/декремент приращения не должна иметь никакого значения, это либо +1 или +(-1). Основное отличие в этом типичном (!) Примере заключается в том, что в первом примере мы сравниваем с номером (ifge i), во втором - по сравнению со значением (if_icmple i 2). И компиляция делается на каждой итерации. Таким образом, , если у есть какой-либо (небольшой) прирост производительности, я думаю, это потому, что сравнивать его с 0, а затем сравнивать с другими значениями. Таким образом, я думаю, что это не увеличение/уменьшение, что делает разницу, но критерии остановки.

Так что, если вы в необходимости сделать некоторые микро-оптимизацию на уровне исходного кода, попробуйте написать свои петли таким образом, чтобы вы сравнить с нулем, в противном случае оставьте его в читаемом как можно (и инкрементации является гораздо проще понять):

for (int i = 0; i <= 2; i++) {} // readable 
for (int i = -2; i <= 0; i++) {} // micro-optimized and "faster" (hopefully) 

Добавление

Вчера я сделал очень простой тест - только что создал массив 2000x2000 и заселен клетку, основанную на расчетах с клеточным отром ices, после подсчета от 0->1999 для обеих строк и ячеек, в другое время назад от 1999->0. Я не был удивлен, что оба сценария имели аналогичную производительность (185..210 мс на моей машине).

Так да, есть разница в уровне байтового кода (eclipse 3.6), но, эй, мы сейчас в 2010 году, похоже, сейчас это не имеет существенного отличия. Итак, опять же, используя слова Стивенса, «не тратьте свое время» на такую ​​оптимизацию. Держите код читаемым и понятным.

+0

Ummm ...смотреть на байт-код не особо поучительно. Вам действительно нужно сделать тщательный анализ собственного кода, испускаемого компилятором JIT. –

+0

@ Stephen C - Я не согласен, что это не поучительно, это дает нам верхнюю границу любой дельта производительности. Предполагая, что мы знали тайминги 'iconst',' ifge' и 'if_icmple' и что JIT не собирается делать что-либо более медленным *, можно сказать, что одна версия будет меньше X% медленнее, чем другая , – CurtainDog

+0

Прохладный! Я даже не думал об этой проблеме в этом аспекте. Спасибо. – AlexR

2

В случае сомнения, контрольный показатель.

public class IncDecTest 
{ 
    public static void main(String[] av) 
    { 
     long up = 0; 
     long down = 0; 
     long upStart, upStop; 
     long downStart, downStop; 
     long upStart2, upStop2; 
     long downStart2, downStop2; 

     upStart = System.currentTimeMillis(); 
     for(long i = 0; i < 100000000; i++) 
     { 
      up++; 
     } 
     upStop = System.currentTimeMillis(); 

     downStart = System.currentTimeMillis(); 
     for(long j = 100000000; j > 0; j--) 
     { 
      down++; 
     } 
     downStop = System.currentTimeMillis(); 

     upStart2 = System.currentTimeMillis(); 
     for(long k = 0; k < 100000000; k++) 
     { 
      up++; 
     } 
     upStop2 = System.currentTimeMillis(); 

     downStart2 = System.currentTimeMillis(); 
     for(long l = 100000000; l > 0; l--) 
     { 
      down++; 
     } 
     downStop2 = System.currentTimeMillis(); 

     assert (up == down); 

     System.out.println("Up: " + (upStop - upStart)); 
     System.out.println("Down: " + (downStop - downStart));  
     System.out.println("Up2: " + (upStop2 - upStart2)); 
     System.out.println("Down2: " + (downStop2 - downStart2)); 
    } 
} 

Со следующим JVM:

java version "1.6.0_22" 
Java(TM) SE Runtime Environment (build 1.6.0_22-b04-307-10M3261) 
Java HotSpot(TM) 64-Bit Server VM (build 17.1-b03-307, mixed mode) 

имеет следующий вывод (RAN это несколько раз, чтобы убедиться, что виртуальная машина была загружена и убедиться, что номера успокоились немного).

$ java -ea IncDecTest 
Up: 86 
Down: 84 
Up2: 83 
Down2: 84 

Они все идут очень близко друг к другу, и у меня есть ощущение, что любое несоответствие является виной виртуальная машина загружается код в некоторых точках, а не другие, или фоновая задача происходит, или просто упасть и округляется на миллисекундной границе.

Хотя в какой-то момент (первые дни Java), возможно, было какое-то исполнение voodoo, мне кажется, что это уже не так.

Не стесняйтесь, попробуйте запустить/изменить код, чтобы увидеть сам.

+2

Хотя я не думаю, что реальность была бы намного иной, чем это, помните, что это не очень хорошая микробиблиотека ... начиная с того, что она использует 'currentTimeMillis()', а не 'nanoTime()' для (прочитайте Javadoc для этих методов). Также взгляните на [this] (http://code.google.com/p/caliper/wiki/JavaMicrobenchmarks). – ColinD

2

Возможно, это связано с тем, что инженеры Sun делают много профилирования и микро-оптимизации, и те примеры, которые вы нашли, являются результатом этого. Также возможно, что они являются результатом того, что инженеры Sun «оптимизируют», основываясь на глубоком знании компиляторов JIT ... или на основе неглубокого/неправильного знания/вуду-мышления.

Это не исключено, что эти последовательности:

  • быстрее, чем петли приращения,
  • не быстрее или медленнее, чем петли приращения или
  • медленнее, чем прибавка петель для последних виртуальных машин, и код больше не является оптимальным.

В любом случае, вы не должны подражать эту практику в вашем коде, если тщательное профилирование с последней JVM, демонстрирует, что:

  • вашего код действительно выиграет от оптимизации, и
  • декрементный цикл действительно быстрее, чем инкрементный цикл для вашего конкретного приложения.

И даже тогда, вы может обнаружить, что ваш тщательно ручной оптимизированный код меньше оптимальной на других платформах ... и что вам нужно повторить процесс снова и снова.

В наши дни общепризнано, что лучшей первой стратегией является написать простой код и оставить оптимизацию JIT-компилятору. Написание сложного кода (например, циклов, выполняющихся в обратном порядке) может фактически сорвать попытки компилятора JIT оптимизировать.

Смежные вопросы