gpt4 book ai didi

java - Java中的动态调用功能

转载 作者:搜寻专家 更新时间:2023-10-31 19:50:03 24 4
gpt4 key购买 nike

如果我们使用Dynamic Invoke,那么应用程序中的性能是否有改善?如果不是,它的优势是什么?

最佳答案

所以最后的答案是这样的:

使用callite进行动态调用比传统方法调用要快。

进行字段访问的唯一更快的方法是直接访问字段,而无需任何方法调用并使用不安全的方法。

答案是没有调用动态并不慢。很快这是非常快的。

较新的更新:

另一个更新(此后的那个星期日)
我更改了代码,以将调用站点用于动态调用,这极大地改变了时间。

(所有运行在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)

1,000次运行
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

更新的代码将调用站点与动态调用一起使用
//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();

要查看其余内容,您必须查看博客条目。有使用不安全,反射,调用站点,调用动态和其余部分的示例。 :)

调用站点对于加快调用动态非常重要。

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

更新(旧更新):

我取出了hashCode和count代码,之所以添加它们是因为反射是如此之快,以至于我以为以某种方式完全将循环拖出了循环:

1000万次运行,其中删除了计数/哈希码employee.getName()

  • 常规方法调用时间= 25纳秒
  • 调用动态方法的调用时间= 18纳秒**
  • 反射方法调用时间= 36纳秒
  • 字段方法调用动态调用时间= 8纳秒
  • 字段方法反射调用时间= 6纳秒

  • 1万次运行,其中删除了计数/哈希码employee.getName()

  • 常规方法调用时间= 70纳秒**
  • 调用动态方法的调用时间= 84纳秒
  • 反射方法调用时间= 211纳秒
  • 字段方法调用动态调用时间= 153纳秒
  • 字段方法反射调用时间= 40纳秒

  • 与反射相比,性能有所提高,但是很复杂。这取决于您拨打电话的次数。您可能只在乎代码是否处于紧密循环中。

    我最近看到的基准测试显示,与普通反射相比,性能提高了15倍,而比普通方法调用速度慢了2.5倍。但是,您知道这句老话,不相信任何内容,只相信所读内容的1/2。

    我想我会尝试一下。

    我一直在鬼混反射和invokedynamic了一段时间。

    A short write up on invoke dynamic.

    这是我使用JDK 1.8 build 94获得的结果。

    一百万次通话(结果以毫微秒为单位):

    10,000万次调用
    regular method call time        = 103
    invoke dynamic method call time = 116
    reflection method call time = 252

    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纳秒的时间,那么您需要具备一些应对技巧。

    这是重新创建测试的代码(也请单击上面的链接):
        MethodHandles.Lookup lookup = MethodHandles.lookup();
    Class thisClass = lookup.lookupClass(); // (who am I?)

    MethodType methodType;
    MethodHandle methodHandle;

    创建一个雇员对象。
        Employee employee = new Employee();

    使用动态调用查找getName(有关更多示例,请参见博客,上面的链接)。
        //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);

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

    开始时间,结束时间,迭代次数(次数),计算时间,通常的嫌疑人。
        long start = 0;
    long end = 0;
    long times = 100_000_000;
    long regularTime;
    long invokeDynamicTime;
    long reflectionTime;
    long count=0;

    现在让我们预热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);

    P.S.我增加了计数,所以我的代码不会以某种方式被淘汰。

    现在让我们计算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);

    我决定再添加一种。如果您确实只想要该属性,那么直接访问该字段会怎样。

    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

    好的,这是字段访问的代码,它比使用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);

    关于java - Java中的动态调用功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4552979/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com