如何提高 Field.set 的性能(可能使用 MethodHandles)? [英] How can I improve performance of Field.set (perhap using MethodHandles)?

查看:19
本文介绍了如何提高 Field.set 的性能(可能使用 MethodHandles)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一些调用 Field.setField.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)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆