2009-12-07 11 views
7

Después de leer aquí una pregunta sobre qué cosas podría hacer nuestra computadora en un segundo hice una pequeña prueba que tenía en mente por un tiempo y estoy muy sorprendido por los resultados. Mira:¿Por qué esta diferencia de rendimiento? (Captura de excepciones)

programa sencillo para detectar una excepción nula, lleva casi un segundo para hacer iteraciones 1900:

for(long c = 0; c < 200000000; c++) 
{ 
    try 
    { 
     test = null; 
     test.x = 1; 
    } 
    catch (Exception ex) 
    { 
    } 
} 

Alternativamente, comprobando si la prueba == null antes de hacer la asignación, la misma pogram puede hacer aproximadamente 200 millones iteraciones en un segundo.

for(long c = 0; c < 1900; c++) 
{ 
    test = null; 
    f (!(test == null)) 
    { 
     test.x = 1; 
    } 
} 

¿Alguien tiene una explicación detallada de por qué esta ENORME diferencia?

EDIT: Ejecución de la prueba en modo de lanzamiento, estudio visual fuera me estoy poniendo 35000-40000 iteraciones vs 400000000 iteraciones (siempre aprox)

Nota estoy corriendo esta mierda con un PIV 3.06 Ghz

+0

como pregunta. voy a probarlo –

+9

Usando 'if (test! = Null)' sería más claro por cierto. –

+0

@Jon Skeet: ¿No sería también un poco más rápido? (Una operación de CPU en lugar de dos) – Powerlord

Respuesta

12

No hay forma de que tome un segundo para las iteraciones de 1900 a menos que esté ejecutando en el depurador. Ejecutar pruebas de rendimiento bajo el depurador es una mala idea.

EDIT: Tenga en cuenta que esto no es un caso de cambio de la liberación construir - se trata de un caso de ejecución sin el depurador; es decir, presionar Ctrl-F5 en lugar de F5.

Dicho esto, provocar excepciones cuando se pueden evitar muy fácilmente es también una mala idea.

Mi opinión sobre el desempeño de excepciones: si está usando de manera apropiada, no deben causar problemas de rendimiento significativas menos que usted está en una situación catastrófica de todos modos (por ejemplo, que estamos tratando de hacer cientos de miles de llamadas al servicio web y la red no funciona).

Las excepciones son costosas en los depuradores, sin duda en Visual Studio, de todos modos, debido a la determinación de si entrar o no en el depurador, etc. y probablemente realizar cualquier cantidad de análisis de la pila que de otra forma sería innecesario. Siguen siendo un tanto caros de todos modos, pero no deberías tirar suficientes para darte cuenta. Todavía hay que deshacerse de la pila, encontrar manejadores de captura relevantes, etc., pero esto solo debería estar ocurriendo cuando algo está mal en primer lugar.

EDIT: Por supuesto, una excepción es todavía va a dar un menor número de iteraciones por segundo (aunque 35000 sigue siendo un número muy bajo - Me esperaba más de 100K) porque estás haciendo casi nada en la no -caso de la excepciónVeamos los dos:

versión

no excepción del cuerpo del bucle

  • Asignar a la variable nula
  • Compruebe si la variable es nula; que es, a fin de ir de nuevo a la parte superior del bucle

(Como se ha mencionado en los comentarios, es muy posible que el JIT optimizar esta distancia de todos modos ...) versión

Excepción:

  • Asignar a la variable nula
  • desreferencia variables
    • cheque implícito de nulidad
    • Crear un objeto de excepción
    • Compruebe si hay controladores de excepciones filtrados para llamar
    • buscar la pila para el bloque catch para saltar a
    • Compruebe si hay bloques finally
    • rama apropiada

¿No es de extrañar que esté viendo un menor rendimiento?

Ahora compare eso con la situación más común en la que hace un montón de trabajo, posiblemente IO, creación de objetos, etc. - y quizás se emite una excepción. Entonces la diferencia se vuelve mucho menos significativa.

+1

. La etiqueta dice que es C#. –

+0

@devoured elysium: Lo hace ahora; no lo hizo cuando respondí. –

+0

¡Es cierto! Lo estaba ejecutando bajo VS. Ahora está en 35000 iteraciones, pero la diferencia sigue siendo enorme ... la otra prueba sube a 400000000 iteraciones en las mismas condiciones ... – Drevak

2

Consulte Chris Brumme's blog con especial atención a la sección Rendimiento y tendencias para obtener una explicación sobre por qué las excepciones son lentas. Se llaman 'excepciones' por una razón: no deberían suceder muy a menudo.

1

Una optimización que realiza el compilador y creo que podría ser "eliminación de código muerto"; también dependiendo del compilador que está utilizando, este último programa está haciendo lo que los ensambladores llaman "no-op".

+0

Si eso no funciona, entonces 400 millones de iteraciones por segundo son bastante lentas. Podría ser; Supongo que depende de lo lento que sean los incrementos largos en una máquina de 32 bits. –

1

En mis pruebas, el código "excepcional" no es tan lento, mucho más lento, pero no tanto. La diferencia radica en crear el objeto Exception (o, para ser específico, NullReferenceException). La parte más lenta es recuperar la cadena para el mensaje de excepción (hay una llamada interna a GetResourceString) y obtener el seguimiento de la pila.

+0

Ver mi comentario en la respuesta de Jhon Skeet. ¿Cuáles son sus números? – Drevak

2

Hay otro factor aquí. Si tiene el archivo .pdb en su lugar en el directorio de ejecución, cuando se lanza la excepción, el tiempo de ejecución de .NET leerá el archivo .pdb para que el número de línea de código se incluya en el rastreo de la pila de excepción. Esto toma bastante tiempo. Pruebe su primer método (el que tiene una excepción) con y sin el archivo .pdb en el directorio de ejecución.

Hice una prueba de tiempo simple con y sin el.pdb en su lugar como respuesta a otra pregunta, here.

0

Este es un micro punto de referencia horrible.

El último bucle 'optimizado' tiene como invariable el tiempo de compilación que la prueba siempre es nula, por lo que no es necesario ni siquiera molestarse en compilar la asignación intentada. De hecho estás probando un bucle vacío arrojando una excepción cada vez.

Un muy buen jit podría incluso eliminar por completo el bucle, teniendo en cuenta que el bucle no tiene cuerpo, por lo tanto no tiene efectos secundarios aparte de incrementar el contador y que el contador no se usa (esto es improbable ya que tal optimización tiene poca utilidad en el mundo real).

excepciones son razonablemente caro para tirar (en relación con el flujo de control de ramificación convencional) [1] debido principalmente a 3 cosas:

  1. todas las excepciones son los tipos de referencia y por lo tanto (por ahora) se asignan montón y posteriormente basura recolectada.
  2. Los niveles de la pantalla completa automáticamente en la excepción (Esto es proporcional a la La distancia que la pila se desenrolla - algo que ejemplo falla completamente para medir)
  3. Al entrar en el código de control de excepciones se salta todas las cosas agradables como la predicción de saltos que hoy vamos profundamente procesador segmentado mantener a sí mismos haciendo algo útil

lanzamiento y captura de excepciones dentro de un bucle estrecho es casi seguro que un diseño defectuoso de forma masiva de todos modos, pero si usted busca para medir este impacto se debe escribir un bucle que de hecho lo hace.


  1. caro aquí ser un término relativo muy. Todavía puede hacer decenas de miles de ellos en un hardware modesto.
Cuestiones relacionadas