Rewrite "performance" test to JMH benchmarks
This commit rewrites the remaining "fastEnough" performance tests into proper JMH benchmarks. See gh-24830
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
/**
|
||||
* Benchmarks for parsing and executing SpEL expressions.
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
public class SpelBenchmark {
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class BenchmarkData {
|
||||
|
||||
public ExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
public EvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
|
||||
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object propertyAccessParseAndExecution(BenchmarkData data) {
|
||||
Expression expr = data.parser.parseExpression("placeOfBirth.city");
|
||||
return expr.getValue(data.eContext);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object methodAccessParseAndExecution(BenchmarkData data) {
|
||||
Expression expr = data.parser.parseExpression("getPlaceOfBirth().getCity()");
|
||||
return expr.getValue(data.eContext);
|
||||
}
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class CachingBenchmarkData extends BenchmarkData {
|
||||
|
||||
public Expression propertyExpression;
|
||||
|
||||
public Expression methodExpression;
|
||||
|
||||
public CachingBenchmarkData() {
|
||||
this.propertyExpression = this.parser.parseExpression("placeOfBirth.city");
|
||||
this.methodExpression = this.parser.parseExpression("getPlaceOfBirth().getCity()");
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object cachingPropertyAccessParseAndExecution(CachingBenchmarkData data) {
|
||||
return data.propertyExpression.getValue(data.eContext);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object cachingMethodAccessParseAndExecution(CachingBenchmarkData data) {
|
||||
return data.methodExpression.getValue(data.eContext);
|
||||
}
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class ValueBenchmarkData {
|
||||
|
||||
public EvaluationContext context;
|
||||
|
||||
public Expression expression;
|
||||
|
||||
public ValueBenchmarkData() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("key", "value");
|
||||
this.context = new StandardEvaluationContext(map);
|
||||
ExpressionParser spelExpressionParser = new SpelExpressionParser();
|
||||
this.expression = spelExpressionParser.parseExpression("#root['key']");
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object getValueFromMap(ValueBenchmarkData data) {
|
||||
return data.expression.getValue(data.context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,7 +21,6 @@ import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.testfixture.EnabledForTestGroups;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
@@ -30,10 +29,8 @@ import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.core.testfixture.TestGroup.PERFORMANCE;
|
||||
|
||||
/**
|
||||
* Testing variations on map access.
|
||||
@@ -75,8 +72,8 @@ public class MapAccessTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetValue(){
|
||||
Map<String,String> props1 = new HashMap<>();
|
||||
public void testGetValue() {
|
||||
Map<String, String> props1 = new HashMap<>();
|
||||
props1.put("key1", "value1");
|
||||
props1.put("key2", "value2");
|
||||
props1.put("key3", "value3");
|
||||
@@ -98,25 +95,6 @@ public class MapAccessTests extends AbstractExpressionTests {
|
||||
assertThat(expr.getValue(map)).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledForTestGroups(PERFORMANCE)
|
||||
public void testGetValuePerformance() throws Exception {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("key", "value");
|
||||
EvaluationContext context = new StandardEvaluationContext(map);
|
||||
|
||||
ExpressionParser spelExpressionParser = new SpelExpressionParser();
|
||||
Expression expr = spelExpressionParser.parseExpression("#root['key']");
|
||||
|
||||
StopWatch s = new StopWatch();
|
||||
s.start();
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
expr.getValue(context);
|
||||
}
|
||||
s.stop();
|
||||
assertThat(s.getTotalTimeMillis()).isLessThan(200L);
|
||||
}
|
||||
|
||||
|
||||
public static class TestBean {
|
||||
|
||||
@@ -166,11 +144,11 @@ public class MapAccessTests extends AbstractExpressionTests {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public Map<String,String> getProperties() {
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(Map<String,String> properties) {
|
||||
public void setProperties(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
||||
@@ -185,7 +163,7 @@ public class MapAccessTests extends AbstractExpressionTests {
|
||||
|
||||
@Override
|
||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
return new TypedValue(((Map<? ,?>) target).get(name));
|
||||
return new TypedValue(((Map<?, ?>) target).get(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -196,7 +174,7 @@ public class MapAccessTests extends AbstractExpressionTests {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
||||
((Map<Object,Object>) target).put(name, newValue);
|
||||
((Map<Object, Object>) target).put(name, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.testfixture.EnabledForTestGroups;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.springframework.core.testfixture.TestGroup.PERFORMANCE;
|
||||
|
||||
///CLOVER:OFF
|
||||
|
||||
/**
|
||||
* Tests the evaluation of real expressions in a real context.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
@EnabledForTestGroups(PERFORMANCE)
|
||||
public class PerformanceTests {
|
||||
|
||||
public static final int ITERATIONS = 10000;
|
||||
public static final boolean report = true;
|
||||
|
||||
private static ExpressionParser parser = new SpelExpressionParser();
|
||||
private static EvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
@Test
|
||||
public void testPerformanceOfPropertyAccess() throws Exception {
|
||||
long starttime = 0;
|
||||
long endtime = 0;
|
||||
|
||||
// warmup
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
Expression expr = parser.parseExpression("placeOfBirth.city");
|
||||
assertThat(expr).isNotNull();
|
||||
expr.getValue(eContext);
|
||||
}
|
||||
|
||||
starttime = System.currentTimeMillis();
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
Expression expr = parser.parseExpression("placeOfBirth.city");
|
||||
assertThat(expr).isNotNull();
|
||||
expr.getValue(eContext);
|
||||
}
|
||||
endtime = System.currentTimeMillis();
|
||||
long freshParseTime = endtime - starttime;
|
||||
if (DEBUG) {
|
||||
System.out.println("PropertyAccess: Time for parsing and evaluation x 10000: "+freshParseTime+"ms");
|
||||
}
|
||||
|
||||
Expression expr = parser.parseExpression("placeOfBirth.city");
|
||||
assertThat(expr).isNotNull();
|
||||
starttime = System.currentTimeMillis();
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
expr.getValue(eContext);
|
||||
}
|
||||
endtime = System.currentTimeMillis();
|
||||
long reuseTime = endtime - starttime;
|
||||
if (DEBUG) {
|
||||
System.out.println("PropertyAccess: Time for just evaluation x 10000: "+reuseTime+"ms");
|
||||
}
|
||||
if (reuseTime > freshParseTime) {
|
||||
System.out.println("Fresh parse every time, ITERATIONS iterations = " + freshParseTime + "ms");
|
||||
System.out.println("Reuse SpelExpression, ITERATIONS iterations = " + reuseTime + "ms");
|
||||
fail("Should have been quicker to reuse!");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerformanceOfMethodAccess() throws Exception {
|
||||
long starttime = 0;
|
||||
long endtime = 0;
|
||||
|
||||
// warmup
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()");
|
||||
assertThat(expr).isNotNull();
|
||||
expr.getValue(eContext);
|
||||
}
|
||||
|
||||
starttime = System.currentTimeMillis();
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()");
|
||||
assertThat(expr).isNotNull();
|
||||
expr.getValue(eContext);
|
||||
}
|
||||
endtime = System.currentTimeMillis();
|
||||
long freshParseTime = endtime - starttime;
|
||||
if (DEBUG) {
|
||||
System.out.println("MethodExpression: Time for parsing and evaluation x 10000: "+freshParseTime+"ms");
|
||||
}
|
||||
|
||||
Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()");
|
||||
assertThat(expr).isNotNull();
|
||||
starttime = System.currentTimeMillis();
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
expr.getValue(eContext);
|
||||
}
|
||||
endtime = System.currentTimeMillis();
|
||||
long reuseTime = endtime - starttime;
|
||||
if (DEBUG) {
|
||||
System.out.println("MethodExpression: Time for just evaluation x 10000: "+reuseTime+"ms");
|
||||
}
|
||||
|
||||
if (reuseTime > freshParseTime) {
|
||||
System.out.println("Fresh parse every time, ITERATIONS iterations = " + freshParseTime + "ms");
|
||||
System.out.println("Reuse SpelExpression, ITERATIONS iterations = " + reuseTime + "ms");
|
||||
fail("Should have been quicker to reuse!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user