Наскільки щвидко працює Java Reflection API

Опубліковано: 2009-07-13   00:21:32

JavaМоя попередня замітка присвячена викорисатанню Java Reflection API. На основі цієї технології працює багато фішок мови Java. Наприклад Serialization та Enterprise Java Beans. Фактично всі вони полегшують дуже сильно життя розробнику ПО з використанням Java. Однак, вже доволі давно я прочитав в Developing without EJB про те, що все що базується на основі цієї технології працює не дуже швидко. Тести я дуже люблю, тому вирішив перевірити, наскільки це твердження правдиве. А незодавно в мене з'явилась хвилинка часу, для цього.

Методика тестування

За основу для написання тесту я взяв свій невадалий тест для перевірки швикдості виклику методу в java.

Однак тоді виникла проблема з тим, що JIT від Sun дуже добре передбачає такі речі, як розрахунки та виконує їх миттєво. Тому в якості методу, який я викликаю, я вирішив використовувати щось складніше ніж простий інкремент. Вирішив використовувати суму випадкових чисел, де кожне наступне число отримується за формулою:

rand = (max*rand+min)%(max-min);

Перший член такоъ послідовності береться на основі миттєвого таймштампу. Такий генератор випадкових чисел хоч і є дуже брудним, однак працює доволі непогано для широкого діапазону значень max та min

Для тесту було написано такий клас:

public class MethodCall {
/** Constants */
static final int times = 20;
static final int count = 1000000;
static final int min = 1;
static final int max = 2000000;
/** Variables */
int summ = 0;
int rand = 1;
/** Method that return's random value */
public void method() {
rand = (max*rand+min)%(max-min);
summ += rand;
}
void std() {
method();
}
void reflected() {
try {
this.getClass().getMethod("method").invoke(this);
} catch(Exception exception) {
exception.printStackTrace();
System.out.println(exception.getMessage());
}
}
public static void main(String [] args) {
// Clear test
MethodCall caller = new MethodCall();
long beg, end, full = 0;
for (int k = 0; k < times; k++) {
beg = System.currentTimeMillis();
for (int i = 0; i < count; i++)
caller.method();
end = System.currentTimeMillis();
System.out.println("Method call: "+(end-beg));
full += end-beg;
}
System.out.println("Average value: "+(full/times));
// Test standart method call
caller = new MethodCall();
full = 0;
for (int k = 0; k < times; k++) {
beg = System.currentTimeMillis();
for (int i = 0; i < count; i++)
caller.std();
end = System.currentTimeMillis();
System.out.println("Usual method call: "+(end-beg));
full += end-beg;
}
System.out.println("Average value: "+(full/times));
// Test reflection method call
caller = new MethodCall();
full = 0;
for (int k = 0; k < times; k++) {
beg = System.currentTimeMillis();
for (int i = 0; i < count; i++)
caller.reflected();
end = System.currentTimeMillis();
System.out.println("Reflected method call: "+(end-beg));
full += end-beg;
}
System.out.println("Average value: "+(full/times));
}
private MethodCall() {
summ = 0;
rand = (int) System.currentTimeMillis();
}
}

Таким чином я проводжу три тести: перший - прямий виклик методу, другий - виклик методу через ынший метод, і третій - виклик методу через Reflection API. Кожен тест виконується двадцять разів, в кожному з яких виконується по одному мільйону викликів. Ну і головним показником буде в результаті сердній час.

Що до генерації випадкових чисел, то як показали результати, максимальне значення випадкової величини має бути більшим ніж кількітсь викликів - інакше наскільки я здогадуюсь JIT-компілятор передбачає напаеред всі значення які можуть бути отримані.

Результати тесту

Ну з першоюї хитрістю java впоралась просто чудово. На перших двох тестах сердній час склав всього 40 мілісекунд. А от швикдість виконання тесту з Reflection не порадувала - 2900 мс, що приблизно в 70 повільніше. Тобто піданні швидкодії є помітним.

А що це значить з точки зору практичного використання? В більшості випадків Ви не помітите жодної різниці між продуктом написаним з використанням Reflection для виклику методів класів та прямим викликом. Для того, щоб помітити різницію, треба щоб на кожну дію виконувались тисячі звернень до можливостей reflection. Хоча якщо є час та бажання писати без цієї технології, чи потрібно отримувати максимальну швибкодію будь-якою ціною, то у Вас й справді є підстави відмовитись від неї :)

Сподіваюсь буде корисним та щиро дякую за увагу!

Теги: java , reflection , тест
Коментарі: 1
 

Коментарі:

Александр2009-11-27 06:54:15 :

Тесты автора статьи прекрасно демонстрируют как можно используя не совсем верный подход манипулировать мнением читателей.

Хотелось бы порекомендовать автору углубить свои знания в области тестирования, в самой технологии Java в том числе и Reflection API.

А теперь конкретные замечания по тесту:

1. Не сказано ничего о платформе (сервер, клиент, windows, linux?)

2. Не показано в тесте влияние различных факторов на скорость: количество аргументов метода, вызов интерфейсного, статического методов и т.д.

3. Самое главное. Результаты 3-го теста не имеют никакого отношения к скорости Reflection API.

И по 3-ьему пункту более подробно.

Использование vararg методов на первый взгляд мне показалось неэффективным, но более подробные тесты показали что особого влияния на первом этапе vararg вызов не оказывает - хвала инжинерам Sun-а :). Конечно, будь в методе хоть один аргумент, результаты тестирования были бы еще хуже - большУю часть времени JVM просто бы собирала мусор оставшийся от вызова vararg метода.

Самым главным тормозом является поиск метода. Закешируйте поиск метода и получите минимум 20-ти кратный прирост производительности. Более того, при таком подходе станет уже существенным влияние vararg вызова. Избавьтесь от vararg на этом этапе и получите еще 5-10% прироста производительности.

В итоге у меня получилось что используя даже обычный Reflection API с умом, мы получаем замедление в 2 раза, но практически неограниченную гибкость. С ростом сложности метода, влияние reflection на скорость его выполнения падает что позволяет использовать эту технологию боле интенсивно.

Я бы посоветовал автору углубится в эту очень интересную тему, почитать об особенностях JVM, о грядущих изменениях в Java7. Еще один аспект использования reflection - это скриптовые языки такие как jython, jruby и конечно же pnuts. Изучая довольно простой, но мощный скриптовый язык pnuts автор сможет лучше понять в каком состоянии сегодня находится reflection в Java.

 

Додати коментар

user

email

url

text

Повідомляти про новікоментарі