Перейти к основному содержанию

Примеры

caution

Помните, что ActiveJ Specializer - это экспериментальный проект, используйте его с осторожностью. Он не поддерживает лямбда-выражения и может испытывать трудности при специализации нетривиальных экземпляров.

Простой калькулятор#

Мы взяли этот учебник по калькулятору парсеков и немного адаптировали его для ActiveJ Specializer. В оригинальном учебнике Parsec возвращает разобранные выражения в виде двойных значений:

Parser<Double> parser = new OperatorTable<Double>()    .infixl(op(" ", (l, r) -> l r), 10)    .infixl(op("-", (l, r) -> l - r), 10)    .infixl(Parsers.or(term("*"), WHITESPACE_MUL).retn((l, r) -> l * r), 20)    .infixl(op("/", (l, r) -> l / r), 20)    .prefix(op("-", v -> -v), 30)    .build(unit);

ActiveJ Specializer демонстрирует свои лучшие качества при работе с древовидными структурами данных. Поэтому мы будем разбирать выражения в AST:

private static final Parser<CalculatorExpression> EXPRESSION = new OperatorTable<CalculatorExpression>()        .infixl(DELIMITERS.token(" ").retn(Sum::new), 10)        .infixl(DELIMITERS.token("-").retn(Sub::new), 10)        .infixl(DELIMITERS.token("*").retn(Mul::new), 20)        .infixl(DELIMITERS.token("/").retn(Div::new), 20)        .infixl(DELIMITERS.token("%").retn(Mod::new), 20)        .prefix(DELIMITERS.token("-").retn(Neg::new), 30)        .infixr(DELIMITERS.token("^").retn(Pow::new), 40)        .build(ATOM);

Предположим, что у нас есть простое уравнение 3 2 * 4. Согласно синтаксическому анализатору, будет создан следующий AST:

graph TD + --> 3 --> * * --> 2 * --> 4

Давайте протестируем Specializer:

public static void main(String[] args) {  double x = -1;
  // manual code, super fast  System.out.println(((2.0 + 2.0 * 2.0) * -x) + 5.0 + 1024.0 / (100.0 + 58.0) * 50.0 * 37.0 - 100.0 + 2.0 * (Math.pow(x, 2.0)) % 3.0);
  CalculatorExpression expression = PARSER.parse("((2 + 2 * 2) * -x) + 5 + 1024 / (100 + 58) * 50 * 37 - 100 + 2 * x ^ 2 % 3");
  System.out.println(expression);
  // tree-walking evaluation, super slow  System.out.println(expression.evaluate(x));
  // specialized instance evaluation, about as fast as manual code  CalculatorExpression specialized = SPECIALIZER.specialize(expression);  System.out.println(specialized.evaluate(x));}

ActiveJ Specializer преобразует AST в набор статических конечных классов с запеченными значениями предоставленного уравнения . JIT сильно оптимизирует и инлайнит эти классы во время выполнения. В результате мы получаем оптимизированное выражение , которое можно использовать повторно, если мы вычисляем уравнение с неизвестным значением несколько раз. Настало время для контрольных показателей. Попробуем обработать уравнение ((2 2 * 2) * -x) 5 1024 / (100 58) * 50 * 37 - 100 2 * x ^ 2 тремя разными способами и сравним производительность: ввести уравнение вручную разобрать уравнение в AST и оценить его без специализации * разобрать уравнение в AST и оценить его со специализацией Результаты бенчмарка очень показательны:

Benchmark Mode Cnt Score Error UnitsCalculatorBenchmark.ast avgt 10 828.924 ± 8.369 ns/opCalculatorBenchmark.manual avgt 10 115.985 ± 1.009 ns/opCalculatorBenchmark.specialized avgt 10 117.635 ± 1.500 ns/op

Как вы можете видеть, уравнения, набранные вручную, и специализированные AST обрабатывались одинаково быстро. ActiveJ Specializer ускорил обработку AST в 8 раз.