Приходилось ли вам разбирать выражение? Рисовать график функции по введенной с клавиатуры пользователем строке?
Согласитесь, занятие приносит больше головной боли, чем радости от результата. Возможно вы знакомы с библиотеками antlr или javacc, тогда вы отделаетесь малой кровью. Но приобретете хвост из некрасивых сгенерированных классов, который как можно быстрее скроете от посторонних глаз в самом дальнем пакете.
Написав вчера о cglib, я заметил в документации главу о модификации байт-кода. И само собой напрашивается вопрос, а можно ли в runtime заставить класс выполнять, то что очень хочется, а не то что хочет класс?
Я честно взялся за изучение документации и быстро вышел на популярный редактор байт-кода asm. Однако к моему разочарованию модифицировать байт-код можно только неплохо зная и разбираясь в нём. Точнее это означает, что если вам необходимо вернуть результат выполнения «x * x + x /2» — вы должны скомпилировать это выражение в инструкции.
Не желая писать компилятор, я всё-таки сформулировал запрос к google и нашёл, то что искал
К моему счастью подобный компилятор уже реализован в другом редакторе байт-кода javassist. К слову, javassist более документирован, однако в open-source community бытует мнение, что cglib много быстрее. В нашем случае скорость создания класса, не такая такая важная составляющая, как скорость выполнения его методов. Итак, что же требуется сделать:
Создание класса:
Всё! В наших руках объект класса, реализующий интерфейс Evaluator, который вернёт нам x * x + x / 2
Что ж, теперь не плохо было бы сравнить производительность. Спасибо TheShade за комментарий, я немного модифицировал Timer, и использовал его для тестирования. Результат тестирования, показывающий, что оба метода выполняются за одно и тоже время:
Класс Evaluation.java — Сравнение двух способов
Класс Timer.java — Замер скорости и статистика
Согласитесь, занятие приносит больше головной боли, чем радости от результата. Возможно вы знакомы с библиотеками antlr или javacc, тогда вы отделаетесь малой кровью. Но приобретете хвост из некрасивых сгенерированных классов, который как можно быстрее скроете от посторонних глаз в самом дальнем пакете.
Написав вчера о cglib, я заметил в документации главу о модификации байт-кода. И само собой напрашивается вопрос, а можно ли в runtime заставить класс выполнять, то что очень хочется, а не то что хочет класс?
Я честно взялся за изучение документации и быстро вышел на популярный редактор байт-кода asm. Однако к моему разочарованию модифицировать байт-код можно только неплохо зная и разбираясь в нём. Точнее это означает, что если вам необходимо вернуть результат выполнения «x * x + x /2» — вы должны скомпилировать это выражение в инструкции.
Не желая писать компилятор, я всё-таки сформулировал запрос к google и нашёл, то что искал
К моему счастью подобный компилятор уже реализован в другом редакторе байт-кода javassist. К слову, javassist более документирован, однако в open-source community бытует мнение, что cglib много быстрее. В нашем случае скорость создания класса, не такая такая важная составляющая, как скорость выполнения его методов. Итак, что же требуется сделать:
Copy Source | Copy HTML
- public interface Evaluator {
- public double eval(double x);
- }
Создание класса:
Copy Source | Copy HTML
- ClassPool pool = ClassPool.getDefault();
-
- CtClass evalClass = pool.makeClass("Formula");
- evalClass.setInterfaces(
- new CtClass[]{
- pool.makeClass("com.micro.bench.Evaluator")
- });
-
- String expession = "x * x + x / 2";
-
- evalClass.addMethod(
- CtNewMethod.make(
- "public double eval (double x) { return (" + expession + ") ; }",
- evalClass));
-
- Class clazz = evalClass.toClass();
- runtime = (Evaluator) clazz.newInstance();
Всё! В наших руках объект класса, реализующий интерфейс Evaluator, который вернёт нам x * x + x / 2
Что ж, теперь не плохо было бы сравнить производительность. Спасибо TheShade за комментарий, я немного модифицировал Timer, и использовал его для тестирования. Результат тестирования, показывающий, что оба метода выполняются за одно и тоже время:
total| amount| last| last 5| last 10| avg| dev| operation 6856.00| 20.00| 351.00| 343.00| 341.00| 342.80| 1.24| .. runtime calc 6874.00| 20.00| 337.00| 343.00| 344.00| 343.70| 1.03| .. compile-time calc
- total — всего мс
- amount — количество запусков шт
- last — последний мс
- last 5 — среднее по последним 5 запускам мс
- last 10 — среднее по последним 10 запускам мс
- avg — среднее вообще мс
- dev — отклонение
Класс Evaluation.java — Сравнение двух способов
Класс Timer.java — Замер скорости и статистика