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?
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)
vscmp 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)
vstest 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í.
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.
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
.
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.
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)
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.
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).
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.
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.
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.
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