java — x & gt; -1 против x & gt; = 0, есть ли разница в производительности

Однажды я слышал, как учитель бросил это, и с тех пор меня это беспокоит. Допустим, мы хотим проверить, является ли целое число x больше или равно 0. Есть два способа проверить это:

if (x > -1){
//do stuff
}

а также

if (x >= 0){
//do stuff
}

По словам этого учителя > будет немного быстрее, чем >=, В данном случае это была Java, но, по его словам, это также относится к C, c ++ и другим языкам. Есть ли правда в этом утверждении?

35

Решение

Там нет никакой разницы в любом смысле в реальном мире.

Давайте посмотрим на некоторый код, сгенерированный различными компиляторами для различных целей.

  • Я предполагаю, что подписанная операция int (которая, кажется, намерение OP)
  • Я ограничился опросом C и компиляторами, которые у меня есть под рукой (по общему признанию, довольно маленький образец — GCC, MSVC и IAR)
  • основные оптимизации включены (-O2 для GCC, /Ox для MSVC, -Oh для IAR)
  • используя следующий модуль:

    void my_puts(char const* s);
    
    void cmp_gt(int x)
    {
    if (x > -1) {
    my_puts("non-negative");
    }
    else {
    my_puts("negative");
    }
    }
    
    void cmp_gte(int x)
    {
    if (x >= 0) {
    my_puts("non-negative");
    }
    else {
    my_puts("negative");
    }
    }
    

И вот что каждый из них произвел для операций сравнения:

MSVC 11 для ARM:

// if (x > -1) {...
00000        |cmp_gt| PROC
00000 f1b0 3fff    cmp         r0,#0xFFFFFFFF
00004 dd05         ble         |$LN2@cmp_gt|// if (x >= 0) {...
00024      |cmp_gte| PROC
00024 2800         cmp         r0,#0
00026 db05         blt         |$LN2@cmp_gte|

MSVC 11 для x64:

// if (x > -1) {...
cmp_gt  PROC
00000 83 f9 ff     cmp     ecx, -1
00003 48 8d 0d 00 00                  // speculative load of argument to my_puts()
00 00        lea     rcx, OFFSET FLAT:$SG1359
0000a 7f 07        jg  SHORT $LN5@cmp_gt

// if (x >= 0) {...
cmp_gte PROC
00000 85 c9        test    ecx, ecx
00002 48 8d 0d 00 00                  // speculative load of argument to my_puts()
00 00        lea     rcx, OFFSET FLAT:$SG1367
00009 79 07        jns     SHORT $LN5@cmp_gte

MSVC 11 для x86:

// if (x > -1) {...
_cmp_gt PROC
00000 83 7c 24 04 ff   cmp     DWORD PTR _x$[esp-4], -1
00005 7e 0d        jle     SHORT $LN2@cmp_gt// if (x >= 0) {...
_cmp_gte PROC
00000 83 7c 24 04 00   cmp     DWORD PTR _x$[esp-4], 0
00005 7c 0d        jl  SHORT $LN2@cmp_gte

GCC 4.6.1 для x64

// if (x > -1) {...
cmp_gt:
.seh_endprologue
test    ecx, ecx
js  .L2

// if (x >= 0) {...
cmp_gte:
.seh_endprologue
test    ecx, ecx
js  .L5

GCC 4.6.1 для x86:

// if (x > -1) {...
_cmp_gt:
mov eax, DWORD PTR [esp+4]
test    eax, eax
js  L2

// if (x >= 0) {...
_cmp_gte:
mov edx, DWORD PTR [esp+4]
test    edx, edx
js  L5

GCC 4.4.1 для ARM:

// if (x > -1) {...
cmp_gt:
.fnstart
.LFB0:
cmp r0, #0
blt .L8

// if (x >= 0) {...
cmp_gte:
.fnstart
.LFB1:
cmp r0, #0
blt .L2

IAR 5.20 для ARM Cortex-M3:

// if (x > -1) {...
cmp_gt:
80B5 PUSH     {R7,LR}
.... LDR.N    R1,??DataTable1  ;; `?<Constant "non-negative">`
0028 CMP      R0,#+0
01D4 BMI.N    ??cmp_gt_0

// if (x >= 0) {...
cmp_gte:
80B5 PUSH     {R7,LR}
.... LDR.N    R1,??DataTable1  ;; `?<Constant "non-negative">`
0028 CMP      R0,#+0
01D4 BMI.N    ??cmp_gte_0

Если вы все еще со мной, вот различия любой заметки между оценкой (x > -1) а также (x >= 0) которые появляются:

  • MSVC использует ARM cmp r0,#0xFFFFFFFF за (x > -1) против cmp r0,#0 за (x >= 0), Код операции первой инструкции на два байта длиннее. Я предполагаю, что это может ввести некоторое дополнительное время, поэтому мы назовем это преимуществом для (x >= 0)
  • MSVC для x86 использует cmp ecx, -1 за (x > -1) против test ecx, ecx за (x >= 0), Код операции первой инструкции длиннее на один байт. Я предполагаю, что это может ввести некоторое дополнительное время, поэтому мы назовем это преимуществом для (x >= 0)

Обратите внимание, что GCC и IAR сгенерировали идентичный машинный код для двух видов сравнения (с возможным исключением того, какой регистр использовался). Итак, согласно этому опросу, кажется, что (x >= 0) имеет очень небольшой шанс быть «быстрее». Но какое бы преимущество ни имела минимально короткое байтовое кодирование кода операции (и я подчеркиваю, должно быть) наверняка будут полностью омрачены другими факторами.

Я был бы удивлен, если бы вы нашли что-то другое для сопряженного вывода Java или C #. Я сомневаюсь, что вы найдете какую-либо разницу в примечаниях даже для очень маленькой цели, такой как 8-битный AVR.

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

29

Другие решения

Это очень сильно зависит от базовой архитектуры, но любая разница будет незначительной.

Во всяком случае, я бы ожидал (x >= 0) быть немного быстрее, по сравнению с 0 поставляется бесплатно на некоторых наборах инструкций (таких как ARM).

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

29

Ваш учитель читал несколько действительно старых книг. Раньше такое случалось с некоторыми архитектурами, не имеющими greater than or equal инструкция, которая оценивает > требуется меньше машинных циклов, чем >=Но эти платформы редки в наши дни. Я предлагаю перейти на удобочитаемость и использовать >= 0,

19

Большая проблема здесь преждевременная оптимизация. Многие считают, что писать удобочитаемый код важнее написания эффективное код [1, 2]. Я бы применил эти оптимизации в качестве последнего этапа в низкоуровневой библиотеке, как только будет доказано, что дизайн работает.

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

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

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

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

13

Да, есть разница, вы должны увидеть байт-код.

за

    if (x >= 0) {
}

байт-код

    ILOAD 1
IFLT L1

за

if (x > -1) {
}

байт-код

ILOAD 1
ICONST_M1
IF_ICMPLE L3

Версия 1 быстрее, потому что она использует специальную операцию с нулевым операндом

iflt : jump if less than zero

Но можно увидеть разницу только при запуске JVM в режиме только интерпретации java -Xint ...Например, этот тест

    int n = 0;
for (;;) {
long t0 = System.currentTimeMillis();
int j = 0;
for (int i = 100000000; i >= n; i--) {
j++;
}
System.out.println(System.currentTimeMillis() - t0);
}

показывает 690 мс для n = 0 и 760 мс для n = 1. (я использовал 1 вместо -1, потому что это проще продемонстрировать, идея осталась прежней)

9

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

4

«> =» это одиночная операция, как и «>». Не 2 отдельные операции с OR.

Но> = 0, вероятно, быстрее, потому что компьютеру нужно проверять только один бит (отрицательный знак).

3

Согласно этому учителю> будет немного быстрее, чем> =. В этом
в случае, если это была Java, но, по его словам, это также относится и к C, C ++
и другие языки. Есть ли правда в этом утверждении?

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

Ты можешь читать:
этот или же этот

1
По вопросам рекламы [email protected]