Repository: camel Updated Branches: refs/heads/master bac9ddd4b -> 1c86ed716
CAMEL-11484: Optimise - Simple Language / ExpressionBuilder can use cache of frequent used expressions when having nested functions Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/1c86ed71 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/1c86ed71 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/1c86ed71 Branch: refs/heads/master Commit: 1c86ed71607ee48d7d2726278232905857d0c4f5 Parents: bac9ddd Author: Claus Ibsen <davscl...@apache.org> Authored: Fri Jun 30 10:51:59 2017 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Fri Jun 30 11:51:47 2017 +0200 ---------------------------------------------------------------------- .../language/simple/SimpleExpressionParser.java | 14 ++++++++- .../camel/language/simple/SimpleLanguage.java | 8 +++--- .../language/simple/SimplePredicateParser.java | 12 +++++++- .../simple/ast/SimpleFunctionExpression.java | 30 ++++++++++++++++++-- .../simple/ast/SimpleFunctionStart.java | 12 +++++--- .../SimpleLanguageTransformRandomTest.java | 3 ++ .../camel/itest/jmh/SimpleExpressionTest.java | 11 +++---- 7 files changed, 71 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/1c86ed71/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java b/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java index bf5fe8d..0e2bd9c 100644 --- a/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java +++ b/camel-core/src/main/java/org/apache/camel/language/simple/SimpleExpressionParser.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.camel.Expression; +import org.apache.camel.Predicate; import org.apache.camel.builder.ExpressionBuilder; import org.apache.camel.language.simple.ast.LiteralExpression; import org.apache.camel.language.simple.ast.LiteralNode; @@ -32,21 +33,32 @@ import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException; import org.apache.camel.language.simple.types.SimpleParserException; import org.apache.camel.language.simple.types.SimpleToken; import org.apache.camel.language.simple.types.TokenType; +import org.apache.camel.util.LRUCache; /** * A parser to parse simple language as a Camel {@link Expression} */ public class SimpleExpressionParser extends BaseSimpleParser { + // use caches to avoid re-parsing the same expressions over and over again + private LRUCache<String, Expression> cacheExpression; + @Deprecated public SimpleExpressionParser(String expression) { super(expression, true); } + @Deprecated public SimpleExpressionParser(String expression, boolean allowEscape) { super(expression, allowEscape); } + public SimpleExpressionParser(String expression, boolean allowEscape, + LRUCache<String, Expression> cacheExpression) { + super(expression, allowEscape); + this.cacheExpression = cacheExpression; + } + public Expression parseExpression() { clear(); try { @@ -141,7 +153,7 @@ public class SimpleExpressionParser extends BaseSimpleParser { if (token.getType().isFunctionStart()) { // starting a new function functions.incrementAndGet(); - return new SimpleFunctionStart(token); + return new SimpleFunctionStart(token, cacheExpression); } else if (functions.get() > 0 && token.getType().isFunctionEnd()) { // there must be a start function already, to let this be a end function functions.decrementAndGet(); http://git-wip-us.apache.org/repos/asf/camel/blob/1c86ed71/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java b/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java index d62e909..1767dc3 100644 --- a/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java +++ b/camel-core/src/main/java/org/apache/camel/language/simple/SimpleLanguage.java @@ -155,10 +155,10 @@ public class SimpleLanguage extends LanguageSupport implements StaticService { answer = SimpleBackwardsCompatibleParser.parsePredicate(expression, allowEscape); if (answer == null) { // use the new parser - SimplePredicateParser parser = new SimplePredicateParser(expression, allowEscape); + SimplePredicateParser parser = new SimplePredicateParser(expression, allowEscape, cacheExpression); answer = parser.parsePredicate(); } - if (cachePredicate != null) { + if (cachePredicate != null && answer != null) { cachePredicate.put(expression, answer); } } @@ -179,10 +179,10 @@ public class SimpleLanguage extends LanguageSupport implements StaticService { answer = SimpleBackwardsCompatibleParser.parseExpression(expression, allowEscape); if (answer == null) { // use the new parser - SimpleExpressionParser parser = new SimpleExpressionParser(expression, allowEscape); + SimpleExpressionParser parser = new SimpleExpressionParser(expression, allowEscape, cacheExpression); answer = parser.parseExpression(); } - if (cacheExpression != null) { + if (cacheExpression != null && answer != null) { cacheExpression.put(expression, answer); } } http://git-wip-us.apache.org/repos/asf/camel/blob/1c86ed71/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java b/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java index a9576b1..a127f4a 100644 --- a/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java +++ b/camel-core/src/main/java/org/apache/camel/language/simple/SimplePredicateParser.java @@ -47,21 +47,31 @@ import org.apache.camel.language.simple.types.SimpleParserException; import org.apache.camel.language.simple.types.SimpleToken; import org.apache.camel.language.simple.types.TokenType; import org.apache.camel.util.ExpressionToPredicateAdapter; +import org.apache.camel.util.LRUCache; /** * A parser to parse simple language as a Camel {@link Predicate} */ public class SimplePredicateParser extends BaseSimpleParser { + // use caches to avoid re-parsing the same expressions over and over again + private LRUCache<String, Expression> cacheExpression; + @Deprecated public SimplePredicateParser(String expression) { super(expression, true); } + @Deprecated public SimplePredicateParser(String expression, boolean allowEscape) { super(expression, allowEscape); } + public SimplePredicateParser(String expression, boolean allowEscape, LRUCache<String, Expression> cacheExpression) { + super(expression, allowEscape); + this.cacheExpression = cacheExpression; + } + public Predicate parsePredicate() { clear(); try { @@ -223,7 +233,7 @@ public class SimplePredicateParser extends BaseSimpleParser { AtomicBoolean startFunction) { if (token.getType().isFunctionStart()) { startFunction.set(true); - return new SimpleFunctionStart(token); + return new SimpleFunctionStart(token, cacheExpression); } else if (token.getType().isFunctionEnd()) { startFunction.set(false); return new SimpleFunctionEnd(token); http://git-wip-us.apache.org/repos/asf/camel/blob/1c86ed71/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java b/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java index 5affb51..7c4caf3 100644 --- a/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java +++ b/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java @@ -20,6 +20,7 @@ import org.apache.camel.Expression; import org.apache.camel.builder.ExpressionBuilder; import org.apache.camel.language.simple.types.SimpleParserException; import org.apache.camel.language.simple.types.SimpleToken; +import org.apache.camel.util.LRUCache; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.OgnlHelper; import org.apache.camel.util.StringHelper; @@ -30,10 +31,19 @@ import org.apache.camel.util.StringHelper; */ public class SimpleFunctionExpression extends LiteralExpression { + // use caches to avoid re-parsing the same expressions over and over again + private LRUCache<String, Expression> cacheExpression; + + @Deprecated public SimpleFunctionExpression(SimpleToken token) { super(token); } + public SimpleFunctionExpression(SimpleToken token, LRUCache<String, Expression> cacheExpression) { + super(token); + this.cacheExpression = cacheExpression; + } + /** * Creates a Camel {@link Expression} based on this model. * @@ -42,7 +52,15 @@ public class SimpleFunctionExpression extends LiteralExpression { @Override public Expression createExpression(String expression) { String function = text.toString(); - return createSimpleExpression(function, true); + + Expression answer = cacheExpression != null ? cacheExpression.get(function) : null; + if (answer == null) { + answer = createSimpleExpression(function, true); + if (cacheExpression != null && answer != null) { + cacheExpression.put(function, answer); + } + } + return answer; } /** @@ -57,7 +75,15 @@ public class SimpleFunctionExpression extends LiteralExpression { */ public Expression createExpression(String expression, boolean strict) { String function = text.toString(); - return createSimpleExpression(function, strict); + + Expression answer = cacheExpression != null ? cacheExpression.get(function) : null; + if (answer == null) { + answer = createSimpleExpression(function, strict); + if (cacheExpression != null && answer != null) { + cacheExpression.put(function, answer); + } + } + return answer; } private Expression createSimpleExpression(String function, boolean strict) { http://git-wip-us.apache.org/repos/asf/camel/blob/1c86ed71/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java b/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java index e505548..5198789 100644 --- a/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java +++ b/camel-core/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionStart.java @@ -21,6 +21,7 @@ import org.apache.camel.Expression; import org.apache.camel.language.simple.types.SimpleIllegalSyntaxException; import org.apache.camel.language.simple.types.SimpleParserException; import org.apache.camel.language.simple.types.SimpleToken; +import org.apache.camel.util.LRUCache; import org.apache.camel.util.StringHelper; /** @@ -28,11 +29,14 @@ import org.apache.camel.util.StringHelper; */ public class SimpleFunctionStart extends BaseSimpleNode implements BlockStart { - private CompositeNodes block; + // use caches to avoid re-parsing the same expressions over and over again + private final LRUCache<String, Expression> cacheExpression; + private final CompositeNodes block; - public SimpleFunctionStart(SimpleToken token) { + public SimpleFunctionStart(SimpleToken token, LRUCache<String, Expression> cacheExpression) { super(token); this.block = new CompositeNodes(token); + this.cacheExpression = cacheExpression; } public boolean lazyEval(SimpleNode child) { @@ -58,7 +62,7 @@ public class SimpleFunctionStart extends BaseSimpleNode implements BlockStart { } private Expression doCreateLiteralExpression(final String expression) { - SimpleFunctionExpression function = new SimpleFunctionExpression(this.getToken()); + SimpleFunctionExpression function = new SimpleFunctionExpression(this.getToken(), cacheExpression); LiteralNode literal = (LiteralNode) block.getChildren().get(0); function.addText(literal.getText()); return function.createExpression(expression); @@ -109,7 +113,7 @@ public class SimpleFunctionStart extends BaseSimpleNode implements BlockStart { // we have now concat the block as a String which contains the function expression // which we then need to evaluate as a function String exp = sb.toString(); - SimpleFunctionExpression function = new SimpleFunctionExpression(token); + SimpleFunctionExpression function = new SimpleFunctionExpression(token, cacheExpression); function.addText(exp); try { return function.createExpression(exp).evaluate(exchange, type); http://git-wip-us.apache.org/repos/asf/camel/blob/1c86ed71/camel-core/src/test/java/org/apache/camel/language/SimpleLanguageTransformRandomTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/language/SimpleLanguageTransformRandomTest.java b/camel-core/src/test/java/org/apache/camel/language/SimpleLanguageTransformRandomTest.java index 6280f4c..1642318 100644 --- a/camel-core/src/test/java/org/apache/camel/language/SimpleLanguageTransformRandomTest.java +++ b/camel-core/src/test/java/org/apache/camel/language/SimpleLanguageTransformRandomTest.java @@ -30,6 +30,9 @@ public class SimpleLanguageTransformRandomTest extends ContextTestSupport { out = template.requestBodyAndHeader("direct:start", "Random number", "max", "20", int.class); assertTrue(out <= 20); + + out = template.requestBodyAndHeader("direct:start", "Random number", "max", "30", int.class); + assertTrue(out <= 30); } @Override http://git-wip-us.apache.org/repos/asf/camel/blob/1c86ed71/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/SimpleExpressionTest.java ---------------------------------------------------------------------- diff --git a/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/SimpleExpressionTest.java b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/SimpleExpressionTest.java index 1c45768..8968af7 100644 --- a/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/SimpleExpressionTest.java +++ b/tests/camel-jmh/src/test/java/org/apache/camel/itest/jmh/SimpleExpressionTest.java @@ -20,10 +20,9 @@ import java.util.concurrent.TimeUnit; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; -import org.apache.camel.Expression; -import org.apache.camel.builder.ExpressionBuilder; import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.impl.DefaultExchange; +import org.apache.camel.spi.Language; import org.junit.Test; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Level; @@ -73,6 +72,7 @@ public class SimpleExpressionTest { CamelContext camel; String expression = "Hello ${body}"; Exchange exchange; + Language simple; @Setup(Level.Trial) public void initialize() { @@ -81,9 +81,7 @@ public class SimpleExpressionTest { camel.start(); exchange = new DefaultExchange(camel); exchange.getIn().setBody("World"); - - // warm up as we use cache on simple expression - ExpressionBuilder.simpleExpression(expression); + simple = camel.resolveLanguage("simple"); } catch (Exception e) { // ignore @@ -104,8 +102,7 @@ public class SimpleExpressionTest { @Benchmark @Measurement(batchSize = 1000) public void simpleExpression(BenchmarkState state, Blackhole bh) { - Expression simple = ExpressionBuilder.simpleExpression(state.expression); - String out = simple.evaluate(state.exchange, String.class); + String out = state.simple.createExpression(state.expression).evaluate(state.exchange, String.class); if (!out.equals("Hello World")) { throw new IllegalArgumentException("Evaluation failed"); }