2011-02-11 13 views
12

Estoy interesado si debo en línea manualmente pequeños métodos que se llaman 100k - 1 millón de veces en algún algoritmo sensible al rendimiento.Java - llamar a métodos estáticos vs manual en línea - sobrecarga de rendimiento

En primer lugar, pensé que, al no incluir, incurriría en una sobrecarga ya que JVM deberá determinar si desea alinear o no este método (o incluso si no lo hace).

Sin embargo, el otro día reemplacé este código en línea manualmente con la invocación de métodos estáticos y observé un aumento en el rendimiento. ¿Cómo es eso posible? ¿Sugiere esto que en realidad no hay gastos generales y que al permitir que JVM se alinee con "su voluntad" en realidad aumenta el rendimiento? ¿O esto depende enormemente de la plataforma/arquitectura?

(El ejemplo en el que se produjo un aumento de rendimiento fue la sustitución de matriz de intercambio (int t = a[i]; a[i] = a[j]; a[j] = t;) con una llamada de método estático swap(int[] a, int i, int j). Otro ejemplo en el que no había ninguna diferencia de rendimiento fue cuando inlined un método 10-liner que se llamó 1000000 veces .)

Respuesta

11

He visto algo similar. La "inserción manual" no es necesariamente más rápida, el programa de resultados puede ser demasiado complejo para que lo analice el optimizador.

En su ejemplo, hagamos algunas suposiciones descabelladas. Cuando utiliza el método swap(), JVM puede analizar el cuerpo del método y concluir que, dado que i y j no cambian, aunque hay 4 accesos de matriz, solo se necesitan 2 verificaciones de rango en lugar de 4. También el no es necesaria la variable local t, JVM puede usar 2 registros para hacer el trabajo, sin involucrar r/w de t en la pila.

Más tarde, el cuerpo del swap() se inserta en el método de llamador. Eso es después de la optimización previa, por lo que los guardados aún están en su lugar.Incluso es posible que el cuerpo del método de llamada haya probado que i y j están siempre dentro del rango, por lo que las 2 comprobaciones de rango restantes también se descartan.

Ahora en la versión manual, el optimizador tiene que analizar todo el programa a la vez, hay demasiadas variables y demasiadas acciones, puede fallar al demostrar que es seguro guardar las comprobaciones de rango, o eliminar la variable local t. En el peor de los casos, esta versión puede costar 6 accesos de memoria más para hacer el intercambio, que es un gran sobrecarga. Incluso si solo hay 1 lectura extra de memoria, todavía es muy notable.

Por supuesto, no tenemos ninguna base para creer que siempre es mejor hacer un "delineamiento" manual, es decir, extraer métodos pequeños, pensando que ayudará al optimizador.

-

Lo que he aprendido es que, olvidar optimizaciones micro manuales. No es que no me interesen las mejoras en el rendimiento micro, no es que siempre confíe en la optimización de JVM. Es que no tengo absolutamente ninguna idea de qué hacer que haga más bien que mal. Entonces me di por vencido.

0

El compilador Hotspot de Hotspot es capaz de incluir un montón de cosas, especialmente en el modo -server, aunque no sé cómo se obtuvo un real aumento de rendimiento. (Mi conjetura sería que la creación de línea se hace mediante el recuento de invocación de método y el método de intercambio de los dos valores no se llama con demasiada frecuencia)

Por cierto, si su rendimiento realmente importa, puede intentar esto para intercambiar dos int valores. (No estoy diciendo que será más rápido, pero puede valer la pena una batea.)

a[i] = a[i]^a[j]; 
a[j] = a[i]^a[j]; 
a[i] = a[i]^a[j]; 
+3

No es más rápido en casi cualquier arquitectura moderna. – Puppy

+1

Lo intenté hace un par de años y fue más lento .... – mikera

+0

@DeadMG Como dije, vale la pena intentarlo. Nada mas. – biziclop

1

Sin embargo, el otro día, he sustituido el código inline manualmente con invocación de métodos estáticos y he visto una actuación aumentar. ¿Cómo es eso posible?

Probablemente el perfilador JVM vea el cuello de botella más fácilmente si está en un lugar (un método estático) que si se implementa varias veces por separado.

8

La JVM puede alinear pequeños métodos de manera muy eficiente. El único beneficio que se está imprimiendo es si puede eliminar el código, es decir, simplificar lo que hace al subrayarlo.

La JVM busca ciertas estructuras y tiene algunas optimizaciones "codificadas a mano" cuando reconoce esas estructuras. Al usar un método de intercambio, la JVM puede reconocer la estructura y optimizarla de manera diferente con una optimización específica.

Puede que le interese probar la versión de depuración OpenJDK 7 que tiene la opción de imprimir el código nativo que genera.

2

Disculpa mi respuesta tardía, pero acabo de encontrar este tema y me llamó la atención.

Al desarrollar en Java, intente escribir código "simple y estúpido". Razones:

  1. la optimización se realiza en tiempo de ejecución (ya que la compilación en sí se realiza en tiempo de ejecución). El compilador descubrirá de todos modos qué optimización realizar, ya que compila no el código fuente que se escribe, sino la representación interna que utiliza (varias AST -> código VM -> código VM ... -> las transformaciones del código binario nativo se hacen en tiempo de ejecución por el compilador de JVM y el intérprete de JVM)
  2. Al optimizar, el compilador usa algunos patrones de programación comunes para decidir qué optimizar; ¡así que ayúdalo a ayudarte! escribir un método estático privada (tal vez también final) y se darán cuenta de inmediato que se puede:
    • en línea el método
    • compilar a código nativo

Si el método se colocarán en línea manualmente , es solo parte de otro método que el compilador primero intenta comprender y ver si es hora de transformarlo en código binario o si debe esperar un poco para entender el flujo del programa. Además, dependiendo de lo que haga el método, varios re-JIT son posibles durante el tiempo de ejecución => JVM produce un código binario óptimo solo después de un "calentamiento" ... y tal vez su programa finalizó antes de que la JVM se caliente (porque esperar que al final el rendimiento sea bastante similar).

Conclusión: tiene sentido optimizar el código en C/C++ (ya que la traducción al binario se hace estáticamente), pero las mismas optimizaciones generalmente no hacen una diferencia en Java, donde el compilador JIT código de bytes, no su código fuente. Y, por cierto, por lo que he visto javac ni siquiera se molesta en hacer optimizaciones :)

Cuestiones relacionadas