Подтвердить что ты не робот

Является ли ++ я быстрее, чем я ++ для for-loops в java?

В java я обычно делаю цикл for следующим образом:

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

Но недавно коллега набрал его так:

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Для этого вопроса нужен некоторый код байта 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() имеют одинаковый байтовый код.

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

Нет никакой разницы.

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

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

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

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

Ответ 11

Даже это было бы быстрее, никто не заботится в дни HotSpot. Первое, что делает JIT, - это удалить все оптимизации, созданные javac. После этого все остается в JIT, чтобы сделать это быстро.