2011-10-20 14 views
8

He leído varios artículos y preguntas/respuestas que concluyen que la mejor práctica es permitir que el compilador JIT haga toda la optimización para las llamadas de función en línea. Tiene sentido.¿El compilador JIT optimiza (en línea) las declaraciones de variables innecesarias?

¿Qué pasa con las declaraciones de variables en línea? ¿El compilador también optimiza estos?

Es decir, será la siguiente:

 Dim h = (a + b + c)/2  'Half-Perimeter 

     If maxEdgeLength/(Math.Sqrt(h * (h - a) * (h - b) * (h - c))/h) <= MaximumTriangleAspectRatio Then 
      'Do stuff here. 
     End If 

tener un mejor rendimiento que esto:

 Dim perimeter = a + b + c 'Perimeter 
     Dim h = perimeter/2  'Half-Perimeter 

     Dim area = Math.Sqrt(h * (h - a) * (h - b) * (h - c)) 'Heron's forumula. 
     Dim inradius = area/h 
     Dim aspectRatio = maxEdgeLength/inradius 

     If aspectRatio <= MaximumTriangleAspectRatio Then 
      'Do stuff here. 
     End If 

Por supuesto que prefieren este último porque es más fácil de leer y depurar, pero no puede permitirse el lujo la degradación del rendimiento si existe.

Nota: Ya he identificado este código como un cuello de botella: no hay necesidad de retortas sobre la optimización prematura. :-)

+6

¿No puede pagar 20 bytes adicionales de RAM?Es muy poco probable que esto impulse el rendimiento de su aplicación, especialmente si este código se ejecuta una y otra vez, son los mismos 20 bytes cada vez. –

+0

¿No sería el compilador (no el JIT!) El "responsable" de este tipo de optimización durante la traducción a MSIL? (Y si, entonces, en qué medida optimiza el compilador de MS C#). En cualquier caso * hágalo en un entorno relativista * ya que esa es la única forma de "saber con certeza", que es más rápido y en qué medida. –

+4

Usted es la única persona aquí que puede responder la pregunta. Has escrito el código en ambos sentidos. Ejecútalo en ambos sentidos, mide el tiempo y luego sabrás cuál es más rápido. Lo que el jitter hace o deja de hacer es irrelevante; saber lo que hace la trepidación de ninguna manera responde a la pregunta "¿cuál es más rápido?" –

Respuesta

16

Las variables temporales que tienen nombres o no son un problema.

Pero puede optimizar esa desigualdad de manera significativa.

Su código fue:

If maxEdgeLength/(Math.Sqrt(h * (h - a) * (h - b) * (h - c))/h) <= MaximumTriangleAspectRatio Then 

multiplica ambos lados por la raíz cuadrada, eliminando la división (la desigualdad se conserva, porque la raíz cuadrada no puede devolver un número negativo):

If maxEdgeLength <= (Math.Sqrt(h * (h - a) * (h - b) * (h - c))/h) * MaximumTriangleAspectRatio Then 

Ahora, cuadrado ambos lados para eliminar la raíz cuadrada costosa:

If maxEdgeLength * maxEdgeLength <= h * (h - a) * (h - b) * (h - c)/h/h * MaximumTriangleAspectRatio * MaximumTriangleAspectRatio Then 

Cancelar, y m Ultiply por h.

If maxEdgeLength * maxEdgeLength * h <= (h - a) * (h - b) * (h - c) * MaximumTriangleAspectRatio * MaximumTriangleAspectRatio Then 

Esto será mucho más rápido. Si se repite este cálculo, considere almacenar en caché los resultados de parte de esta expresión para una mejora aún mayor.

Use los comentarios para explicar la fórmula. Deshacerse de una llamada Math.Sqrt en una función de cuello de botella vale la pena escribir la expresión en un formato menos que simple.

+1

En lugar de (solo) usar comentarios, refactorizar el cálculo en una función independiente: es demasiado tiempo para que ocurra en un código como ese. Explique * que * funcione según sea necesario (en particular, explique la transformación hecha arriba ...). –

+6

+1 para identificar que una optimización no se puede encontrar en el código o la compilación, sino en el algoritmo en sí. –

5

Por cierto, acaba de hacer de abogado del diablo, también quería señalar esto:

JIT procesos en línea de toda la función se ve en la longitud, en bytes de MSIL, y no a la complejidad de los cálculos. Agregar variables locales (y esperar que el JIT las registre) podría aumentar el tamaño de MSIL de la función lo suficiente como para que la función completa no sea candidata para la alineación.

Esto no es tan probable que marque una gran diferencia como el uso innecesario de Math.Sqrt, pero es una posibilidad. Como dijo Eric Lippert, sabrás más midiendo realmente. Sin embargo, tal medida solo es válida para una ejecución particular del programa, y ​​no se generaliza a diferentes procesadores o versiones futuras del tiempo de ejecución de .NET (incluidos los service packs) que a menudo modifican el comportamiento de JIT. Entonces, quiere un enfoque combinado analítico y empírico para la optimización.

+1

Usted hace un excelente punto Ben; excepto en casos triviales, en realidad no existe la "optimización". Cada llamada "optimización" es en realidad una compensación que * esperamos * es mejor, pero podría no serlo. Cambia más uso de registro en este cálculo por menos registros disponibles en ese cálculo, y espera que elija el que necesita para ser más rápido. O intercambia más uso de memoria por menos tiempo, y espero que al hacerlo no se pierda el caché más tarde. Y así. –

+0

@EricLippert: Sí, la optimización absoluta implica compensaciones. Pero eso está al lado de los puntos que estoy haciendo. Al usar la compilación JIT, usted está sujeto a los algoritmos de optimizador modificados, que hacen diferentes intercambios con la misma entrada de MSIL. Incluso con los compiladores nativos AOT tradicionales, la misma secuencia de instrucciones puede tener un funcionamiento diferente en diferentes procesadores con diferentes características de CPI. –

Cuestiones relacionadas