2010-09-07 16 views

Respuesta

12

No definido. Solicita explícitamente C#, pero eso depende de la arquitectura del procesador, es decir, del compilador de tiempo de ejecución CLR.

+1

Supongo que esto es cierto para la mayoría de los lenguajes de programación. – Andy

+0

... y lo mejor está determinado por lo que es más legible en el contexto en que se usa. –

44

Ninguno; ambos deben compilar hasta lo mismo si uno es más rápido o mejor.

Más importante aún, la mayoría de los programadores probablemente encontrarán que > 0 es más legible, y la legibilidad es más importante que las optimizaciones submicro como esta.

+7

+1 para señalar la importancia de la legibilidad – Matt

+7

No sé sobre su aseveración de legibilidad; Leo> = 1 como "1 o más", y esa es una forma más natural de definir una condición que "más que cero". Sin embargo, "más de cinco" tiene perfecto sentido en la conversación. Creo que la legibilidad dependerá del contexto, y de la presencia y la redacción de cualquier documento de requisitos o guías de usuario que los desarrolladores busquen en paralelo al código. También depende en gran medida del tipo de matemática involucrada; como dijo Jesse Millikan, las dos comparaciones no serían equivalentes en el mundo de coma flotante. – KeithS

+0

@ KiethS: Leí la intención de '> 0' como" entero positivo distinto de cero ". Leer '> = 1' me hace pensar en cómo los enteros no tienen los dígitos correctos del decimal para decidir eso. Y sí, las expresiones no son lo mismo; aunque no he hecho extensos programas de coma flotante antes. ¿Hay casos en que '> = 1' será mejor? Por supuesto. Pero dada la forma en que está escrita la pregunta, asumí que "el entero positivo no nulo" era la intención. –

7

La diferencia de rendimiento va a ser insignificante entre los dos (si es que hay alguno). Estoy trabajando en probar exactamente lo que podría ser (dependerá de la plataforma, ya que cualquier otro probablemente se reducirá al código emitido y ejecutado por JIT).

Tenga en cuenta, sin embargo, que en lo que respecta al rendimiento, se trata de una micro-optimización extrema que probablemente no esté justificada.

La mejor opción será la que sea más legible y transmita su intención de la mejor manera en su código.

1

Para responder a la más rápida, no estoy seguro, pero creo que son equivalentes. Y para responder mejor, creo que depende del contexto.

0

No notará ninguna diferencia, a menos que sea posible en un bucle muy ajustado que sea crítico para el rendimiento en su aplicación. Entonces necesita perfilar su código de todos modos para decidir cuál es mejor.

Utilice el que tenga más sentido en su aplicación.

26

El que es mejor es el que expresa más claramente su intención.

Si está probando para ver si es un entero en el rango [1, 6], entonces debería escribir como:

if (x >= 1 && x <= 6) { ... } 

Escribiendo esto funcionaría, pero no tan obviamente cumplir con la especificación :

if (x > 0 && x < 7) { ... } 

También estoy asumiendo que está hablando de tipos enteros aquí. Si se trata de números flotantes o decimales, entonces no son equivalentes.


A menos que perfila su código y pareció que este es el cuello de botella, no debe preocuparse por micro-optimizaciones. Aun así, puede ser interesante inspeccionar el código generado por el compilador de C# en cada caso para ver si se compilaron en la misma IL o no. Esto se puede hacer usando .NET Reflector.

if (x >= 1) 
{ 
    Console.WriteLine("True!"); 
} 

resulta en:

L_000b: ldloc.0   // Load the value of x 
L_000c: ldc.i4.1   // Load the constant 1 
L_000d: blt.s L_0019  // Branch if less than 
L_000f: ldstr "True!" 
L_0014: call void [mscorlib]System.Console::WriteLine(string) 

Considerando lo siguiente:

if (x > 0) 
{ 
    Console.WriteLine("True!"); 
} 

resultados en las siguientes IL:

L_000b: ldloc.0   // Load the value of x 
L_000c: ldc.i4.0   // Load the constant 0 
L_000d: ble.s L_0019  // Branch if less than or equal 
L_000f: ldstr "True!" 
L_0014: call void [mscorlib]System.Console::WriteLine(string) 

En ambos casos el compilador ha invertido la comparación. La prueba "mayor que o igual a" se compiló a una instrucción "menor que" y la prueba "mayor que" se compiló a "menor o igual a". En general, el compilador es libre de realizar tales modificaciones y ejecutar una versión diferente del compilador puede producir bytecode diferente (pero equivalente).

Dado que no compilan a la misma IL, la mejor manera de ver cuál es el más rápido es ejecutar realmente el código en un bucle y ver cuánto tarda cada versión en ejecutarse. Intenté hacer esto, pero no vi ninguna diferencia de rendimiento mensurable entre las dos formas de escribir el código.

+0

¡Estoy de acuerdo! Es lo que se espera de la declaración ...> 0 y> = 1 realmente hace 2 cosas diferentes (y a veces lo mismo) ... ¡o lo quieres todo o parte de él, que nada de eso! –

+0

Me gusta que haya mostrado el IL, especialmente cuando cambia la comparación en el bytecode. – Nick

0

Por lo general, cuando comparo algo a> 0 o> = 1, estoy tratando de ver si una matriz/colección contiene elementos. Si ese es el caso, en lugar de usar .Count > 0, intente utilizar el método de ayuda Enumerable.Any() en System.Linq, que debería ser mucho más rápido.

De lo contrario, no sé :)

+0

¿Por qué 'Any()' sería más rápido? Creo que sería más lento, ya que 'Count' es solo un acceso a la propiedad, pero' Any() 'tiene que enumerar el primer término de la secuencia. – jnylen

+0

@jnylen: Depende del conteo que estés usando. 'Enumerable.Count()' es mucho peor que 'Enumerable.Any()'. Si su contenedor tiene una propiedad 'Count' (es decir' List') entonces sí, debe usar eso. –

+2

Creo que .Any() evalúa a .Count> 0 si la colección es compatible con ese miembro. Y siempre es más rápido que .Count() cuando la colección no lo admite. No estoy seguro de qué IL compila, por lo que puede no ser de mucha ayuda ... –

2

No hay diferencia porque la CPU hace internamente una resta de los dos números e inspecciona el resultado y el desbordamiento. No hay un paso adicional involucrado para ninguna instrucción.

Cuando se trata de código, depende de lo que está intentando documentar. > = 1 significa que 1 es el número más bajo posible. > 0 significa que 0 no está permitido. Hay una pequeña diferencia semántica que los profesionales notarán. Ellos elegirán el operador adecuado para documentar su intención.

si usted piensa que> = ny> = n + 1 son los mismos que se equivoca:> = int.MaxValue y> (int.MaxValue + 1) son diferentes ^^

3

Estoy de acuerdo con la otras respuestas que las micro-optimizaciones generalmente no deberían tenerse en cuenta. Sin embargo, puede ser interesante ver cuál de las dos versiones tiene IL más pequeña/aparentemente_mayor.

Así:

using System; 

namespace IL_Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int i = 3; 
      if (i > 0) 
      { 
       Console.Write("i is greater than zero"); 
      } 
     } 
    } 
} 

se traduce en:

(DEBUG)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.3 
    L_0002: stloc.0 
    L_0003: ldloc.0 
    L_0004: ldc.i4.0 
    L_0005: cgt 
    L_0007: ldc.i4.0 
    L_0008: ceq 
    L_000a: stloc.1 
    L_000b: ldloc.1 
    L_000c: brtrue.s L_001b 
    L_000e: nop 
    L_000f: ldstr "i is greater than zero" 
    L_0014: call void [mscorlib]System.Console::Write(string) 
    L_0019: nop 
    L_001a: nop 
    L_001b: ret 
} 

(RELEASE)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i) 
    L_0000: ldc.i4.3 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.0 
    L_0004: ble.s L_0010 
    L_0006: ldstr "i is greater than zero" 
    L_000b: call void [mscorlib]System.Console::Write(string) 
    L_0010: ret 
} 

mientras

using System; 

namespace IL_Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      int i = 3; 
      if (i >= 1) 
      { 
       Console.Write("i is greater than zero"); 
      } 
     } 
    } 
} 

en

(DEBUG)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i, 
     [1] bool CS$4$0000) 
    L_0000: nop 
    L_0001: ldc.i4.3 
    L_0002: stloc.0 
    L_0003: ldloc.0 
    L_0004: ldc.i4.1 
    L_0005: clt 
    L_0007: stloc.1 
    L_0008: ldloc.1 
    L_0009: brtrue.s L_0018 
    L_000b: nop 
    L_000c: ldstr "i is greater than zero" 
    L_0011: call void [mscorlib]System.Console::Write(string) 
    L_0016: nop 
    L_0017: nop 
    L_0018: ret 
} 

(RELEASE)

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 i) 
    L_0000: ldc.i4.3 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.1 
    L_0004: blt.s L_0010 
    L_0006: ldstr "i is greater than zero" 
    L_000b: call void [mscorlib]System.Console::Write(string) 
    L_0010: ret 
} 

Por lo que yo puedo ver el i> = 1 es marginalmente más rápido que i> 0 EN MODO DEBUG

En modo de liberación al l la diferencia está en offset 0004 a BLE frente a BLT. Supongo que estas dos operaciones IL se traducen en operaciones nativas que consumen CPU por igual ...

+0

¿Se intercambian esos o me pasa algo? –

+2

Usted dice que uno es marginalmente más rápido, ¿por qué no publicó los resultados de su índice de referencia? ¿En qué máquina has probado? Optimizaciones en? La optimización es realizada por el JIT, no por el compilador de C#, por lo que mirar el IL generado no dice mucho sobre el rendimiento. – Niki

+2

@Caspar: el primero está haciendo: 'if (i> 0) == false', luego return (do not console.write). El segundo está haciendo: 'if (i <1) == true', luego regresa. La IL producida por la prueba de Andrei parece ser mucho más prolija que la que encontró Mark Byers. Punto hecho por otros: ejecutarlo, compararlo, preguntar ¿es REALMENTE más lento? El código aquí puede ser DEBUG compilado, o con otra versión del compilador ... no lo sabemos. Sí, JIT puede optimizar más, o no. Comparelo ... – maxwellb

4

Por supuesto, depende de la arquitectura de la CPU en la que se ejecutará el programa. En x86 las instrucciones jge y jg, que son relevantes aquí, toman el mismo número de ciclos IIRC. En el caso específico de pruebas para> 0, si está utilizando enteros sin signo, puede (realmente no lo sé) usar la instrucción test en lugar del cmp, ya que para enteros sin signo> 0 es equivalente a! = 0 . Otras arquitecturas pueden ser diferentes. El punto es que esto es de tan bajo nivel que, incluso en el caso excepcional de que valga la pena optimizarlo, no existe una forma independiente de hardware para optimizarlo.

Editar: Ha olvidado mencionar: Cualquier compilador o VM que merezca la pena debería ser capaz de descubrir que las pruebas> = 1 equivalen a probar> 0 y realizar una optimización tan trivial si incluso hace la diferencia en el lenguaje ensamblador nivel.

0

Si hubiera una diferencia entre los dos, entonces diría que esto sería una micro-optimización, lo que no debería afectar el rendimiento general de la aplicación.

Además, cuando uno realmente está averiguando si tiene que usar> 0 o> = 1, entonces diría que el costo de averiguar cuál es más rápido no supera el beneficio de rendimiento (mínimo).

Por lo tanto, también diría que debe usar la opción que más expresa la intención.

Cuestiones relacionadas