Является ли ++ я действительно быстрее, чем я ++ в циклах for в Java? - PullRequest
31 голосов
/ 28 января 2011

В Java я обычно делаю цикл for, как показано ниже:

for (int i = 0; i < max; i++) {
   something
}

Но недавно коллега напечатал это так:

for (int i = 0; i < max; ++i) {
   something
}

Он сказал, что последний будет быстрее.Это правда?

Ответы [ 11 ]

56 голосов
/ 28 января 2011

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

Миф возник из C, где ++i считалось быстрее i++ потому что первое можно реализовать, увеличивая i, затем возвращая его.Последнее может быть реализовано путем копирования значения i во временную переменную, увеличивая i, а затем возвращая временную переменную.Первая версия не должна делать временную копию, и многие считают, что это быстрее.Однако, если выражение используется в качестве выражения, современные компиляторы C могут оптимизировать временную копию, чтобы на практике не было никакой разницы.

36 голосов
/ 30 сентября 2013

Этот вопрос нуждался в некотором байт-коде Java. Рассмотрим следующий код:

public class PostPre {
    public static void main(String args[]) {
        int n = 5;
        loop1(n);
        loop2(n);
    }

    public static void loop1(int n) {
        for (int i = 0; i < n; i++) {}
    }

    public static void loop2(int n) {
        for (int i = 0; i < n; ++i) {}
    }
}

Теперь скомпилируйте и разберите:

$ javac PostPre.java; javap -c PostPre.class 
Compiled from "PostPre.java"
public class PostPre {
  public PostPre();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5      
       1: istore_1      
       2: iload_1       
       3: invokestatic  #2                  // Method loop1:(I)V
       6: iload_1       
       7: invokestatic  #3                  // Method loop2:(I)V
      10: return        

  public static void loop1(int);
    Code:
       0: iconst_0      
       1: istore_1      
       2: iload_1       
       3: iload_0       
       4: if_icmpge     13
       7: iinc          1, 1
      10: goto          2
      13: return        

  public static void loop2(int);
    Code:
       0: iconst_0      
       1: istore_1      
       2: iload_1       
       3: iload_0       
       4: if_icmpge     13
       7: iinc          1, 1
      10: goto          2
      13: return        
}

loop1() и loop2() имеют одинаковый байт-код.

9 голосов
/ 28 января 2011

Для любого разумно способного оптимизатора они будут точно такими же. Если вы не уверены, посмотрите на выходной байт-код или профилируйте его.

5 голосов
/ 28 января 2011

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

2 голосов
/ 23 февраля 2015

Попробуйте это в вашей среде

public class IsOptmized {
    public static void main(String[] args) {

        long foo; //make sure the value of i is used inside the loop
        long now = 0; 
        long prefix = 0;
        long postfix = 0;

        for (;;) {
            foo = 0;
            now = System.currentTimeMillis();
            for (int i = 0; i < 1000000000; i++) {
                foo += i;
            }
            postfix = System.currentTimeMillis() - now;

            foo = 0;
            now = System.currentTimeMillis();
            for (int i = 0; i < 1000000000; ++i) {
                foo += i;
            }
            prefix = System.currentTimeMillis() - now;

            System.out.println("i++ " + postfix + " ++i " + prefix + " foo " + foo);
        }
    }
}

Шахта дает мне

i++ 1690 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1600 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1611 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1600 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1692 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000

Так что, даже если это не так уж и много, я полагаю, что есть разница

1 голос
/ 05 апреля 2013

Декомпилируйте с помощью «javap -c YourClassName», посмотрите результат и примите решение.Таким образом, вы видите, что на самом деле делает компилятор в каждом случае, а не то, что вы думаете.Таким образом, вы также видите, ПОЧЕМУ один путь быстрее другого.

1 голос
/ 31 января 2011

Нет, никакой разницы не будет.

Это пришло из C ++, но даже в этом случае вообще не будет никакой разницы.Где есть разница, где я объект.i ++ должен был бы сделать дополнительную копию объекта, поскольку он должен возвращать исходное неизмененное значение элемента, тогда как ++ я могу вернуть измененный объект, поэтому сохраняет копию.стоимость копии может быть значительной, поэтому ее стоит запомнить.И из-за этого люди склонны использовать его и для переменных int, так как в любом случае это так же хорошо ...

1 голос
/ 28 января 2011

Это не будет быстрее.Компилятор и JVM с JIT сделают фарш из таких незначительных различий.

Вы можете использовать обычные методы оптимизации цикла, чтобы получить преимущества в скорости, такие как развертывание, если применимо.

0 голосов
/ 10 августа 2015

В Java такой разницы нет. Java-машина взаимодействует с кодом, и независимо от того, пишете ли вы ++ i или i ++, он будет преобразован в байтовый код в точно такой же набор инструкций.

Но в C / C ++ есть огромная разница, и если вы не используете какие-либо флаги оптимизации, то ваш цикл может быть медленнее до 3 раз.

Использование флагов оптимизации, таких как -O / -O3, заставит компилятор сделать код сборки вывода проще (в большинстве случаев) и, следовательно, быстрее (в большинстве случаев).

0 голосов
/ 28 января 2011

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

for (int i = 0; i < max; i += 1) {
   something
}

* протестировано с помощью компилятора Eclipse

...