2010-12-29 19 views
5

Si usamos Dynamic Invoke, ¿hay alguna mejora en el rendimiento de la aplicación? Si no, ¿cuál es su ventaja?Función de invocación dinámica en java

+0

es con respecto característica dinámica de invocación en el JDK 7. – user556913

+0

@ T. J. Crowder: Creo que se está refiriendo a la nueva instrucción 'bytecode' invookedynamic en Java 7. http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html – coobird

+0

@coobird, @user: Gracias. –

Respuesta

0

Creo que te refieres a la reflexión por invocación dinámica. El costo de la reflexión es un poco más alto que la invocación al método normal.

reflexión tiene muchos usos, por ejemplo, lo uso extensivamente para la unión ui-datos ..

1

supongo OP significa InvokeDynamic como se ha mencionado aquí http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html para la invocación del método. Nunca he usado pero this post looks very descriptive cotizaciones

"La diferencia de rendimiento puede no importar para una biblioteca que hace algunas llamadas reflejadas, especialmente si esas llamadas son principalmente para establecer dinámicamente una estructura estática en la memoria contra la que puede hacer normal llamadas. Pero en un lenguaje dinámico, donde cada llamada debe usar estos mecanismos, es un golpe de rendimiento severo ".

6

Así que la respuesta final es la siguiente:

invocación dinámica con callsites es más rápido que las llamadas a métodos tradicionales.

Lo único más rápido para el acceso de campo es acceder directamente al campo sin realizar ninguna llamada a métodos ni utilizar inseguros.

La respuesta es no invocar La dinámica no es lenta. Es rápido. Es muy rápido.

actualización más reciente:

Otra actualización (más tarde ese mismo domingo) me cambió el código para utilizar un callsite de invocación dinámica y que cambió el tiempo un poco.

(Todo funciona con JDK 1.8 construir 94)

java version "1.8.0-ea" 
Java(TM) SE Runtime Environment (build 1.8.0-ea-b94) 
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b36, mixed mode) 

1,000 carreras

description         duration in nanoseconds 
regular method call time     2095 
invoke dynamic method call time    1098 
reflection method call time     3104 

field method invoke dynamic call time  1165 
field method invoke reflection call time 689 
unsafe field access time     94 
direct field access (baseline)    92 

10.000 carreras

description        duration in nanoseconds 
regular method call time     68 
invoke dynamic method call time   43 
reflection method call time    202 

field method invoke dynamic call time  42 
field method invoke reflection call time 45 
unsafe field access time     87 
direct         86 

100.000 carreras

description         duration in nanoseconds 
regular method call time     70 
invoke dynamic method call time    44 
reflection method call time     249 

field method invoke dynamic call time  45 
field method invoke reflection call time 47 
unsafe field access time     88 
direct          36 

1.000.000 carreras

description         duration in nanoseconds 
regular method call time     11 
invoke dynamic method call time    6 
reflection method call time     12 

field method invoke dynamic call time  6 
field method invoke reflection call time 4 
unsafe field access time     1 
direct          0 

10.000.000 carreras

description         duration in nanoseconds 
regular method call time     9 
invoke dynamic method call time    6 
reflection method call time     25 

field method invoke dynamic call time  6 
field method invoke reflection call time 4 
unsafe field access time     1 
direct          0 

100.000.000 carreras

description         duration in nanoseconds 
regular method call time     9 
invoke dynamic method call time    6 
reflection method call time     12 

field method invoke dynamic call time  6 
field method invoke reflection call time 4 
unsafe field access time     1 
direct          0 

código actualizado que utiliza la llamada-s ite con invocación dinámica

//fieldName is the reflection field (example below how to look it up and change its access) 
MethodHandle methodHandleFieldDirect = lookup.unreflectGetter(fieldName); 
CallSite callSiteField = new ConstantCallSite(methodHandleFieldDirect); 
methodHandleFieldDirect = callSiteField.dynamicInvoker(); 
name = (String) methodHandleFieldDirect.invokeExact(new Employee()); 


//Lookup invoke dynamic 
methodType = MethodType.methodType(String.class); 
methodHandle = lookup.findVirtual(Employee.class, "getName", methodType); 
CallSite callSiteMethod = new ConstantCallSite(methodHandleFieldDirect); 
methodHandle = callSiteMethod.dynamicInvoker(); 

Para ver el resto, debes ver la entrada del blog. Hay ejemplos de uso inseguro, reflexión, llamadas, dinámica de invocación y el resto. :)

Los sitios de llamadas son importantes para acelerar la dinámica de invocación.

http://rick-hightower.blogspot.com/2013/10/java-invoke-dynamic-examples-java-7.html

Update (actualización anterior):

Saqué el código hash y el recuento de código, que añadí porque la reflexión fue tan rápido que pensé que los bucles se estaban compilados JIT por completo de alguna manera:

de 10 millones de carreras con recuento/código hash removidos employee.getName()
  • de llamada método regular de tiempo = 25 nanosegundos
  • invocar tiempo de llamada método dinámico = 18 nanosegundos **
  • reflexión de llamada método de tiempo = 36 nanosegundos
  • método
  • campo invocar llamada dinámica en tiempo = 8 nanosegundos método
  • campo de llamada reflexión tiempo = 6 nanosegundos
10 mil carreras con el empleado count/hashcode eliminado.getName()
  • de llamada método regular de tiempo = 70 nanosegundos **
  • invocar tiempo de llamada método dinámico = 84 nanosegundos
  • tiempo método
  • reflexión llamada = 211 nanosegundos
  • método
  • campo invocar tiempo llamada dinámica = 153 nanosegundos
  • campo método de llamada reflexión tiempo = 40 nanosegundos

Hay una mejora en el rendimiento sobre la reflexión, pero es Complicado. Depende de cuántas veces hagas las llamadas. Probablemente solo le importe si el código está en un círculo cerrado.

Los puntos de referencia que he visto recientemente muestran una mejora de 15 veces sobre la reflexión simple, y solo 2,5 veces más lenta que invocar un método de la manera normal. Pero ya conoces el viejo dicho, no creas nada y solo 1/2 de lo que lees.

Creo que lo probaría.

He estado bromeando con la reflexión e invocado la dinámica por un momento. see A short write up on invoke dynamic.

Éstos son los resultados que consigo que utilizan JDK 1.8 build 94.

un millón de llamadas (resultados están en nano segundos):

10.000 miles de llamadas

regular method call time  = 103 
invoke dynamic method call time = 116 
reflection method call time  = 252 

100.000 llamadas (después del calentamiento).

regular method call time  = 46 
invoke dynamic method call time = 112 
reflection method call time  = 171 

1.000.000 llamadas

regular method call time  = 23 
invoke dynamic method call time = 35 
reflection method call time  = 30 

reflexión es más rápido en 1 M de invocar dinámico. Ummm ... Odd.

10.000.000 llamadas

regular method call time  = 34 
invoke dynamic method call time = 24 
reflection method call time  = 43 

Ahora invocan dinámica es más rápido que una llamada a un método regular!

Ahora 100.000.000

regular method call time  = 22 
invoke dynamic method call time = 24 
reflection method call time  = 28 

En este punto, el compilador JIT lleva todo el dolor de distancia. Si no puede dedicarle un extra de 2 a 6 nano segundos, entonces necesita tener algunas habilidades de afrontamiento.

Este es el código para recrear las pruebas (también siga el siguiente enlace):

MethodHandles.Lookup lookup = MethodHandles.lookup(); 
    Class thisClass = lookup.lookupClass(); // (who am I?) 

    MethodType methodType; 
    MethodHandle methodHandle; 

crear un objeto empleado.

Employee employee = new Employee(); 

Busque getName con invoke dynamic (consulte el blog para obtener más ejemplos, enlace más arriba).

//Lookup invoke dynamic 
    methodType = MethodType.methodType(String.class); 
    methodHandle = lookup.findVirtual(Employee.class, "getName", methodType); 
    name = (String) methodHandle.invokeExact(new Employee()); 
    System.out.println("invoke dynamic " + name); 

Busque Employee.getName with reflection.

//Lookup reflection 
    Method method = Employee.class.getMethod("getName", new Class<?>[]{}); 
    name = (String) method.invoke(new Employee()); 
    System.out.println("reflection " + name); 

Hora de inicio, hora de finalización, número de iteraciones (tiempos), tiempos calculados, los sospechosos habituales.

long start = 0; 
    long end = 0; 
    long times = 100_000_000; 
    long regularTime; 
    long invokeDynamicTime; 
    long reflectionTime; 
    long count=0; 

Ahora vamos a calentar la JVM.

//warm up 
    for (int index =0 ; index < times; index++) { 
     employee.getName(); 
     name = (String) methodHandle.invokeExact(employee); 
     name = (String) method.invoke(employee); 
    } 

Calculemos la invocación de método regular.

start = System.nanoTime(); 
    for (int index =0 ; index < times; index++) { 
     name = employee.getName(); 
     count += name.hashCode(); 
    } 
    count=0; 
    end = System.nanoTime(); 
    regularTime = end - start; 
    System.out.printf("regular method call time  = %d\n", regularTime/times); 

P.S. Añadí el conteo, por lo que mi código no sería borrado de alguna manera.

Ahora calculemos invokeDyanmic time.

start = System.nanoTime(); 
    for (int index =0 ; index < times; index++) { 
     name = (String) methodHandle.invokeExact(employee); 
     count += name.hashCode(); 
    } 
    count=0; 
    end = System.nanoTime(); 
    invokeDynamicTime = end - start; 

    System.out.printf("invoke dynamic method call time = %d\n", invokeDynamicTime/times); 

Ahora calculemos el tiempo de reflexión.

start = System.nanoTime(); 
    for (int index =0 ; index < times; index++) { 
     name = (String) method.invoke(employee); 
     count += name.hashCode(); 
    } 
    count=0; 
    end = System.nanoTime(); 
    reflectionTime = end - start; 
    System.out.printf("reflection method call time  = %d\n", reflectionTime/times); 

He decidido agregar uno más a la mezcla. Si realmente quiere la propiedad, ¿qué ocurre si accede al campo directamente?

10.000

regular method call time     = 109 
invoke dynamic method call time   = 115 
reflection method call time    = 236 
field method invoke dynamic call time  = 178 
field method reflection call time   = 709 

100_000

regular method call time     = 49 
invoke dynamic method call time   = 118 
reflection method call time    = 312 
field method invoke dynamic call time  = 75 
field method reflection call time   = 158 

1_000_000

regular method call time     = 28 
invoke dynamic method call time   = 41 
reflection method call time    = 30 
field method invoke dynamic call time  = 11 
field method reflection call time   = 18 

10_000_000

regular method call time     = 28 
invoke dynamic method call time   = 41 
reflection method call time    = 30 
field method invoke dynamic call time  = 11 
field method reflection call time   = 18 

100_000_000

regular method call time     = 40 
invoke dynamic method call time   = 25 
reflection method call time    = 44 
field method invoke dynamic call time  = 10 
field method reflection call time   = 9 

Ok, aquí está el código para el acceso de campo que es 4 veces más rápido que el uso de employee.getName().

long start = 0; 
    long end = 0; 
    long times = 10_000_000; 
    long regularTime; 
    long invokeDynamicTime; 
    long reflectionTime; 
    long invokeDynamicTimeUsingField; 
    long fieldDirect; 

    long count=0; 

    //warm up 
    for (int index =0 ; index < times; index++) { 
     employee.getName(); 
     name = (String) methodHandle.invokeExact(employee); 
     name = (String) method.invoke(employee); 
     name = (String) methodHandleFieldDirect.invokeExact(employee); 
    } 


    start = System.nanoTime(); 
    for (int index =0 ; index < times; index++) { 
     name = employee.getName(); 
     count += name.hashCode(); 
    } 
    count=0; 
    end = System.nanoTime(); 
    regularTime = end - start; 
    System.out.printf(" regular method call time     = %d\n", regularTime/times); 

    start = System.nanoTime(); 
    for (int index =0 ; index < times; index++) { 
     name = (String) methodHandle.invokeExact(employee); 
     count += name.hashCode(); 
    } 
    count=0; 
    end = System.nanoTime(); 
    invokeDynamicTime = end - start; 

    System.out.printf(" invoke dynamic method call time   = %d\n", invokeDynamicTime/times); 

    start = System.nanoTime(); 
    for (int index =0 ; index < times; index++) { 
     name = (String) method.invoke(employee); 
     count += name.hashCode(); 
    } 
    count=0; 
    end = System.nanoTime(); 
    reflectionTime = end - start; 
    System.out.printf(" reflection method call time    = %d\n", reflectionTime/times); 


    start = System.nanoTime(); 
    for (int index =0 ; index < times; index++) { 
     name = (String) methodHandleFieldDirect.invokeExact(employee); 
     count += name.hashCode(); 
    } 
    count=0; 
    end = System.nanoTime(); 
    invokeDynamicTimeUsingField = end - start; 
    System.out.printf(" field method invoke dynamic call time  = %d\n", invokeDynamicTimeUsingField/times); 

    // 
    start = System.nanoTime(); 
    for (int index =0 ; index < times; index++) { 
     name = (String) fieldName.get(employee); 
     count += name.hashCode(); 
    } 
    count=0; 
    end = System.nanoTime(); 
    fieldDirect = end - start; 
    System.out.printf(" field method reflection call time   = %d\n", fieldDirect/times); 

} 

Ahora el campo de la reflexión real/invocar código dinámico:

Employee employee = new Employee(); 

    fieldName = null; 
    for (Field field : Employee.class.getDeclaredFields()) { 
     if (field.getName().equals("name")) { 
      fieldName = field; 
      fieldName.setAccessible(true); 
      break; 
     } 
    } 
    MethodHandle methodHandleFieldDirect = lookup.unreflectGetter(fieldName); 
    name = (String) methodHandleFieldDirect.invokeExact(new Employee()); 
    System.out.println("method handle for field direct " + name); 

    //Lookup invoke dynamic 
    methodType = MethodType.methodType(String.class); 
    methodHandle = lookup.findVirtual(Employee.class, "getName", methodType); 
    name = (String) methodHandle.invokeExact(new Employee()); 
    System.out.println("invoke dynamic " + name); 

    //Lookup reflection 
    Method method = Employee.class.getMethod("getName", new Class<?>[]{}); 
    name = (String) method.invoke(new Employee()); 
    System.out.println("reflection " + name); 
Cuestiones relacionadas