簡體   English   中英

java中的動態調用功能

[英]Dynamic Invoke feature in java

如果我們使用Dynamic Invoke ,那么應用程序中是否有任何性能改進? 如果不是它的優勢是什么?

所以最后的答案是這樣的:

使用callites調用動態比傳統方法調用更快。

對於字段訪問來說,唯一更快的是直接訪問字段而無需任何方法調用並使用不安全。

答案是沒有調用動態不慢。 它很快。 它非常快。

更新更新:

另一個更新(在星期天晚些時候)我更改了代碼以使用調用站點進行動態調用,這改變了時間。

(所有運行JDK 1.8 build 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)

1000次運行

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次運行

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次運行

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次運行

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次運行

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次運行

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

更新了使用call-site並調用動態的代碼

//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();

要查看其余內容,您必須查看博客條目。 有一些使用不安全,反射,調用,調用動態和其他的例子。 :)

Callsites對於加快調用動態非常重要。

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

更新(舊版更新):

我拿出了hashCode和count代碼,我添加了因為反射太快了以至於我認為循環完全以某種方式得到了JITTed:

已刪除count / hashcode的1000萬次運行employee.getName()
  • 常規方法調用時間= 25納秒
  • 調用動態方法調用時間= 18納秒**
  • 反射方法調用時間= 36納秒
  • field方法調用動態調用時間= 8納秒
  • 場方法反射調用時間= 6納秒
刪除了count / hashcode的10,000次運行employee.getName()
  • 常規方法調用時間= 70納秒**
  • 調用動態方法調用時間= 84納秒
  • 反射方法調用時間= 211納秒
  • field方法調用動態調用時間= 153納秒
  • 場方法反射調用時間= 40納秒

與反射相比,性能有所提高但很復雜。 這取決於您撥打電話的次數。 您可能只關心代碼是否處於緊密循環中。

我最近看到的基准測試顯示比普通反射提高了15倍,而且比普通方式調用方法慢了2.5倍。 但你知道那句老話,不要相信你所聽到的,只讀你所讀的一半。

我想我會嘗試一下。

我現在一直在用反射和invokedynamic搞砸了。 請參閱關於調用動態的簡短介紹。

以下是我使用JDK 1.8 build 94獲得的結果。

一百萬次通話(結果以納秒為單位):

10,000個電話

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

100,000次通話(熱身后)。

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

1,000,000個電話

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

反射在1M時比調用動態更快。 嗯...奇怪。

10,000,000個電話

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

現在調用動態比常規方法調用快!

現在100,000,000

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

此時,JIT編譯器消除了所有的痛苦。 如果你不能多花2到6納秒,那么你需要有一些應對技巧。

以下是重新創建測試的代碼(也按照上面的鏈接):

    //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);

通過調用動態查找getName(有關更多示例,請參閱博客,上面的鏈接)。

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

使用反射查找Employee.getName。

    //warm up
    for (int index =0 ; index < times; index++) {
        employee.getName();
        name = (String) methodHandle.invokeExact(employee);
        name = (String) method.invoke(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);

現在讓我們熱身JVM。

  //warm up for (int index =0 ; index < times; index++) { employee.getName(); name = (String) methodHandle.invokeExact(employee); name = (String) method.invoke(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); 

PS我添加了計數,所以我的代碼不會以某種方式被淘汰。

現在讓我們計算invokeDyanmic時間。

  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); 

我決定再添加一個。 如果你真的只想要這個屬性,那么如果你直接訪問該字段會怎樣。

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

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

1_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

10_000_000

    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);

}

100_000_000

    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);

好的,這是字段訪問的代碼,比使用employee.getName()快4倍。

  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); } 

現在實際的字段反射/調用動態代碼:

  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); 

我想OP意味着InvokeDynamic,如http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html中提到的方法調用。 我從未使用過自己,但這篇文章看起來非常具有描述性

“對於執行一些反射調用的庫,性能差異可能無關緊要,特別是如果這些調用主要是為了在內存中動態設置一個靜態結構來進行正常調用。但是在動態語言中,每個調用都必須使用這些機制,是一個嚴重的性能損失。“

我認為你的意思是通過動態調用來反映。 反射的成本比普通方法調用略高。

反射有很多用法,例如我廣泛用於ui數據綁定。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM