java performance methodhandle

java - MethodHandle performance



(3)

Parece que esto fue respondido indirectamente por @AlekseyShipilev en referencia a una consulta diferente. En el siguiente enlace, ¿cómo puedo mejorar el rendimiento de Field.set (quizás utilizando MethodHandles)?

Si lees, verás puntos de referencia adicionales que muestran resultados similares. Es probable que las llamadas directas puedan ser optimizadas simplemente por JIT de manera que, de acuerdo con los hallazgos anteriores, la diferencia es: MethodHandle.invoke = ~ 195ns MethodHandle.invokeExact = ~ 10ns Direct calls = 1.266ns

Entonces, las llamadas directas seguirán siendo más rápidas, pero el MH es muy rápido. Para la mayoría de los casos de uso, esto debería ser suficiente y ciertamente es más rápido que el marco de reflexión anterior (por cierto, según los hallazgos anteriores, la reflexión también es significativamente más rápida bajo java8 vm)

Si esta diferencia es significativa en su sistema, sugeriría encontrar patrones diferentes en lugar de una reflexión directa que admita llamadas directas.

Escribí un pequeño punto de referencia que prueba el rendimiento de java.lang.invoke.MethodHandle , java.lang.reflect.Method y llamadas directas de métodos.

Leí el rendimiento de MethodHandle.invoke() casi lo mismo que las llamadas directas. Pero los resultados de mi prueba muestran otra: MethodHandle invoca aproximadamente tres veces más lento que la reflexión. Cual es mi problema ¿Puede ser este el resultado de algunas optimizaciones JIT?

public class Main { public static final int COUNT = 100000000; static TestInstance test = new TestInstance(); static void testInvokeDynamic() throws NoSuchMethodException, IllegalAccessException { int [] ar = new int[COUNT]; MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(int.class); MethodHandle handle = lookup.findStatic(TestInstance.class, "publicStaticMethod", mt) ; try { long start = System.currentTimeMillis(); for (int i=0; i<COUNT; i++) { ar[i] = (int)handle.invokeExact(); } long stop = System.currentTimeMillis(); System.out.println(ar); System.out.println("InvokeDynamic time: " + (stop - start)); } catch (Throwable throwable) { throwable.printStackTrace(); } } static void testDirect() { int [] ar = new int[COUNT]; try { long start = System.currentTimeMillis(); for (int i=0; i<COUNT; i++) { ar[i] = TestInstance.publicStaticMethod(); } long stop = System.currentTimeMillis(); System.out.println(ar); System.out.println("Direct call time: " + (stop - start)); } catch (Throwable throwable) { throwable.printStackTrace(); } } static void testReflection() throws NoSuchMethodException { int [] ar = new int[COUNT]; Method method = test.getClass().getMethod("publicStaticMethod"); try { long start = System.currentTimeMillis(); for (int i=0; i<COUNT; i++) { ar[i] = (int)method.invoke(test); } long stop = System.currentTimeMillis(); System.out.println(ar); System.out.println("Reflection time: " + (stop - start)); } catch (Throwable throwable) { throwable.printStackTrace(); } } static void testReflectionAccessible() throws NoSuchMethodException { int [] ar = new int[COUNT]; Method method = test.getClass().getMethod("publicStaticMethod"); method.setAccessible(true); try { long start = System.currentTimeMillis(); for (int i=0; i<COUNT; i++) { ar[i] = (int)method.invoke(test); } long stop = System.currentTimeMillis(); System.out.println(ar); System.out.println("Reflection accessible time: " + (stop - start)); } catch (Throwable throwable) { throwable.printStackTrace(); } } public static void main(String ... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InterruptedException { Thread.sleep(5000); Main.testDirect(); Main.testInvokeDynamic(); Main.testReflection(); Main.testReflectionAccessible(); System.out.println("/n___/n"); System.gc(); System.gc(); Main.testDirect(); Main.testInvokeDynamic(); Main.testReflection(); Main.testReflectionAccessible(); } }

Ambiente: versión Java "1.7.0_11" Java (TM) SE Runtime Environment (compilación 1.7.0_11-b21) VM de servidor de 64 bits de Java HotSpot (TM) (compilación 23.6-b04, modo mixto) SO - Windows 7 64


Parece que otros han visto resultados similares: http://vanillajava.blogspot.com/2011/08/methodhandle-performance-in-java-7.html

Aquí hay alguien más: http://andrewtill.blogspot.com/2011/08/using-method-handles.html

Corrí el segundo y vi que eran casi de la misma velocidad, incluso corrigiendo esa prueba para tener un calentamiento. Sin embargo, lo arreglé para que no estuviera creando una matriz args cada vez. En el recuento predeterminado, se obtuvo el mismo resultado: los métodos de manejo fueron un poco más rápidos. Pero hice un recuento de 10000000 (predeterminado * 10) y la reflexión fue mucho más rápida.

Por lo tanto, recomendaría pruebas con parámetros. Me pregunto si MethodHandles trata los parámetros de manera más eficiente. También, verifique cambiar la cuenta - cuántas iteraciones.

El comentario de @ meriton sobre la pregunta enlaza con su trabajo y parece muy útil: llamar a un captador en Java a través de la reflexión: ¿Cuál es la forma más rápida de llamarlo repetidamente (en cuanto a rendimiento y escalabilidad)?


Si publicStaticMethod fue una implementación simple como devolver una constante, es muy posible que la llamada directa esté en línea con el compilador JIT. Esto puede no ser posible con methodHandles.

RE http://vanillajava.blogspot.com/2011/08/methodhandle-performance-in-java-7.html ejemplo, como se mencionó que comenta que no es una gran implementación. Si cambia la conversión de tipo a int (en lugar de Integer) en el ciclo de cálculo, los resultados se acercan más al método directo de llamada.

Con la implementación complicada de (crear y llamar a una tarea futura que devuelve un int aleatorio) se obtuvo una referencia con números más cercanos donde MethodStatic era un máximo de un 10% más lento que el método directo. Por lo tanto, podría estar viendo un rendimiento 3 veces más lento debido a las optimizaciones JIT