问题描述
我正在编写一些调用 Field.set
和 Field.get
很多很多数千次.显然这很慢,因为 reflection.
I'm writing some code that calls Field.set
and Field.get
many many thousands of times. Obviously this is very slow because of the reflection.
我想看看我是否可以使用 MethodHandle
.到目前为止,这是我所拥有的:
I want to see if I can improve performance using MethodHandle
in Java 7. So far here's what I have:
代替 field.set(pojo, value)
,我在做:
private static final Map<Field, MethodHandle> setHandles = new HashMap<>();
MethodHandle mh = setHandles.get(field);
if (mh == null) {
mh = lookup.unreflectSetter(field);
setHandles.put(field, mh);
}
mh.invoke(pojo, value);
然而,这似乎并不比使用反射的 Field.set 调用表现得更好.我在这里做错了吗?
However, this doesn't seem to perform better than the Field.set call using reflection. Am I doing something wrong here?
我使用 invokeExact
可能会更快,但是当我尝试使用它时,我得到了一个 java.lang.invoke.WrongMethodTypeException
.
是否有人成功地优化了对 Field.set 或 Field.get 的重复调用?
Has anyone successfully been able to optimize repeated calls to Field.set or Field.get?
推荐答案
2015-06-01:更新以反映@JoeC 对另一种句柄静态情况的评论.还更新到最新的 JMH 并在现代硬件上重新运行.结论几乎相同.
请进行适当的基准测试,可以说使用 JMH 并不难.一旦你这样做了,答案就显而易见了.它还可以展示 invokeExact
的正确使用(需要目标/源代码 1.7 才能编译和运行):
Please do proper benchmarking, it is arguably not that hard with JMH. Once you do that, the answer becomes obvious. It can also showcase the proper use of invokeExact
(requires target/source 1.7 to compile and run):
@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 上它产生:
On 1x4x2 i7-4790K, JDK 8u40, Linux x86_64 it yields:
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
和/或 Fields
不是静态已知的情况,例如从 Map
或类似的东西中提取.相反,static_*
情况是调用者静态已知的情况.
...which suggests MH are really much faster than Reflection in this particular case (this is because the access checks against the private field is done at lookup time, and not at the invocation time). dynamic_*
cases simulate the case when the MethodHandles
and/or Fields
are not statically known, e.g. pulled from Map<String, MethodHandle>
or something like it. Conversely, static_*
cases are those where the invokers are statically known.
请注意反射性能在 dynamic_*
情况下与 MethodHandles 相当,这是因为反射在 JDK 8 中进一步优化(因为实际上,您不需要访问检查来读取您的自己的字段),所以答案可能是只是"切换到 JDK 8 ;)
Notice the reflective performance is on par with MethodHandles in dynamic_*
cases, this is because reflection is heavily optimized further in JDK 8 (because really, you don't need the access check to read your own fields), so the answer may be "just" switching to JDK 8 ;)
static_*
情况甚至更快,因为 MethoHandles.invoke
调用被积极地内联.这消除了 MH 情况下的部分类型检查.但是,在反射案例中,仍然存在快速检查,因此滞后.
static_*
cases are even faster, because the MethoHandles.invoke
calls are aggressively inlined. This eliminates part of the type checking in MH cases. But, in reflection cases, there are still quick checks present, and therefore, it lags behind.
这篇关于如何提高 Field.set 的性能(可能使用 MethodHandles)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!