x> -1 vs x>= 0, hay una diferencia de rendimiento


He oído a un maestro caer esta vez, y me ha estado molestando desde entonces. Digamos que queremos comprobar si el entero x es mayor o igual a 0. Hay dos maneras de comprobar esto:

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

Y

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

Según este maestro > sería un poco más rápido que >=. En este caso era Java, pero según él esto también se aplicaba para C, c++ y otros lenguajes. ¿Hay algo de verdad en esta declaración?

Author: hippietrail, 2013-01-25

10 answers

No hay diferencia en ningún sentido del mundo real.

Echemos un vistazo a algún código generado por varios compiladores para varios destinos.

  • Estoy asumiendo una operación int firmada (que parece la intención de la OP)
  • He limitado por encuesta a C y a compiladores que tengo fácilmente a mano (es cierto que una muestra bastante pequeña - GCC, MSVC e IAR)
  • optimizaciones básicas habilitadas (-O2 para GCC, /Ox para MSVC, -Oh para IAR)
  • Utilizando el siguiente módulo:

    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");
        }
    }
    

Y esto es lo que cada uno de ellos produjo para las operaciones de comparación:

MSVC 11 targeting 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 targeting 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 targeting 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 targeting 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 targeting 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 targeting 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 apuntando a un BRAZO 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

Si todavía estás conmigo, aquí están los diferencias de cualquier nota entre evaluar (x > -1) y (x >= 0) que aparecen:

  • MSVC targeting ARM uses cmp r0,#0xFFFFFFFF for (x > -1) vs cmp r0,#0 for (x >= 0). El opcode de la primera instrucción es dos bytes más largo. Supongo que puede introducir algún tiempo adicional, por lo que llamaremos a esto una ventaja para (x >= 0)
  • MSVC targeting x86 utiliza cmp ecx, -1 para (x > -1) vs test ecx, ecx para (x >= 0). El opcode de la primera instrucción es un byte más largo. Supongo que eso puede introducir algo de tiempo adicional, así que llame a esto una ventaja para (x >= 0)

Tenga en cuenta que GCC e IAR generaron código máquina idéntico para los dos tipos de comparación (con la posible excepción de cuyo registro se utilizó). Así que de acuerdo con esta encuesta, parece que (x >= 0) tiene una probabilidad muy pequeña de ser "más rápido". Pero cualquiera que sea la ventaja que la codificación de bytes de opcode mínimamente más corta pueda tener (y subrayo podría tener) será ciertamente completamente eclipsada por otros factores.

Estaría sorprendido si encontró algo diferente para la salida jitted de Java o C#. Dudo que encuentre alguna diferencia de nota incluso para un objetivo muy pequeño como un AVR de 8 bits.

En resumen, no te preocupes por esta microoptimización. Creo que mi escritura aquí arriba ya ha pasado más tiempo del que se gastará por cualquier diferencia en el rendimiento de estas expresiones acumuladas a través de todas las CPU ejecutándolas en mi vida. Si tiene la capacidad de medir la diferencia en el rendimiento, por favor, aplica tus esfuerzos a algo más importante como estudiar el comportamiento de las partículas subatómicas o algo así.

 28
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-25 21:03:28

Depende mucho de la arquitectura subyacente, pero cualquier diferencia será minúscula.

En todo caso, esperaría que (x >= 0) fuera un poco más rápido, ya que la comparación con 0 viene gratis en algunos conjuntos de instrucciones (como ARM).

Por supuesto, cualquier compilador sensible elegirá la mejor implementación independientemente de qué variante esté en su fuente.

 29
Author: Graham Borland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-25 11:45:54

Su maestro ha estado leyendo algunos libros muy antiguos. Solía ser el caso con algunas arquitecturas que carecían de la instrucción greater than or equal de que evaluar > requería menos ciclos de máquina que >=, pero estas plataformas son raras en estos días. Sugiero ir para la legibilidad, y el uso de >= 0.

 19
Author: dasblinkenlight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-25 11:29:41

Una preocupación mayor aquí es optimización prematura. Muchos consideran que escribir código legible es más importante que escribir código eficiente[1, 2]. Aplicaría estas optimizaciones como una última etapa en una biblioteca de bajo nivel una vez que se haya demostrado que el diseño funciona.

No deberías estar considerando constantemente hacer optimizaciones minúsculas en tu código a costa de la legibilidad, ya que hará que leer y mantener el código sea más difícil. Si estos las optimizaciones deben tener lugar, abstraerlas en funciones de nivel inferior para que aún quede código que sea más fácil de leer para los humanos.

Como un ejemplo loco, considere a alguien que escribe sus programas en ensamblado a alguien que está dispuesto a renunciar a esa eficiencia adicional y usar Java por sus beneficios en diseño, facilidad de uso y capacidad de mantenimiento.

Como nota al margen, si está utilizando C, tal vez escribir una macro que utiliza el código ligeramente más eficiente es una más factible solución, ya que logrará eficiencia, legibilidad y mantenibilidad más que operaciones dispersas.

Y, por supuesto, las compensaciones de eficiencia y legibilidad dependen de su aplicación. Si ese bucle se ejecuta 10000 veces por segundo, entonces es un posible cuello de botella y es posible que desee invertir tiempo en optimizarlo, pero si se trata de una sola declaración que se llama de vez en cuando, probablemente no vale la pena por la ganancia de minutos.

 13
Author: Aram Kocharyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-12 07:31:17

Sí, hay una diferencia, deberías ver el bytecode.

Para

    if (x >= 0) {
    }

El bytecode es

    ILOAD 1
    IFLT L1

Para

if (x > -1) {
}

El bytecode es

ILOAD 1
ICONST_M1
IF_ICMPLE L3

La versión 1 es más rápida, porque utiliza una operación especial de operando cero

iflt : jump if less than zero 

Pero es posible ver la diferencia solo ejecutando JVM en modo de solo interpretación java -Xint ..., por ejemplo, esta prueba

    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);
    }

Muestra 690 ms para n = 0 y 760 ms para n = 1. (Usé 1 en lugar de -1 porque es más fácil demostrar, la idea permanece igual)

 9
Author: Evgeniy Dorofeev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-26 05:43:57

De hecho, creo que la segunda versión debería ser un poco más rápida, ya que requiere una verificación de un solo bit(suponiendo que compare a cero como muestra arriba). Sin embargo, tales optimizaciones nunca se muestran realmente, ya que la mayoría de los compiladores optimizarán dichas llamadas.

 4
Author: Ivaylo Strandjev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-25 11:27:26

">=" es una operación única, al igual que ">". No 2 operaciones separadas con OR.

Pero >=0 es probablemente más rápido, porque la computadora necesita verificar solo un bit (signo negativo).

 3
Author: bigGuy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-25 11:35:15

Según este maestro > sería un poco más rápido que>=. En este caso era Java, pero según él esto también se aplica para C, c++ y otros idiomas. ¿Hay algo de verdad en esta declaración?

Tu maestro está fundamentalmente equivocado. No solo por qué el azar es que la comparación con 0 puede ser sligly rápido, sino porque este tipo de optimización local son bien hecho por su compilador / intérprete, y se puede ensuciar todo tratando de ayudar. Definitivamente no es una buena cosa para enseñar.

Puedes leer: this or this

 1
Author: qPCR4vir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:32:24

Siento irrumpir en esta conversación sobre el rendimiento.

Antes de hacer una digresión, observemos que la JVM tiene instrucciones especiales para manejar no solo cero, sino también constantes del uno al tres. Dicho esto, es probable que la capacidad de la arquitectura para manejar cero se pierda mucho tiempo detrás de más de la optimización del compilador, sino también bytecode a la traducción de código de máquina y tal.

Recuerdo de mis días de lenguaje ensamblador x86 que había instrucciones en el conjunto tanto para mayor que (ja) y mayor que o igual a (jae). Usted haría uno de estos:

; x >= 0
mov ax, [x]
mov bx, 0
cmp ax, bx
jae above

; x > -1
mov ax, [x]
mov bx, -1
cmp ax, bx
ja  above

Estas alternativas toman la misma cantidad de tiempo, porque las instrucciones son idénticas o similares, y consumen un número predecible de ciclos de reloj. Véase, por ejemplo, este. ja y jae pueden verificar un número diferente de registros aritméticos, pero esa verificación está dominada por la necesidad de que la instrucción tome un tiempo predecible. Esto a su vez es necesario para mantener la arquitectura de la CPU manejable.

Pero vine aquí a divagar.

Las respuestas ante mí tienden a ser pertinentes, y también indicativas de que vas a estar en el mismo estadio en lo que respecta al rendimiento, independientemente del enfoque que elijas.

Lo que te deja con la elección basada en otros criterios. Y aquí es donde quería hacer una nota. Al probar índices, prefiera la comprobación de estilo ajustado, principalmente x >= lowerBound, a la x > lowerBound - 1. El argumento está destinado a ser inventado, pero se reduce a la legibilidad, ya que aquí todo lo demás es verdaderamente igual.

Ya que conceptualmente estás probando contra un límite inferior, x >= lowerBound es la prueba canónica que provoca la cognición más adaptada de los lectores de tu código. x + 10 > lowerBound + 9, x - lowerBound >= 0, y x > -1 son todas formas indirectas de probar contra un límite inferior.

Nuevamente, lamento irrumpir, pero sentí que esto era importante más allá de lo académico de las cosas. Siempre pienso en estos términos y deje que el compilador se preocupe por las optimizaciones de minutos que cree que puede obtener de juguetear con las constantes y el rigor de los operadores.

 1
Author: Mihai Danila,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-25 20:31:53

En primer lugar, depende en gran medida de la plataforma de hardware. Para PCS modernos y SOC ARM diferencia se basan principalmente en optimizaciones del compilador. Pero para las CPU sin FPU, las matemáticas firmadas serían un desastre.

Por ejemplo, las CPU simples de 8 bits como Intel 8008, 8048,8051, Zilog Z80, Motorola 6800 o incluso los microcontroladores RISC PIC o Atmel modernos hacen todas las matemáticas a través de ALU con registros de 8 bits y básicamente solo llevan bits de bandera y bits de bandera z (indicador de valor cero). Todas las matemáticas serias se hacen a través de bibliotecas y expresiones

  BYTE x;
  if (x >= 0) 

Definitivamente ganaría, usando instrucciones JZ o JNZ asm sin llamadas a la biblioteca muy costosas.

 0
Author: Lyubomyr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-25 23:19:31