2012-04-28 36 views
18

ILSpy muestra que String.IsNullOrEmpty se implementa en términos de String.Length. Pero entonces ¿por qué es String.IsNullOrEmpty(s) más rápido que s.Length == 0?¿Por qué String.IsNullOrEmpty es más rápido que String.Length?

Por ejemplo, es un 5% más rápido en este punto de referencia:..

var stopwatches = Enumerable.Range(0, 4).Select(_ => new Stopwatch()).ToArray(); 
var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,STU,V,W,X,Y,Z,".Split(','); 
var testers = new Func<string, bool>[] { s => s == String.Empty, s => s.Length == 0, s => String.IsNullOrEmpty(s), s => s == "" }; 
int count = 0; 
for (int i = 0; i < 10000; ++i) { 
    stopwatches[i % 4].Start(); 
    for (int j = 0; j < 1000; ++j) 
     count += strings.Count(testers[i % 4]); 
    stopwatches[i % 4].Stop(); 
} 

(Otros puntos de referencia muestran resultados similares Éste reduce al mínimo el efecto de la costra que se ejecuta en el ordenador también, como un aparte, las pruebas de comparación a cadenas vacías salieron de la misma en aproximadamente un 13% más lento que IsNullOrEmpty.)

Además, ¿por qué es IsNullOrEmpty sólo es más rápido en x86, x64, mientras que en String.Length es aproximadamente un 9% más rápido?

Actualización: Detalles de configuración de prueba: .NET 4.0 ejecutándose en Windows 7 de 64 bits, procesador Intel Core i5, proyecto de consola compilado con "Código de optimización" habilitado. Sin embargo, "Suprimir la optimización de JIT en la carga del módulo" también se habilitó (ver respuesta aceptada y comentarios).

Con la optimización totalmente habilitado, Length es aproximadamente un 14% más rápido que IsNullOrEmpty con el delegado y otra superior eliminado, como en esta prueba:

var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,,STU,V,,W,,X,,,Y,,Z,".Split(','); 
int count = 0; 
for (uint i = 0; i < 100000000; ++i) 
    count += strings[i % 32].Length == 0 ? 1 : 0; // Replace Length test with String.IsNullOrEmpty 
+7

Sin conocer la situación exacta en la que se encuentra, estoy bastante seguro de que en la mayoría de las situaciones hay otras optimizaciones que agonizar en lugar de la forma más rápida de verificar si una cadena no contiene ningún dato. – Andrew

+0

@minitech Porque hay 4 probadores, y los sincroniza de forma independiente. –

+0

¿Cómo se comparan 'Empty' y' == 'con' Length' y 'IsNullOrEmpty'? –

Respuesta

20

Es porque ejecutó su punto de referencia desde Visual Studio, lo que impide que el compilador JIT optimice el código. Sin optimizaciones, este código se produce para String.IsNullOrEmpty

00000000 push  ebp 
00000001 mov   ebp,esp 
00000003 sub   esp,8 
00000006 mov   dword ptr [ebp-8],ecx 
00000009 cmp   dword ptr ds:[00153144h],0 
00000010 je   00000017 
00000012 call  64D85BDF 
00000017 mov   ecx,dword ptr [ebp-8] 
0000001a call  63EF7C0C 
0000001f mov   dword ptr [ebp-4],eax 
00000022 movzx  eax,byte ptr [ebp-4] 
00000026 mov   esp,ebp 
00000028 pop   ebp 
00000029 ret 

y ahora compararlo con código producido por == Longitud 0

00000000 push ebp 
00000001 mov ebp,esp 
00000003 sub esp,8 
00000006 mov dword ptr [ebp-8],ecx 
00000009 cmp dword ptr ds:[001E3144h],0 
00000010 je  00000017 
00000012 call 64C95BDF 
00000017 mov ecx,dword ptr [ebp-8] 
0000001a cmp dword ptr [ecx],ecx 
0000001c call 64EAA65B 
00000021 mov dword ptr [ebp-4],eax 
00000024 cmp dword ptr [ebp-4],0 
00000028 sete al 
0000002b movzx eax,al 
0000002e mov esp,ebp 
00000030 pop ebp 
00000031 ret 

Se puede ver, que codifican para Longitud == 0 hace todo lo que hace código para String.IsNullOrEmpty, pero adicionalmente intenta algo así como tontamente convertir el valor booleano (devuelto por la comparación de longitud) aga en booleano y esto lo hace más lento que String.IsNullOrEmpty.

Si compila el programa con las optimizaciones habilitadas (modo de lanzamiento) y ejecuta el archivo .exe directamente desde Windows, el código generado por el compilador JIT es mucho mejor. Para Cadena.IsNullOrEmpty es:

001f0650 push ebp 
001f0651 mov  ebp,esp 
001f0653 test ecx,ecx 
001f0655 je  001f0663 
001f0657 cmp  dword ptr [ecx+4],0 
001f065b sete al 
001f065e movzx eax,al 
001f0661 jmp  001f0668 
001f0663 mov  eax,1 
001f0668 and  eax,0FFh 
001f066d pop  ebp 
001f066e ret 

y para Longitud == 0:

001406f0 cmp  dword ptr [ecx+4],0 
001406f4 sete al 
001406f7 movzx eax,al 
001406fa ret 

Con este código, resultado son los esperados, es decir Longitud == 0 es ligeramente más rápido que Cadena .IsNullOrEmpty.

También vale la pena mencionar que el uso de Linq, expresiones lambda y módulo de computación en su punto de referencia no es una buena idea, porque estas operaciones son lentas (relativamente a la comparación de cadenas) y hacen que el índice de referencia sea inexacto.

+0

Obtengo los mismos resultados si ejecuto las pruebas desde dentro Ejecuto las pruebas dentro o fuera de Visual Studio. En ambos casos, estoy construyendo en modo Release contra .NET Framework 4, y la configuración de "Optimizar código" está activada en el archivo del proyecto (su valor predeterminado). En Visual Studio, veo el código ensamblado no optimizado que ha publicado.¿Cómo ve el código ensamblador que se genera cuando se ejecuta fuera de Visual Studio? –

+0

Eso es extraño. Probé este punto de referencia en 3 computadoras diferentes con diferentes sistemas operativos (Windows Server 2008 x64, Windows XP x86) con diferentes CPU y siempre obtengo que Length == 0 es más rápido. Además, desactivé la generación de archivos .PDB en Visual Studio para este proyecto, pero probablemente esto no sea un problema. ¿Has probado con otra computadora? Anexé al proceso en ejecución con [WinDbg] (http://archive.msdn.microsoft.com/debugtoolswindows) para ver el código de ensamblaje optimizado. –

+0

Volví a realizar los experimentos dentro y fuera de VS2010 y no pude reproducir lo que informé en mi comentario anterior. Ahora veo lo mismo que tú, que 'Length' es un poco más rápido cuando estás fuera de VS2010. También recordé esta configuración: Herramientas> Opciones> Depuración> General> Suprimir la optimización de JIT en la carga del módulo. Me olvidé de apagarlo. Cuando lo hice, obtuve los mismos resultados dentro y fuera de VS2010, con 'Length' siendo más rápido. Además, al alternar la configuración de optimización JIT, puedo reproducir todos los 4 listados de código de ensamblaje dentro de VS2010. –

-4

puede ser causada por los tipos de las variables involucradas. * Empty parece usar un valor booleano, length an int (supongo).

¡Paz!

  • : editar
+3

-1 para adivinar. Y adivinando incorrectamente. Si bien es muy recomendable que comparta sus conocimientos en este foro, adivinar una respuesta agrega ruido indeseable a nuestro discurso. –

1

prueba Usted es somethere mal. IsNullOrEmpty no puede ser más rápido por definición, ya que realiza una operación de comparación nula adicional y luego prueba la Longitud.

Así que la respuesta puede ser: es más rápido debido a su prueba. Sin embargo, incluso su código muestra que IsNullOrEmpty es consistentemente más lento en mi máquina en los modos x86 y x64.

+0

Creo que IsNullOrEmpty puede ejecutarse más rápido en el caso de una cadena nula, ya que no se realiza la verificación de longitud. Aunque dudo que se observe un rendimiento apreciable, se observa un aumento en el rendimiento, si se espera que la cadena sea nula, esta comprobación puede tener más sentido. – overslacked

+1

Creo que no es válido hablar sobre cadenas 'null' ya que' .Length' no se aplica en absoluto en ese caso :) –

+0

Touché! . . . . – overslacked

4

Su punto de referencia no mide String.IsNullOrEmpty vs String.Length, sino más bien cómo se generan diferentes expresiones lambda para las funciones. Es decir. no es muy sorprendente que el delegado que solo contiene llamada de función única (IsNullOrEmpty) sea más rápido que uno con llamada de función y comparación (Longitud == 0).

Para obtener una comparación del código real de llamada y escritura que los llama directamente sin delegados.

EDITAR: Mis mediciones muestran que la versión delegada con IsNullOrEmpty es ligeramente más rápida que el resto, mientras que las llamadas directas a la misma comparación están en orden inverso (y aproximadamente dos veces más rápido debido a una cantidad significativamente menor de código adicional) en mi máquina . Es probable que los resultados sean cautelosos entre máquinas, modo x86/x64, así como entre versiones de tiempo de ejecución. A efectos prácticos, consideraría que las 4 formas son más o menos las mismas si necesita usarlas en consultas LINQ.

En general, dudo que haya una diferencia mensurable en el programa real encapsulado por elección entre estos métodos, así que elija el que sea más legible para usted y úselo. Generalmente prefiero IsNullOrEmpty ya que da menos posibilidades de obtener == /! = Mal en una condición.

La eliminación de la manipulación de cadenas en conjunto con el código de tiempo crítico probablemente traerá mucha ventaja de que escoger entre estas opciones, también eliminando LINQ para el código crítico es una opción. Como siempre, asegúrese de medir la velocidad general del programa en el escenario de la vida real.

1

Creo que su prueba no es correcta:

Esta prueba muestra que string.IsNullOrEmpty es siempre más lento que s.Length==0, ya que realiza una comprobación nula adicional:

var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,STU,V,W,X,Y,Z,".Split(','); 
var testers = new Func<string, bool>[] { 
    s => s == String.Empty, 
    s => s.Length == 0, 
    s => String.IsNullOrEmpty(s), 
    s => s == "" , 
}; 
int n = testers.Length; 
var stopwatches = Enumerable.Range(0, testers.Length).Select(_ => new Stopwatch()).ToArray(); 
int count = 0; 
for(int i = 0; i < n; ++i) { // iterate testers one by one 
    Stopwatch sw = stopwatches[i]; 
    var tester = testers[i]; 
    sw.Start(); 
    for(int j = 0; j < 10000000; ++j) // increase this count for better precision 
     count += strings.Count(tester); 
    sw.Stop(); 
} 
for(int i = 0; i < testers.Length; i++) 
    Console.WriteLine(stopwatches[i].ElapsedMilliseconds); 

Resultados:

6573 
5328 
5488 
6419 

Puede usar s.Length==0 cuando se asegura de que los datos de destino no contengan cadenas nulas. En otros casos, sugiero que use el String.IsNullOrEmpty.

+0

Cuando estructurar la prueba de esta manera, obtengo los mismos resultados, pero una desviación estándar más alta entre las pruebas. Creo que es porque es más fácil para otros procesos o códigos del sistema operativo influir injustamente en un solo comprobador. En promedio, todavía termino con 'IsNullOrEmpty' siendo más rápido en x86. Estoy ejecutando un Core i5 de 64 bits. ¿Constantemente encuentras 'Length' es más rápido? –

0

Creo que es imposible IsNullOrEmpty para ser más rápido porque, como todo lo demás, también se comprueba null. Pero más rápido o no, la diferencia será tan pequeña, que esto le da una ventaja al usar IsNullOrEmpty solo por este control nulo adicional que hace que su código sea más seguro.

-2

En CLR via CSharp capítulo 10 "Propiedades" Jeff Richter escribe:

Un método de propiedad puede tomar un largo tiempo para su ejecución; el acceso al campo siempre se completa de inmediato. Una razón común para usar propiedades es realizar la sincronización de subprocesos, que puede detener el subproceso para siempre y, por lo tanto, no se debe usar una propiedad si se requiere sincronización de subprocesos. En esa situación, se prefiere un método. Además, si se puede acceder a su clase de forma remota (por ejemplo, su clase se deriva de System.MarshalByRefObject), llamar al método de propiedad será muy lento, y por lo tanto, se prefiere un método a una propiedad. En mi opinión, las clases derivadas de MarshalByRefObject nunca deben usar propiedades.

Así que si vemos String.Length es propiedad y String.IsNullOrEmpty es un método que puede ejecutar más rápido que la propiedad String.Length.

+1

Una propiedad solo existe realmente como metadatos. Cuando "obtienes" la propiedad, llama a un método habitual llamado 'get_PropertyName', y cuando" fijas "la propiedad, llama a un método regular llamado' set_PropertyName'. Desde la perspectiva de JIT y el tiempo de ejecución, no hay diferencia entre una propiedad y un método. –

Cuestiones relacionadas