На моей работе у нас есть DSL для задания математических формул, который мы позже применяем к множеству точек (в миллионах).
На сегодняшний день мы создаем AST формулы и посещаем каждый узел, чтобы произвести то, что мы называем «оценщиком». Затем мы передаем этому вычислителю аргументы формулы, и для каждой точки он выполняет вычисления.
Например, у нас есть такая формула: x * (3 + y)
┌────┐
┌─────┤mult├─────┐
│ └────┘ │
│ │
┌──v──┐ ┌──v──┐
│ x │ ┌───┤ add ├──┐
└─────┘ │ └─────┘ │
│ │
┌──v──┐ ┌──v──┐
│ 3 │ │ y │
└─────┘ └─────┘
Наш оценщик будет генерировать объекты «Evaluate» для каждого шага.
Этот метод прост в программировании, но не очень эффективен.
Поэтому в последнее время я начал изучать дескрипторы методов, чтобы создать «составной» дескриптор метода для ускорения работы.
Что-то вроде этого: у меня есть класс "Арифметика" с:
public class Arithmetics {
public static double add(double a, double b){
return a+b;
}
public static double mult(double a, double b){
return a*b;
}
}
И при создании своего AST я использую MethodHandles.lookup (), чтобы напрямую обращаться к ним и составлять их. Что-то в этом роде, но в дереве:
Method add = ArithmeticOperator.class.getDeclaredMethod("add", double.class, double.class);
Method mult = ArithmeticOperator.class.getDeclaredMethod("mult", double.class, double.class);
MethodHandle mh_add = lookup.unreflect(add);
MethodHandle mh_mult = lookup.unreflect(mult);
MethodHandle mh_add_3 = MethodHandles.insertArguments(mh_add, 3, plus_arg);
MethodHandle formula = MethodHandles.collectArguments(mh_mult, 1, mh_add_3); // formula is f(x,y) = x * (3 + y)
К сожалению, я очень разочарован результатами. Например, фактическое построение дескриптора метода очень длинное (из-за вызовов MethodHandles :: insertArguments и других подобных функций композиции), а добавленное ускорение для оценки начинает иметь значение только после более чем 600 тысяч итераций.
При 10 млн итераций дескриптор метода начинает действительно сиять, но миллионы итераций не являются (пока?) Типичным вариантом использования. Мы больше в районе 10k-1M, где результат неоднозначен.
Кроме того, фактическое вычисление ускоряется, но не так сильно (~ 2-10 раз). Я ожидал, что эта штука будет работать немного быстрее ..
Так или иначе, я снова начал просматривать StackOverflow и увидел такие потоки LambdaMetafactory: https://stackoverflow.com/a/19563000/389405
И мне не терпится попробовать это. Но перед этим я хотел бы получить ваш ответ по некоторым вопросам:
Мне нужно уметь составлять все эти лямбды. MethodHandles предоставляет множество (медленных, одобрительных) способов сделать это, но я чувствую, что лямбды имеют более строгий «интерфейс», и я пока не могу понять, как это сделать. Ты знаешь как?
лямбда-выражения и дескрипторы методов довольно взаимосвязаны, и я не уверен, что получу значительное ускорение. Я вижу эти результаты для простых лямбд:
direct: 0,02s, lambda: 0,02s, mh: 0,35s, reflection: 0,40
но как насчет составных лямбд?
Спасибо ребята!
LambdaMetaFactory
является то, что он создает новый класс, что означает, что JVM оптимизирует его гораздо более агрессивно, чем дескрипторы методов, хранящиеся в локальных полях или полях экземпляра. Единственный другой способ достичь того же эффекта - использоватьUnsafe
для создания анонимных классов. Так что, может быть, просто сохраните свое дерево MH и преобразуйте его в класс с LMF в качестве последнего шага? Если эти вещи создаются динамически, вы также можете настроить параметры JVM, чтобы JIT могли работать раньше. Обычно они настроены на достижение устойчивого состояния, а затем мало меняются. - person the8472   schedule 08.06.2016LambdaMetaFactory
не поддерживает составные дескрипторы методов. Таким образом, вы можете создавать только экземпляры, которые делегируются существующему методу, что подразумевает, что вам нужно самостоятельно сгенерировать код для объединения узлов. В основном об этом и заключается мой ответ: как только вы узнаете, что вам нужны эти методы, вы можете создавать узлы без отражающих операций, используя функции исходного уровня Java 8. Затем LMF все еще можно использовать для интеграции определенных специализированных конечных узлов. - person Holger   schedule 09.06.2016