Создать BiConsumer как установщик поля без отражения - PullRequest
0 голосов
/ 13 июня 2018

Я пытаюсь добиться максимальной производительности в одном из моих сценариев, не выполняя серьезного рефакторинга.

Я обнаружил метод, который создает BiConsumer из поля с использованием отражения.

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

Отражение имеет репутацию медленного.Поэтому я подумал, что могу использовать дескрипторы метода.

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);
    }
};

Что уже немного быстрее.Однако BiConsumer - это FunctionalInterface, который может быть просто сгенерирован каким-либо образом.

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();
}

Однако тогда я получаю исключение, которого я не совсем понимаю

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)

Каким образомя должен генерировать этот сеттер правильно, чтобы увеличить производительность.(без фактического добавления метода установки)

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Вы можете использовать invoker MethodHandle:

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

Поскольку LambdaMetafactory не позволяет генерировать экземпляр функции для дескриптора метода поля, приведенный выше код создает экземпляр функции для методадескриптор, эквивалентный вызову invokeExact для дескриптора метода доступа к полю.

В худшем случае сгенерированный код не будет отличаться от кода, сгенерированного для лямбда-выражения, выполняющего ручной вызов дескриптора метода invokeметод, поэтому производительность будет на уровне

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);
        }
    };
}

Однако, есть некоторые шансы, что дескриптор метода invoker будет несколько более эффективным в некоторых сценариях.Во-первых, он использует эквивалент invokeExact, а не invoke, параметр, который универсальный код не может использовать из-за стирания типа.Во-вторых, могут быть некоторые внутренние изменения, когда в графе кода нет кода пользователя.

0 голосов
/ 23 июля 2018

Функционально MethodHandleProxies::asInterfaceInstance кажется хорошей подгонкой.Не уверен насчет производительности прокси-серверов дескрипторов производительности.

...