2015-06-01: ハンドルが静的である場合の別のケースに関する @JoeC のコメントを反映するように更新されました。また、最新の JMH に更新し、最新のハードウェアで再実行しました。結論はほぼ変わらない。
適切なベンチマークを行ってください。おそらくJMHではそれほど難しいことではありません。それができれば、答えは自ずと見えてきます。また、適切な使用法を示すこともできますinvokeExact
(コンパイルして実行するには、ターゲット/ソース 1.7 が必要です):
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class MHOpto {
private int value = 42;
private static final Field static_reflective;
private static final MethodHandle static_unreflect;
private static final MethodHandle static_mh;
private static Field reflective;
private static MethodHandle unreflect;
private static MethodHandle mh;
// We would normally use @Setup, but we need to initialize "static final" fields here...
static {
try {
reflective = MHOpto.class.getDeclaredField("value");
unreflect = MethodHandles.lookup().unreflectGetter(reflective);
mh = MethodHandles.lookup().findGetter(MHOpto.class, "value", int.class);
static_reflective = reflective;
static_unreflect = unreflect;
static_mh = mh;
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}
@Benchmark
public int plain() {
return value;
}
@Benchmark
public int dynamic_reflect() throws InvocationTargetException, IllegalAccessException {
return (int) reflective.get(this);
}
@Benchmark
public int dynamic_unreflect_invoke() throws Throwable {
return (int) unreflect.invoke(this);
}
@Benchmark
public int dynamic_unreflect_invokeExact() throws Throwable {
return (int) unreflect.invokeExact(this);
}
@Benchmark
public int dynamic_mh_invoke() throws Throwable {
return (int) mh.invoke(this);
}
@Benchmark
public int dynamic_mh_invokeExact() throws Throwable {
return (int) mh.invokeExact(this);
}
@Benchmark
public int static_reflect() throws InvocationTargetException, IllegalAccessException {
return (int) static_reflective.get(this);
}
@Benchmark
public int static_unreflect_invoke() throws Throwable {
return (int) static_unreflect.invoke(this);
}
@Benchmark
public int static_unreflect_invokeExact() throws Throwable {
return (int) static_unreflect.invokeExact(this);
}
@Benchmark
public int static_mh_invoke() throws Throwable {
return (int) static_mh.invoke(this);
}
@Benchmark
public int static_mh_invokeExact() throws Throwable {
return (int) static_mh.invokeExact(this);
}
}
1x4x2 i7-4790K、JDK 8u40、Linux x86_64 では、次の結果が得られます。
Benchmark Mode Cnt Score Error Units
MHOpto.dynamic_mh_invoke avgt 25 4.393 ± 0.003 ns/op
MHOpto.dynamic_mh_invokeExact avgt 25 4.394 ± 0.007 ns/op
MHOpto.dynamic_reflect avgt 25 5.230 ± 0.020 ns/op
MHOpto.dynamic_unreflect_invoke avgt 25 4.404 ± 0.023 ns/op
MHOpto.dynamic_unreflect_invokeExact avgt 25 4.397 ± 0.014 ns/op
MHOpto.plain avgt 25 1.858 ± 0.002 ns/op
MHOpto.static_mh_invoke avgt 25 1.862 ± 0.015 ns/op
MHOpto.static_mh_invokeExact avgt 25 1.859 ± 0.002 ns/op
MHOpto.static_reflect avgt 25 4.274 ± 0.011 ns/op
MHOpto.static_unreflect_invoke avgt 25 1.859 ± 0.002 ns/op
MHOpto.static_unreflect_invokeExact avgt 25 1.858 ± 0.002 ns/op
...これは、この特定のケースでは MH がリフレクションよりもはるかに高速であることを示唆しています (これは、プライベート フィールドに対するアクセス チェックが呼び出し時ではなくルックアップ時に行われるためです)。dynamic_*
ケースは、MethodHandles
and/orFields
が静的に認識されていない場合 (たとえば、プルされた場合など) をシミュレートしMap<String, MethodHandle>
ます。逆にstatic_*
、呼び出し元が静的にわかっているケースです。
場合によっては、リフレクションのパフォーマンスが MethodHandles と同等でdynamic_*
あることに注意してください。これは、リフレクションが JDK 8 でさらに大幅に最適化されているためです (実際には、独自のフィールドを読み取るためにアクセス チェックが必要ないため)。したがって、答えは「ちょうど」である可能性があります。 JDK 8に切り替えます;)
static_*
MethoHandles.invoke
呼び出しが積極的にインライン化されるため、ケースはさらに高速です。これにより、MH の場合の型チェックの一部が省略されます。ただし、リフレクションの場合は、まだクイック チェックが存在するため、遅れをとっています。