所以最后的答案是这样的:
使用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)
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
更新了使用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();
Run Code Online (Sandbox Code Playgroud)
要查看其余内容,您必须查看博客条目.有一些使用不安全,反射,调用,调用动态和其他的例子.:)
Callsites对于加快调用动态非常重要.
http://rick-hightower.blogspot.com/2013/10/java-invoke-dynamic-examples-java-7.html
更新(旧版更新):
我拿出了hashCode和count代码,我添加了因为反射太快了以至于我认为循环完全以某种方式得到了JITTed:
已删除count/hashcode的1000万次运行employee.getName()与反射相比,性能有所提高但很复杂.这取决于您拨打电话的次数.您可能只关心代码是否处于紧密循环中.
我最近看到的基准测试显示比普通反射提高了15倍,而且比普通方式调用方法慢了2.5倍.但你知道那句老话,不要相信你所听到的,只读你所读的一半.
我想我会尝试一下.
我现在一直在用反射和invokedynamic搞砸了.请参阅 关于调用动态的简短介绍.
以下是我使用JDK 1.8 build 94获得的结果.
一百万次通话(结果以纳秒为单位):
一万次电话
regular method call time = 103
invoke dynamic method call time = 116
reflection method call time = 252
Run Code Online (Sandbox Code Playgroud)
100,000次通话(热身后).
regular method call time = 46
invoke dynamic method call time = 112
reflection method call time = 171
Run Code Online (Sandbox Code Playgroud)
1,000,000个电话
regular method call time = 23
invoke dynamic method call time = 35
reflection method call time = 30
Run Code Online (Sandbox Code Playgroud)
反射在1M时比调用动态更快.嗯...奇怪.
10,000,000个电话
regular method call time = 34
invoke dynamic method call time = 24
reflection method call time = 43
Run Code Online (Sandbox Code Playgroud)
现在调用动态比常规方法调用快!
现在100,000,000
regular method call time = 22
invoke dynamic method call time = 24
reflection method call time = 28
Run Code Online (Sandbox Code Playgroud)
此时,JIT编译器消除了所有的痛苦.如果你不能多花2到6纳秒,那么你需要有一些应对技巧.
以下是重新创建测试的代码(也按照上面的链接):
MethodHandles.Lookup lookup = MethodHandles.lookup();
Class thisClass = lookup.lookupClass(); // (who am I?)
MethodType methodType;
MethodHandle methodHandle;
Run Code Online (Sandbox Code Playgroud)
创建员工对象.
Employee employee = new Employee();
Run Code Online (Sandbox Code Playgroud)
通过调用动态查找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);
Run Code Online (Sandbox Code Playgroud)
使用反射查找Employee.getName.
//Lookup reflection
Method method = Employee.class.getMethod("getName", new Class<?>[]{});
name = (String) method.invoke(new Employee());
System.out.println("reflection " + name);
Run Code Online (Sandbox Code Playgroud)
开始时间,结束时间,迭代次数(次数),计算次数,通常的嫌疑人.
long start = 0;
long end = 0;
long times = 100_000_000;
long regularTime;
long invokeDynamicTime;
long reflectionTime;
long count=0;
Run Code Online (Sandbox Code Playgroud)
现在让我们热身JVM.
//warm up
for (int index =0 ; index < times; index++) {
employee.getName();
name = (String) methodHandle.invokeExact(employee);
name = (String) method.invoke(employee);
}
Run Code Online (Sandbox Code Playgroud)
让我们计算常规方法调用.
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);
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
现在让我们计算一下反射时间.
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);
Run Code Online (Sandbox Code Playgroud)
我决定再添加一个.如果你真的只想要这个属性,那么如果你直接访问该字段会怎样.
万
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
好的,这是字段访问的代码,比使用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);
}
Run Code Online (Sandbox Code Playgroud)
现在实际的字段反射/调用动态代码:
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);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2241 次 |
| 最近记录: |