Polishing

This commit is contained in:
Juergen Hoeller 2017-07-14 17:37:06 +02:00
parent 83051b06b8
commit efc5b47b9a
2 changed files with 162 additions and 133 deletions

View File

@ -48,8 +48,7 @@ import org.springframework.expression.spel.testdata.PersonInOtherPackage;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**
* Checks the behaviour of the SpelCompiler. * Checks SpelCompiler behavior. This should cover compilation all compiled node types.
* This should cover compilation all compiled node types.
* *
* @author Andy Clement * @author Andy Clement
* @since 4.1 * @since 4.1
@ -322,31 +321,11 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
double resultI = expression.getValue(new TestClass1(), Double.TYPE); double resultI = expression.getValue(new TestClass1(), Double.TYPE);
assertCanCompile(expression); assertCanCompile(expression);
double resultC = expression.getValue(new TestClass1(), Double.TYPE); double resultC = expression.getValue(new TestClass1(), Double.TYPE);
assertEquals(3.4d, resultI,0.1d); assertEquals(3.4d, resultI, 0.1d);
assertEquals(3.4d, resultC,0.1d); assertEquals(3.4d, resultC, 0.1d);
assertEquals(3.4d, expression.getValue()); assertEquals(3.4d, expression.getValue());
} }
@Test
public void repeatedCompilation() throws Exception {
// Verifying that after a number of compilations, the classloaders
// used to load the compiled expressions are discarded/replaced.
// See SpelCompiler.loadClass()
Field f = SpelExpression.class.getDeclaredField("compiledAst");
Set<Object> classloadersUsed = new HashSet<>();
for (int i =0; i < 1500; i++) { // 1500 is greater than SpelCompiler.CLASSES_DEFINED_LIMIT
expression = parser.parseExpression("4 + 5");
assertEquals(9, (int) expression.getValue(Integer.class));
assertCanCompile(expression);
f.setAccessible(true);
CompiledExpression cEx = (CompiledExpression)f.get(expression);
classloadersUsed.add(cEx.getClass().getClassLoader());
assertEquals(9, (int) expression.getValue(Integer.class));
}
assertTrue(classloadersUsed.size() > 1);
}
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Test @Test
public void inlineList() throws Exception { public void inlineList() throws Exception {
@ -476,13 +455,12 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertCanCompile(expression); assertCanCompile(expression);
assertEquals(new Integer(42), expression.getValue(Integer.class)); assertEquals(new Integer(42), expression.getValue(Integer.class));
// Code gen is different for -1 .. 6 because there are bytecode instructions specifically for those // Code gen is different for -1 .. 6 because there are bytecode instructions specifically for those values
// values
// Not an int literal but an opminus with one operand: // Not an int literal but an opminus with one operand:
// expression = parser.parseExpression("-1"); // expression = parser.parseExpression("-1");
// assertCanCompile(expression); // assertCanCompile(expression);
// assertEquals(-1, expression.getValue()); // assertEquals(-1, expression.getValue());
expression = parser.parseExpression("0"); expression = parser.parseExpression("0");
assertCanCompile(expression); assertCanCompile(expression);
assertEquals(0, expression.getValue()); assertEquals(0, expression.getValue());
@ -527,8 +505,8 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
float resultI = expression.getValue(new TestClass1(), Float.TYPE); float resultI = expression.getValue(new TestClass1(), Float.TYPE);
assertCanCompile(expression); assertCanCompile(expression);
float resultC = expression.getValue(new TestClass1(), Float.TYPE); float resultC = expression.getValue(new TestClass1(), Float.TYPE);
assertEquals(3.4f, resultI,0.1f); assertEquals(3.4f, resultI, 0.1f);
assertEquals(3.4f, resultC,0.1f); assertEquals(3.4f, resultC, 0.1f);
assertEquals(3.4f, expression.getValue()); assertEquals(3.4f, expression.getValue());
} }
@ -3314,10 +3292,10 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertCanCompile(expression); assertCanCompile(expression);
Object o = expression.getValue(); Object o = expression.getValue();
assertEquals(testclass8,o.getClass().getName()); assertEquals(testclass8,o.getClass().getName());
TestClass8 tc8 = (TestClass8)o; TestClass8 tc8 = (TestClass8) o;
assertEquals(42, tc8.i); assertEquals(42, tc8.i);
assertEquals("123", tc8.s); assertEquals("123", tc8.s);
assertEquals(4.0d, tc8.d,0.5d); assertEquals(4.0d, tc8.d, 0.5d);
assertEquals(true, tc8.z); assertEquals(true, tc8.z);
// no-arg ctor // no-arg ctor
@ -4372,7 +4350,8 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
expression = parser.parseExpression("DR[0].three"); expression = parser.parseExpression("DR[0].three");
Object v = expression.getValue(payload); Object v = expression.getValue(payload);
assertEquals("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three", getAst().getExitDescriptor()); assertEquals("Lorg/springframework/expression/spel/SpelCompilationCoverageTests$Three",
getAst().getExitDescriptor());
Expression expression = parser.parseExpression("DR[0].three.four lt 0.1d?#root:null"); Expression expression = parser.parseExpression("DR[0].three.four lt 0.1d?#root:null");
v = expression.getValue(payload); v = expression.getValue(payload);
@ -4380,7 +4359,7 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
SpelExpression sExpr = (SpelExpression) expression; SpelExpression sExpr = (SpelExpression) expression;
Ternary ternary = (Ternary) sExpr.getAST(); Ternary ternary = (Ternary) sExpr.getAST();
OpLT oplt = (OpLT) ternary.getChild(0); OpLT oplt = (OpLT) ternary.getChild(0);
CompoundExpression cExpr = (CompoundExpression)oplt.getLeftOperand(); CompoundExpression cExpr = (CompoundExpression) oplt.getLeftOperand();
String cExprExitDescriptor = cExpr.getExitDescriptor(); String cExprExitDescriptor = cExpr.getExitDescriptor();
assertEquals("D", cExprExitDescriptor); assertEquals("D", cExprExitDescriptor);
assertEquals("Z", oplt.getExitDescriptor()); assertEquals("Z", oplt.getExitDescriptor());
@ -4620,14 +4599,16 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@Test @Test
public void indexerMapAccessor_12045() throws Exception { public void indexerMapAccessor_12045() throws Exception {
SpelParserConfiguration spc = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,getClass().getClassLoader()); SpelParserConfiguration spc = new SpelParserConfiguration(
SpelCompilerMode.IMMEDIATE,getClass().getClassLoader());
SpelExpressionParser sep = new SpelExpressionParser(spc); SpelExpressionParser sep = new SpelExpressionParser(spc);
expression=sep.parseExpression("headers[command]"); expression=sep.parseExpression("headers[command]");
MyMessage root = new MyMessage(); MyMessage root = new MyMessage();
assertEquals("wibble", expression.getValue(root)); assertEquals("wibble", expression.getValue(root));
// This next call was failing because the isCompilable check in Indexer did not check on the key being compilable // This next call was failing because the isCompilable check in Indexer
// (and also generateCode in the Indexer was missing the optimization that it didn't need necessarily need to call // did not check on the key being compilable (and also generateCode in the
// generateCode for that accessor) // Indexer was missing the optimization that it didn't need necessarily
// need to call generateCode for that accessor)
assertEquals("wibble", expression.getValue(root)); assertEquals("wibble", expression.getValue(root));
assertCanCompile(expression); assertCanCompile(expression);
@ -4803,6 +4784,25 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertIsCompiled(exp); assertIsCompiled(exp);
} }
@Test
public void repeatedCompilation() throws Exception {
// Verifying that after a number of compilations, the classloaders
// used to load the compiled expressions are discarded/replaced.
// See SpelCompiler.loadClass()
Field f = SpelExpression.class.getDeclaredField("compiledAst");
Set<Object> classloadersUsed = new HashSet<>();
for (int i = 0; i < 1500; i++) { // 1500 is greater than SpelCompiler.CLASSES_DEFINED_LIMIT
expression = parser.parseExpression("4 + 5");
assertEquals(9, (int) expression.getValue(Integer.class));
assertCanCompile(expression);
f.setAccessible(true);
CompiledExpression cEx = (CompiledExpression) f.get(expression);
classloadersUsed.add(cEx.getClass().getClassLoader());
assertEquals(9, (int) expression.getValue(Integer.class));
}
assertTrue(classloadersUsed.size() > 1);
}
// helper methods // helper methods

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,8 +26,9 @@ import static org.junit.Assert.*;
/** /**
* Checks the speed of compiled SpEL expressions. * Checks the speed of compiled SpEL expressions.
* By default these tests are marked Ignore since they can fail on a busy machine because they *
* compare relative performance of interpreted vs compiled. * <p>By default these tests are marked @Ignore since they can fail on a busy machine
* because they compare relative performance of interpreted vs compiled.
* *
* @author Andy Clement * @author Andy Clement
* @since 4.1 * @since 4.1
@ -35,37 +36,14 @@ import static org.junit.Assert.*;
@Ignore @Ignore
public class SpelCompilationPerformanceTests extends AbstractExpressionTests { public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
int count = 50000; // Number of evaluations that are timed in one run int count = 50000; // number of evaluations that are timed in one run
int iterations = 10; // Number of times to repeat 'count' evaluations (for averaging)
int iterations = 10; // number of times to repeat 'count' evaluations (for averaging)
private final static boolean noisyTests = true; private final static boolean noisyTests = true;
Expression expression; Expression expression;
public static class Payload {
Two[] DR = new Two[]{new Two()};
public Two[] getDR() {
return DR;
}
}
public static class Two {
Three DRFixedSection = new Three();
public Three getDRFixedSection() {
return DRFixedSection;
}
}
public static class Three {
double duration = 0.4d;
public double getDuration() {
return duration;
}
}
public static class NumberHolder {
public int payload = 36;
}
/** /**
* This test verifies the new support for compiling mathematical expressions with * This test verifies the new support for compiling mathematical expressions with
@ -241,7 +219,6 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
System.out.println("One million iterations: " + (System.currentTimeMillis()-stime) + "ms"); System.out.println("One million iterations: " + (System.currentTimeMillis()-stime) + "ms");
} }
@Test @Test
public void stringConcatenation() throws Exception { public void stringConcatenation() throws Exception {
expression = parser.parseExpression("'hello' + getWorld() + ' spring'"); expression = parser.parseExpression("'hello' + getWorld() + ' spring'");
@ -287,12 +264,6 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
System.out.println("One million iterations: " + (System.currentTimeMillis()-stime) + "ms"); System.out.println("One million iterations: " + (System.currentTimeMillis()-stime) + "ms");
} }
public static class Greeter {
public String getWorld() {
return "world";
}
}
@Test @Test
public void complexExpressionPerformance() throws Exception { public void complexExpressionPerformance() throws Exception {
Payload payload = new Payload(); Payload payload = new Payload();
@ -313,8 +284,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
long etime = System.currentTimeMillis(); long etime = System.currentTimeMillis();
long interpretedSpeed = (etime - stime); long interpretedSpeed = (etime - stime);
iTotal+=interpretedSpeed; iTotal += interpretedSpeed;
log(interpretedSpeed+"ms "); log(interpretedSpeed + "ms ");
} }
logln(); logln();
@ -329,8 +300,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
long etime = System.currentTimeMillis(); long etime = System.currentTimeMillis();
long compiledSpeed = (etime - stime); long compiledSpeed = (etime - stime);
cTotal+=compiledSpeed; cTotal += compiledSpeed;
log(compiledSpeed+"ms "); log(compiledSpeed + "ms ");
} }
logln(); logln();
@ -376,8 +347,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long interpretedSpeed = (etime - stime); long interpretedSpeed = (etime - stime);
interpretedTotal+=interpretedSpeed; interpretedTotal += interpretedSpeed;
log(interpretedSpeed+"ms "); log(interpretedSpeed + "ms ");
} }
logln(); logln();
@ -392,8 +363,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long compiledSpeed = (etime - stime); long compiledSpeed = (etime - stime);
compiledTotal+=compiledSpeed; compiledTotal += compiledSpeed;
log(compiledSpeed+"ms "); log(compiledSpeed + "ms ");
} }
logln(); logln();
@ -407,28 +378,6 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
public static class TestClass2 {
public String name = "Santa";
private String name2 = "foobar";
public String getName2() {
return name2;
}
public Foo foo = new Foo();
public static class Foo {
public Bar bar = new Bar();
Bar b = new Bar();
public Bar getBaz() {
return b;
}
public Bar bay() {
return b;
}
}
public static class Bar {
public String boo = "oranges";
}
}
@Test @Test
public void compilingPropertyReferenceField() throws Exception { public void compilingPropertyReferenceField() throws Exception {
long interpretedTotal = 0, compiledTotal = 0, stime, etime; long interpretedTotal = 0, compiledTotal = 0, stime, etime;
@ -450,8 +399,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long interpretedSpeed = (etime - stime); long interpretedSpeed = (etime - stime);
interpretedTotal+=interpretedSpeed; interpretedTotal += interpretedSpeed;
log(interpretedSpeed+"ms "); log(interpretedSpeed + "ms ");
} }
logln(); logln();
@ -466,8 +415,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long compiledSpeed = (etime - stime); long compiledSpeed = (etime - stime);
compiledTotal+=compiledSpeed; compiledTotal += compiledSpeed;
log(compiledSpeed+"ms "); log(compiledSpeed + "ms ");
} }
logln(); logln();
@ -481,7 +430,6 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
String interpretedResult = null, compiledResult = null; String interpretedResult = null, compiledResult = null;
TestClass2 testdata = new TestClass2(); TestClass2 testdata = new TestClass2();
Expression expression = parser.parseExpression("foo.bar.boo"); Expression expression = parser.parseExpression("foo.bar.boo");
// warmup // warmup
@ -497,8 +445,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long interpretedSpeed = (etime - stime); long interpretedSpeed = (etime - stime);
interpretedTotal+=interpretedSpeed; interpretedTotal += interpretedSpeed;
log(interpretedSpeed+"ms "); log(interpretedSpeed + "ms ");
} }
logln(); logln();
@ -513,8 +461,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long compiledSpeed = (etime - stime); long compiledSpeed = (etime - stime);
compiledTotal+=compiledSpeed; compiledTotal += compiledSpeed;
log(compiledSpeed+"ms "); log(compiledSpeed + "ms ");
} }
logln(); logln();
@ -542,8 +490,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long interpretedSpeed = (etime - stime); long interpretedSpeed = (etime - stime);
interpretedTotal+=interpretedSpeed; interpretedTotal += interpretedSpeed;
log(interpretedSpeed+"ms "); log(interpretedSpeed + "ms ");
} }
logln(); logln();
@ -558,8 +506,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long compiledSpeed = (etime - stime); long compiledSpeed = (etime - stime);
compiledTotal+=compiledSpeed; compiledTotal += compiledSpeed;
log(compiledSpeed+"ms "); log(compiledSpeed + "ms ");
} }
logln(); logln();
@ -635,8 +583,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long interpretedSpeed = (etime - stime); long interpretedSpeed = (etime - stime);
interpretedTotal+=interpretedSpeed; interpretedTotal += interpretedSpeed;
log(interpretedSpeed+"ms "); log(interpretedSpeed + "ms ");
} }
logln(); logln();
@ -652,8 +600,8 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
} }
etime = System.currentTimeMillis(); etime = System.currentTimeMillis();
long compiledSpeed = (etime - stime); long compiledSpeed = (etime - stime);
compiledTotal+=compiledSpeed; compiledTotal += compiledSpeed;
log(compiledSpeed+"ms "); log(compiledSpeed + "ms ");
} }
logln(); logln();
@ -661,21 +609,22 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
assertEquals(interpretedResult,compiledResult); assertEquals(interpretedResult,compiledResult);
reportPerformance("property reference (getter)", interpretedTotal, compiledTotal); reportPerformance("property reference (getter)", interpretedTotal, compiledTotal);
if (compiledTotal>=interpretedTotal) { if (compiledTotal >= interpretedTotal) {
fail("Compiled version is slower than interpreted!"); fail("Compiled version is slower than interpreted!");
} }
} }
// ---
private void reportPerformance(String title, long interpretedTotal, long compiledTotal) { private void reportPerformance(String title, long interpretedTotal, long compiledTotal) {
double averageInterpreted = interpretedTotal/(iterations); double averageInterpreted = interpretedTotal / iterations;
double averageCompiled = compiledTotal/(iterations); double averageCompiled = compiledTotal / iterations;
double ratio = (averageCompiled/averageInterpreted)*100.0d; double ratio = (averageCompiled / averageInterpreted) * 100.0d;
logln(">>"+title+": average for "+count+": compiled="+averageCompiled+"ms interpreted="+averageInterpreted+"ms: compiled takes " + ((int)ratio)+"% of the interpreted time"); logln(">>" + title + ": average for " + count + ": compiled=" + averageCompiled +
if (averageCompiled>averageInterpreted) { "ms interpreted=" + averageInterpreted + "ms: compiled takes " +
fail("Compiled version took longer than interpreted! CompiledSpeed=~"+averageCompiled+ ((int) ratio) + "% of the interpreted time");
"ms InterpretedSpeed="+averageInterpreted+"ms"); if (averageCompiled > averageInterpreted) {
fail("Compiled version took longer than interpreted! CompiledSpeed=~" + averageCompiled +
"ms InterpretedSpeed=" + averageInterpreted + "ms");
} }
logln(); logln();
} }
@ -688,7 +637,7 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
private void logln(String... message) { private void logln(String... message) {
if (noisyTests) { if (noisyTests) {
if (message!=null && message.length>0) { if (message.length > 0) {
System.out.println(message[0]); System.out.println(message[0]);
} }
else { else {
@ -700,4 +649,84 @@ public class SpelCompilationPerformanceTests extends AbstractExpressionTests {
private void compile(Expression expression) { private void compile(Expression expression) {
assertTrue(SpelCompiler.compile(expression)); assertTrue(SpelCompiler.compile(expression));
} }
public static class Payload {
Two[] DR = new Two[]{new Two()};
public Two[] getDR() {
return DR;
}
}
public static class Two {
Three DRFixedSection = new Three();
public Three getDRFixedSection() {
return DRFixedSection;
}
}
public static class Three {
double duration = 0.4d;
public double getDuration() {
return duration;
}
}
public static class NumberHolder {
public int payload = 36;
}
public static class Greeter {
public String getWorld() {
return "world";
}
}
public static class TestClass2 {
public String name = "Santa";
private String name2 = "foobar";
public String getName2() {
return name2;
}
public Foo foo = new Foo();
}
public static class Foo {
public Bar bar = new Bar();
Bar b = new Bar();
public Bar getBaz() {
return b;
}
public Bar bay() {
return b;
}
}
public static class Bar {
public String boo = "oranges";
}
} }