2012-09-10 13 views
5

La documentación parece estar equivocada. ¿Podría alguien decirme qué es verdad?Accediendo al campo local vs campo de objeto. ¿Está doc equivocado?

En Performance Myths sección es:

En dispositivos sin un JIT, campo de almacenamiento en caché de los accesos es de aproximadamente 20% más rápido que accesssing repetidamente el campo. Con un JIT, el acceso al campo cuesta casi lo mismo que el acceso local.

En Avoid Internal Getters/Setters sección es:

Sin JIT, el acceso directo de campo es de aproximadamente 3x más rápido que la invocación de un captador trivial. Con el JIT (donde el acceso directo en el campo es tan barato como acceder a un local), el acceso directo al campo es aproximadamente 7x más rápido que invocar un getter trivial.

Está claro que sin JIT el acceso local es más rápido. También está claro que acceder al campo es más rápido al acceder directamente que con getter.

Pero por eso que en el primer caso es el rendimiento 20% mejor y en el segundo caso el rendimiento es del 133% mejor por el mismo motivo, que es la optimización de JIT para llamar campo de objeto?

+1

Yo diría que sí, la documentación es incorrecta/inexacta/en base a mediciones muy diferentes. OMI, si se toma literalmente, realmente establece que el JAT acelera el acceso al campo en un 20% y un 133% (3x frente a 7x) al mismo tiempo, lo que es imposible – zapl

+0

Exactamente, ese es mi punto. Me pregunto qué valores son correctos. – pawelzieba

+1

Ambas declaraciones se basan en diferentes puntos de referencia [FieldAccessBenchmark.java] (http://code.google.com/p/dalvik/source/browse/trunk/benchmarks/FieldAccessBenchmark.java) para el 20% y [MethodInvocationBenchmark.java] (http://code.google.com/p/dalvik/source/browse/trunk/benchmarks/MethodInvocationBenchmark.java) para 133%. Podría ser una razón Pero no me importaría demasiado, solo <5% ([ zapl

Respuesta

5

Creo que estás comparando manzanas y naranjas. la referencia de Mitos de rendimiento analiza la ventaja de un JIT para el acceso de campo, mientras que la segunda referencia discute la ventaja de un JIT para el acceso a métodos.

lo que tengo entendido, una analogía para acceder campo directo contra el acceso local (no locales campo acceso que usted escribió en su mensaje - no hay tal cosa como un campo local) es la siguiente:

class foo { 
    int bar = 42; 

    int doStuff(){ 
     int x = 1; 
     x += bar; 
     x += bar; 
     x += bar; 
     return x; 
    } 
} 

Cada referencia a bar tiene un costo de rendimiento asociado. Un buen compilador reconocerá la oportunidad para la optimización y 'reescritura' el código como tal:

int doStuff(){ 
    int x = 1f; 
    int local_bar = bar; 
    x += local_bar; 
    x += local_bar; 
    x += local_bar; 
    return x; 
} 

Sin JIT, esta es una optimización de la mano, que se consigue un 20% bache en el rendimiento.

Con un JIT, la optimización es innecesaria, ya que el JIT elimina el golpe de rendimiento del acceso al bar en primer lugar.

La segunda referencia describe el siguiente escenario:

class foo { 
    int bar = 42; 

    int getBar() { return bar; } 

    int doStuff(){ 
     int x = 1; 
     x += getBar(); 
     x += getBar(); 
     x += getBar(); 
     return x; 
    } 
} 

Cada llamada de función tiene una penalización de rendimiento asociado. Un compilador puede no almacenar en memoria caché las múltiples llamadas al método getBar() (como almacenó en caché los múltiples accesos de campo directos a bar en el ejemplo anterior), porque getBar() puede devolver un número completamente diferente cada vez que se llama (es decir, si componente aleatorio o basado en tiempo a su valor de retorno). Por lo tanto, es debe ejecutar tres llamadas al método.

Es vital entender que la función anterior se ejecutará aproximadamente a la misma velocidad con o sin un JIT.

Si reemplazara manualmente getBar() en la función anterior simplemente con bar, obtendría un aumento de rendimiento.En una máquina sin JIT, ese aumento de rendimiento es aproximadamente 3x, porque el acceso al campo es aún algo lento, por lo que reemplazar los métodos muy lentos con accesos de campo algo lentos solo produce un impulso moderado. Sin embargo, con un JIT, el acceso al campo es rápido, por lo que reemplazar los métodos muy lentos con acceso de campo rápido produce un impulso mucho mayor (7x).

¡Espero que tenga sentido!

+1

@Pedantic: ¡pásame! :) Ah, bueno, supongo que terminaré mi respuesta en una edición en aras de la exhaustividad. Es divertido que ambos hiciéramos referencia a manzanas y naranjas. – bfishman

+0

Buena coincidencia :) @bfishman: Hice una solución con "campo local". El documento dice que la ganancia de rendimiento para getter trivial está conectada con la optimización de acceso de campo '(donde el acceso directo en el campo es tan barato como acceder a un local)'. Parece que hay algo más. – pawelzieba

+0

Derecha, por lo que los métodos (captadores) son más lentos que los campos, que son más lentos que los locales. Sin un JIT, puede reemplazar un Método por un Campo y obtener un impulso moderado (3x). Con un JIT, puede reemplazar un Método por un campo, que luego es reemplazado por un Local, por lo tanto, le da un impulso más significativo (7x) desde que pasó de usar un Método a utilizar un Local. O, como dijo @delnan, el impacto de rendimiento relativo del uso de un Getter es mayor con un JIT, porque se pierde al pasar de Método-> Local, vice solo Método-> Campo sin JIT. – bfishman

2

Creo que puede estar comparando manzanas con naranjas. En la primera cita:

caching field accesses is about 20% faster than repeatedly accesssing the field 

implica que una estrategia de almacenamiento en caché podría mejorar el rendimiento sin compilación JIT sólo durante el acceso de campo directo. En otras palabras:

int a = this.field; 
if (a == 1) 
... 
if (a == 7) // etc. 

produce un mejor rendimiento que

if (this.field == 1) 
.... 
if (this.field == 7) //etc. 

La cita sugiere que tendrá una penalización golpeado por referencia en varias ocasiones el campo en lugar de almacenarla localmente.

La segunda cita sugiere que sin JIT utilizando un captador trivial/definidor es más lento que el acceso directo de campo, por ejemplo:

if (this.getField()) // etc. 

es más lento que:

if (this.field) // etc. 

no creo que la la documentación es incorrecta o que una afirmación menoscaba a la otra.

1

Esto es solo una suposición educada, no tengo idea acerca de las partes internas de Dalvik. Pero tenga en cuenta que en el primer caso, el rendimiento del acceso local se compara con el acceso al campo, mientras que en el segundo caso, el acceso al campo se compara con una llamada a un método trivial. También tenga en cuenta que x% de aceleración no es realmente x% menos de tiempo tomado para el mismo código al agregar un JIT, estamos hablando de rendimiento relativo: (a) El acceso local interpretado es un 20% más rápido que el acceso de campo interpretado, y (b) El acceso local JIT es tan rápido como el acceso de campo JIT no implica (c) El acceso local interpretado es tan rápido como el acceso local/de campo JIT. Es más probable que sea más lento, de hecho.

Leer un local en un intérprete es, con la mayoría de las arquitecturas de VM, un acceso de memoria, no un acceso de registro (estamos hablando de registros de máquina, no de registros de Dalvik). Leer un campo es aún más lento: no puedo decir con certeza por qué (supongo que sería la segunda búsqueda, leyendo el registro y el campo de objetos), pero en cualquier caso es más complejo. El JIT, por otro lado, puede poner tanto campos como locales en registros (eso es lo que debo suponer para explicar la igualdad de rendimiento, y de hecho hay JIT que hacen esto, no sé si se aplica aquí) y elimina gran parte de la sobrecarga.

Para llamadas de método, suponiendo que los JIT Dalvik no incorporan métodos (lo cual está implícito), tiene bastante sobrecarga de la llamada real que hace que las llamadas sean costosas incluso cuando JIT: debe guardar registros para apilar, debe restaurar luego, no se puede optimizar porque no todo el código está visible. Una llamada es relativamente más caro que el código call-less porque la alternativa call-less es tan increíblemente rápida, no porque el intérprete funciona mejor al hacer llamadas (no es así, simplemente también lento haciendo todo lo demás). Por ejemplo, la llamada no evita las optimizaciones porque no hay optimizaciones.

Cuestiones relacionadas