创建BiConsumer作为没有反射的字段设置器 [英] Create BiConsumer as Field setter without reflection

查看:167
本文介绍了创建BiConsumer作为没有反射的字段设置器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试在其中一个脚本中获得最高性能,而无需进行重大重构.

I try to get the maximum performance in one of my scripts, without doing a major refactor.

我发现了使用反射从Field创建BiConsumer的方法.

I spotted method that creates a BiConsumer from a Field using reflection.

return (c, v) -> {
    try {
        field.set(c, v);
    } catch (final Throwable e) {
        throw new RuntimeException("Could not set field: " + field, e);
    }
};

反射具有缓慢的声誉. 所以我虽然可以使用方法句柄.

Reflection has the reputation of being slow. So I though I could use the method handles.

Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.unreflectSetter(field);
return (c, v) -> {
    try {
        mh.invoke(c, v);
    } catch (final Throwable e) {
        throw new RuntimeException("Could not set field: " + field, e);
    }
};

已经快了一点. 但是BiConsumerFunctionalInterface,可以通过某种方式生成.

Which is already a tiny bit faster. However BiConsumer is a FunctionalInterface which could just be generated somehow.

public static <C, V> BiConsumer<C, V> createSetter(final MethodHandles.Lookup lookup,
        final Field field) throws Exception {
    final MethodHandle setter = lookup.unreflectSetter(field);
    final CallSite site = LambdaMetafactory.metafactory(lookup,
            "accept",
            MethodType.methodType(BiConsumer.class),
            MethodType.methodType(void.class, Object.class, Object.class), // signature of method BiConsumer.accept after type erasure
            setter,
            setter.type()); // actual signature of setter
    return (BiConsumer<C, V>) site.getTarget().invokeExact();
}

但是我得到了一个我不太了解的异常

However then I get an Exception which I don't really understand

Exception in thread "main" java.lang.invoke.LambdaConversionException: Unsupported MethodHandle kind: putField org.sample.dto.TestDTO.name:(String)void
    at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:182)
    at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
    at org.sample.bench.MyBenchmark.createSetter(MyBenchmark.java:120)
    at org.sample.bench.MyBenchmark.main(MyBenchmark.java:114)

我必须以哪种方式正确生成该setter才能提高性能. (实际上未添加setter方法)

In which way do I have to generate that setter correctly to increase the performance. (without actually adding a setter method)

推荐答案

您可以使用调用程序MethodHandle:

public static <C, V> BiConsumer<C, V> createSetter(
                     MethodHandles.Lookup lookup, Field field) throws Throwable {
    final MethodHandle setter = lookup.unreflectSetter(field);
    MethodType type = setter.type();
    if(field.getType().isPrimitive())
        type = type.wrap().changeReturnType(void.class);
    final CallSite site = LambdaMetafactory.metafactory(lookup,
        "accept", MethodType.methodType(BiConsumer.class, MethodHandle.class),
        type.erase(), MethodHandles.exactInvoker(setter.type()), type);
    return (BiConsumer<C, V>)site.getTarget().invokeExact(setter);
}

由于LambdaMetafactory不允许为字段方法句柄生成函数实例,因此上面的代码为方法句柄创建了一个函数实例,等效于在字段访问器方法句柄上调用invokeExact.

Since the LambdaMetafactory does not allow to generate a function instance for the field method handle, the code above creates a function instance for a method handle equivalent to invoking invokeExact on the field accessor method handle.

在最坏的情况下,生成的代码与为执行方法句柄的invoke方法的手动调用的lambda表达式生成的代码没有区别,因此性能将与

In the worst case, the generated code would not differ from the code generated for the lambda expression performing a manual invocation of the method handle’s invoke method, so the performance would be on par with

public static <C, V> BiConsumer<C, V> createSetterU(
                     MethodHandles.Lookup lookup, Field field) throws Throwable {
    MethodHandle setter = lookup.unreflectSetter(field);
    return (c, v) -> {
        try {
            setter.invoke(c, v);
        } catch (final Throwable e) {
            throw new RuntimeException("Could not set field: " + field, e);
        }
    };
}

但是,在某些情况下,调用者方法句柄有可能稍微更有效.首先,它使用与invokeExact等效的选项,而不是与invoke等效的选项,由于类型擦除,通用代码无法使用.其次,当代码图中不涉及用户代码时,可能会有一些内部调整.

However, there are some chances that the invoker method handle is slightly more efficient in some scenarios. First, it uses the equivalent to invokeExact rather than invoke, an option, the generic code can’t use due to type erasure. Second, there might be some internal tweaks when no user code is involved in the code graph.

这篇关于创建BiConsumer作为没有反射的字段设置器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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