Подтвердить что ты не робот

Любой способ дальнейшей оптимизации вызова Java-рефлексивного метода?

Мне интересно, есть ли какие-либо дополнительные оптимизации, которые я могу реализовать для улучшения скорости рефлексивных вызовов в Java. Не то, чтобы производительность была запретительной, но я получаю воли, думая о какой-то части кода в библиотеке, которую пишу, где-то внедряюсь в узком цикле.

Рассмотрим метод утилиты, который должен отражать рефлексивно:

public static Object invoke(Object targetObject, String methodName, Object[] arguments, Class<?>[] signature)

Основная операция

return method.invoke(targetObject, arguments);

Как оптимизация производительности, я кэширую метод, используя хэш класса целевого объекта, имя метода и подпись (код которого может использовать некоторое улучшение), но помимо этого, есть ли что-нибудь еще, что я могу сделать? Я слышал ссылки на некоторые ранние версии InvokeDynamic, которые звучат многообещающе, но я только предположил, что они, вероятно, еще не применимы, и я отказался от своих собственных манипуляций с байтовым кодом, поскольку я хотел бы, чтобы утилита была простой (но быстро).

Приветствия.

4b9b3361

Ответ 1

Комментарии ниже относятся к реализации Sun, в частности OpenJDK 6. Ваш пробег может отличаться в зависимости от других реализаций платформы Java.

java.lang.Class выполняет некоторое кэширование, поэтому реализация собственного кеша может не сильно улучшить ситуацию. Выполняйте временные тесты с ручным кэшированием и без него.

Также оптимизирован механизм фактического вызова. Первые 15 запусков (по умолчанию) вашего отраженного метода вызывается с использованием JNI; после этого генерируется байт-код и вызов этого отраженного метода будет выполняться идентично вызову этого метода непосредственно в Java-коде.

Ответ 2

Я провел несколько тестов вокруг ответа Криса Джеттера-Янга и использовал опцию verbose, я определенно заметил, что компилятор предпринимает некоторые действия вокруг 15-го вызова. Трудно сказать, есть ли большой дифференциал производительности без более сложного теста, но он убедителен. Здесь вывод:

Test# 0
Test# 1
Test# 2
Test# 3
Test# 4
Test# 5
Test# 6
Test# 7
Test# 8
Test# 9
Test# 10
Test# 11
Test# 12
Test# 13
Test# 14
[Loaded sun.reflect.ClassFileConstants from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.AccessorGenerator from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.MethodAccessorGenerator from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ByteVectorFactory from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ByteVector from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ByteVectorImpl from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ClassFileAssembler from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.UTF8 from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded java.lang.Void from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.Label from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.Label$PatchInfo from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded java.util.AbstractList$Itr from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.MethodAccessorGenerator$1 from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ClassDefiner from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.ClassDefiner$1 from C:\jdk1.5.0_06\jre\lib\rt.jar]
[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]
Test# 15
Test# 16
Test# 17

Я полагаю, что бизнес InvokeDynamic не привлекает слишком много разработчиков на основе ускорения/устранения ошибок.

Спасибо Крису.

Ответ 3

Вы обязательно захотите отразить объект метода только один раз (вероятно, как частный статический) и использовать его для вызова, а не отражать его каждый раз. Не беспокойтесь о карте кэша, если вы не знаете имя во время компиляции.

Если это разумно в вашем контексте, вы можете захотеть висеть и повторно использовать объект массива аргументов (не забудьте обнулить элементы массива при выходе метода, чтобы предотвратить временное ингибирование GC).

Если всегда вызывается с тем же набором (очень маловероятно), вы можете висеть на parms и (массив аргументов с его значениями) повторно использовать их.

Я бы не попытался ничего кроме этого по причинам, уже приведенным в других ответах.

Ответ 4

Преждевременная оптимизация, как правило, плохая. Независимо от того, ваша производительность будет во много раз больше, чем у динамического языка, поэтому я действительно не буду беспокоиться об этом, не документируя тот факт, что он использует отражение и, следовательно, может оказаться неоптимальным.

Кроме того, есть хорошая вероятность, что либо сейчас, либо в будущем Java будет оптимизировать байт-код, чтобы вызов не стоил ничего больше, чем вызов метода при использовании в цикле. Ваша "Оптимизация" может на самом деле помешать компиляторам сделать что-то подобное. (Я знаю, что я расплывчата, но это произошло - ЛОТЬ).

Ответ 5

Если стоимость вызова составляет менее 10% стоимости того, что происходит в методе, вряд ли стоит беспокоиться.

Вы можете определить, что, запустив его 10 ^ 6 раз в цикле, с и без мужества подпрограммы. Время это с секундомером, поэтому секунды переводятся на микросекунды.