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

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

Рассмотрим вспомогательный метод для рефлексивного вызова:

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

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

return method.invoke(targetObject, arguments);

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

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

Ответы [ 5 ]

55 голосов
/ 06 января 2009

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

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

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

8 голосов
/ 09 января 2009

Я провел несколько тестов вокруг ответа Криса Джестера-Янга и, используя опции 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 не привлекает слишком много разработчиков на основе ускорения / устранения отражений.

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

1 голос
/ 06 января 2009

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

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

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

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

0 голосов
/ 09 января 2009

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

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

0 голосов
/ 06 января 2009

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

Кроме того, существует высокая вероятность того, что либо сейчас, либо в будущем Java будет оптимизировать байт-код, чтобы при использовании в цикле вызов не стоил ничего дороже вызова метода. Ваша «Оптимизация» может фактически препятствовать способности компиляторов делать что-то подобное. (Я знаю, что я расплывчатый, но это случилось - МНОГО).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...