This is an automated email from the ASF dual-hosted git repository. kturner pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/accumulo-access.git
The following commit(s) were added to refs/heads/main by this push: new 8b86909 Adds a JMH Benchmark (#10) 8b86909 is described below commit 8b8690923db7581317ed31b9790dedc4dea7cfbb Author: Keith Turner <ktur...@apache.org> AuthorDate: Wed Sep 20 10:45:49 2023 -0400 Adds a JMH Benchmark (#10) --- pom.xml | 13 ++ .../accumulo/access/AccessEvaluatorTest.java | 4 +- .../accumulo/access/AccessExpressionBenchmark.java | 187 +++++++++++++++++++++ 3 files changed, 202 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 006648d..6e1600a 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <version.jmh>1.36</version.jmh> </properties> <dependencies> @@ -40,6 +41,18 @@ <version>5.9.2</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-core</artifactId> + <version>${version.jmh}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-generator-annprocess</artifactId> + <version>${version.jmh}</version> + <scope>test</scope> + </dependency> </dependencies> <build> diff --git a/src/test/java/org/apache/accumulo/access/AccessEvaluatorTest.java b/src/test/java/org/apache/accumulo/access/AccessEvaluatorTest.java index 1afa67a..6133363 100644 --- a/src/test/java/org/apache/accumulo/access/AccessEvaluatorTest.java +++ b/src/test/java/org/apache/accumulo/access/AccessEvaluatorTest.java @@ -60,8 +60,8 @@ public class AccessEvaluatorTest { } - private List<TestDataSet> readTestData() throws IOException { - try (var input = getClass().getClassLoader().getResourceAsStream("testdata.json")) { + static List<TestDataSet> readTestData() throws IOException { + try (var input = AccessEvaluatorTest.class.getClassLoader().getResourceAsStream("testdata.json")) { if (input == null) { throw new IllegalStateException("could not find resource : testdata.json"); } diff --git a/src/test/java/org/apache/accumulo/access/AccessExpressionBenchmark.java b/src/test/java/org/apache/accumulo/access/AccessExpressionBenchmark.java new file mode 100644 index 0000000..6cd4b91 --- /dev/null +++ b/src/test/java/org/apache/accumulo/access/AccessExpressionBenchmark.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.accumulo.access; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.TimeValue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Benchmarks Access Expressions using JMH. To run, use the following commands. + * + * <p><blockquote><pre> + * mvn clean package + * mvn exec:exec -Dexec.executable="java" -Dexec.classpathScope=test -Dexec.args="-classpath %classpath org.apache.accumulo.access.AccessExpressionBenchmark" + * </code></blockquote> + * </pre> + */ +public class AccessExpressionBenchmark { + + + public static class EvaluatorTests { + AccessEvaluator evaluator; + List<AccessExpression> parsedExpressions; + + List<byte[]> expressions; + } + + @State(Scope.Benchmark) + public static class BenchmarkState { + + private ArrayList<byte[]> allTestExpressions; + + private ArrayList<String> allTestExpressionsStr; + + private ArrayList<EvaluatorTests> evaluatorTests; + + @Setup + public void loadData() throws IOException { + List<AccessEvaluatorTest.TestDataSet> testData = AccessEvaluatorTest.readTestData(); + allTestExpressions = new ArrayList<>(); + allTestExpressionsStr = new ArrayList<>(); + evaluatorTests = new ArrayList<>(); + + for(var testDataSet : testData) { + EvaluatorTests et = new EvaluatorTests(); + et.parsedExpressions = new ArrayList<>(); + et.expressions = new ArrayList<>(); + + if(testDataSet.auths.length == 1) { + et.evaluator = AccessEvaluator.builder().authorizations(testDataSet.auths[0]).build(); + } else { + var authSets = + Stream.of(testDataSet.auths).map(Authorizations::of).collect(Collectors.toList()); + et.evaluator = AccessEvaluator.builder().authorizations(authSets).build(); + } + + for(var tests : testDataSet.tests) { + if(tests.expectedResult != AccessEvaluatorTest.ExpectedResult.ERROR) { + for(var exp : tests.expressions) { + allTestExpressionsStr.add(exp); + byte[] byteExp = exp.getBytes(UTF_8); + allTestExpressions.add(byteExp); + et.expressions.add(byteExp); + et.parsedExpressions.add(AccessExpression.of(exp)); + } + } + } + + evaluatorTests.add(et); + } + } + + List<byte[]> getBytesExpressions(){ + return allTestExpressions; + } + + List<String> getStringExpressions(){ + return allTestExpressionsStr; + } + + public ArrayList<EvaluatorTests> getEvaluatorTests() { + return evaluatorTests; + } + + } + + /** + * Measures the time it takes to parse an expression stored in byte[] and produce a parse tree. + */ + @Benchmark + public void measureBytesParsing(BenchmarkState state, Blackhole blackhole) { + for(byte[] accessExpression : state.getBytesExpressions()) { + blackhole.consume(AccessExpression.of(accessExpression)); + } + } + + /** + * Measures the time it takes to parse an expression stored in a String and produce a parse tree. + */ + @Benchmark + public void measureStringParsing(BenchmarkState state, Blackhole blackhole) { + for(String accessExpression : state.getStringExpressions()) { + blackhole.consume(AccessExpression.of(accessExpression)); + } + } + + /** + * Measures the time it takes to evaluate a previously parsed expression. + */ + @Benchmark + public void measureEvaluation(BenchmarkState state, Blackhole blackhole) { + for(EvaluatorTests evaluatorTests : state.getEvaluatorTests()) { + for(AccessExpression expression : evaluatorTests.parsedExpressions) { + blackhole.consume(evaluatorTests.evaluator.canAccess(expression)); + } + } + } + + /** + * Measures the time it takes to parse and evaluate an expression. This has to create the parse tree an operate on it. + */ + @Benchmark + public void measureEvaluationAndParsing(BenchmarkState state, Blackhole blackhole) { + for(EvaluatorTests evaluatorTests : state.getEvaluatorTests()) { + for(byte[] expression : evaluatorTests.expressions) { + blackhole.consume(evaluatorTests.evaluator.canAccess(expression)); + } + } + } + + public static void main(String[] args) throws RunnerException, IOException { + + var state = new BenchmarkState(); + state.loadData(); + + int numExpressions =state.getBytesExpressions().size(); + + System.out.println("Number of Expressions: " + numExpressions); + + Options opt = new OptionsBuilder() + .include(AccessExpressionBenchmark.class.getSimpleName()) + .mode(Mode.Throughput) + .operationsPerInvocation(numExpressions) + .timeUnit(TimeUnit.MICROSECONDS) + .warmupTime(TimeValue.seconds(5)) + .warmupIterations(3) + .measurementIterations(4) + .forks(3) + .build(); + + new Runner(opt).run(); + } + +}