Add benchmark of Parser.create(sql).parseQuery() Closes #176
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/4a29b3cc Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/4a29b3cc Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/4a29b3cc Branch: refs/heads/branch-release Commit: 4a29b3ccf64e9a7d1534f540fd7f6d3d813605a0 Parents: b94a00e Author: Vladimir Sitnikov <[email protected]> Authored: Sat Jan 9 13:36:33 2016 +0300 Committer: Julian Hyde <[email protected]> Committed: Sun Jan 10 00:46:49 2016 -0800 ---------------------------------------------------------------------- .../apache/calcite/sql/parser/SqlParser.java | 25 +- pom.xml | 4 +- ubenchmark/pom.xml | 5 +- .../java/org/apache/calcite/StatementTest.java | 264 ------------------- .../benchmarks/FlightRecorderProfiler.java | 87 ++++++ .../calcite/benchmarks/ParserBenchmark.java | 121 +++++++++ .../calcite/benchmarks/PreconditionTest.java | 56 ++++ .../calcite/benchmarks/StatementTest.java | 264 +++++++++++++++++++ .../apache/calcite/benchmarks/package-info.java | 26 ++ 9 files changed, 575 insertions(+), 277 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/4a29b3cc/core/src/main/java/org/apache/calcite/sql/parser/SqlParser.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlParser.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlParser.java index 4c14395..e1bda73 100644 --- a/core/src/main/java/org/apache/calcite/sql/parser/SqlParser.java +++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlParser.java @@ -133,22 +133,27 @@ public class SqlParser { } /** + * Parses a <code>SELECT</code> statement and reuses parser. + * + * @param sql sql to parse + * @return A {@link org.apache.calcite.sql.SqlSelect} for a regular <code> + * SELECT</code> statement; a {@link org.apache.calcite.sql.SqlBinaryOperator} + * for a <code>UNION</code>, <code>INTERSECT</code>, or <code>EXCEPT</code>. + * @throws SqlParseException if there is a parse error + */ + public SqlNode parseQuery(String sql) throws SqlParseException { + parser.ReInit(new StringReader(sql)); + return parseQuery(); + } + + /** * Parses an SQL statement. * * @return top-level SqlNode representing stmt * @throws SqlParseException if there is a parse error */ public SqlNode parseStmt() throws SqlParseException { - try { - return parser.parseSqlStmtEof(); - } catch (Throwable ex) { - if ((ex instanceof CalciteContextException) - && (originalInput != null)) { - ((CalciteContextException) ex).setOriginalStatement( - originalInput); - } - throw parser.normalizeException(ex); - } + return parseQuery(); } /** http://git-wip-us.apache.org/repos/asf/calcite/blob/4a29b3cc/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 9ac5596..3253f25 100644 --- a/pom.xml +++ b/pom.xml @@ -295,12 +295,12 @@ limitations under the License. <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> - <version>0.7.1</version> + <version>1.11.2</version> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> - <version>0.7.1</version> + <version>1.11.2</version> </dependency> <dependency> <groupId>sqlline</groupId> http://git-wip-us.apache.org/repos/asf/calcite/blob/4a29b3cc/ubenchmark/pom.xml ---------------------------------------------------------------------- diff --git a/ubenchmark/pom.xml b/ubenchmark/pom.xml index 1f1752c..e50d4dd 100644 --- a/ubenchmark/pom.xml +++ b/ubenchmark/pom.xml @@ -55,6 +55,10 @@ limitations under the License. <artifactId>jmh-generator-annprocess</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> </dependencies> <build> @@ -64,7 +68,6 @@ limitations under the License. <configuration> <source>1.6</source> <target>1.6</target> - <compilerArgument>-proc:none</compilerArgument> </configuration> </plugin> <plugin> http://git-wip-us.apache.org/repos/asf/calcite/blob/4a29b3cc/ubenchmark/src/main/java/org/apache/calcite/StatementTest.java ---------------------------------------------------------------------- diff --git a/ubenchmark/src/main/java/org/apache/calcite/StatementTest.java b/ubenchmark/src/main/java/org/apache/calcite/StatementTest.java deleted file mode 100644 index b8a7d2f..0000000 --- a/ubenchmark/src/main/java/org/apache/calcite/StatementTest.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * 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 - * - * http://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.calcite; - -import org.apache.calcite.adapter.java.ReflectiveSchema; -import org.apache.calcite.jdbc.CalciteConnection; -import org.apache.calcite.schema.SchemaPlus; - -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.GenerateMicroBenchmark; -import org.openjdk.jmh.annotations.Level; -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 java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import java.util.Random; - -/** - * Compares {@link java.sql.Statement} vs {@link java.sql.PreparedStatement}. - * - * <p>This package contains micro-benchmarks to test calcite performance. - * - * <p>To run this and other benchmarks: - * - * <blockquote> - * <code>mvn package && - * java -jar ./target/ubenchmarks.jar -wi 5 -i 5 -f 1</code> - * </blockquote> - * - * <p>To run with profiling: - * - * <blockquote> - * <code>java -Djmh.stack.lines=10 -jar ./target/ubenchmarks.jar - * -prof hs_comp,hs_gc,stack -f 1 -wi 5</code> - * </blockquote> - */ -public class StatementTest { - - /** - * Connection to be used during tests. - */ - @State(Scope.Thread) - @BenchmarkMode(Mode.AverageTime) - public static class HrConnection { - Connection con; - int id; - HrSchema hr = new HrSchema(); - Random rnd = new Random(); - { - try { - Class.forName("org.apache.calcite.jdbc.Driver"); - } catch (ClassNotFoundException e) { - throw new IllegalStateException(e); - } - Connection connection; - - try { - Properties info = new Properties(); - info.put("lex", "JAVA"); - info.put("quoting", "DOUBLE_QUOTE"); - connection = DriverManager.getConnection("jdbc:calcite:", info); - } catch (SQLException e) { - throw new IllegalStateException(e); - } - CalciteConnection calciteConnection; - try { - calciteConnection = connection.unwrap(CalciteConnection.class); - } catch (SQLException e) { - throw new IllegalStateException(e); - } - final SchemaPlus rootSchema = calciteConnection.getRootSchema(); - rootSchema.add("hr", new ReflectiveSchema(new HrSchema())); - try { - calciteConnection.setSchema("hr"); - } catch (SQLException e) { - throw new IllegalStateException(e); - } - con = connection; - } - - @Setup(Level.Iteration) - public void pickEmployee() { - id = hr.emps[rnd.nextInt(4)].empid; - } - } - - /** - * Tests performance of reused execution of prepared statement. - */ - public static class HrPreparedStatement extends HrConnection { - PreparedStatement ps; - { - try { - ps = con.prepareStatement("select name from emps where empid = ?"); - } catch (SQLException e) { - throw new IllegalStateException(e); - } - } - } - - @GenerateMicroBenchmark - public String prepareBindExecute(HrConnection state) throws SQLException { - Connection con = state.con; - Statement st = null; - ResultSet rs = null; - String ename = null; - try { - final PreparedStatement ps = - con.prepareStatement("select name from emps where empid = ?"); - st = ps; - ps.setInt(1, state.id); - rs = ps.executeQuery(); - rs.next(); - ename = rs.getString(1); - } finally { - close(rs, st); - } - return ename; - } - - @GenerateMicroBenchmark - public String bindExecute(HrPreparedStatement state) - throws SQLException { - PreparedStatement st = state.ps; - ResultSet rs = null; - String ename = null; - try { - st.setInt(1, state.id); - rs = st.executeQuery(); - rs.next(); - ename = rs.getString(1); - } finally { - close(rs, null); // Statement is not closed - } - return ename; - } - - @GenerateMicroBenchmark - public String executeQuery(HrConnection state) throws SQLException { - Connection con = state.con; - Statement st = null; - ResultSet rs = null; - String ename = null; - try { - st = con.createStatement(); - rs = st.executeQuery("select name from emps where empid = " + state.id); - rs.next(); - ename = rs.getString(1); - } finally { - close(rs, st); - } - return ename; - } - - @GenerateMicroBenchmark - public String forEach(HrConnection state) throws SQLException { - final Employee[] emps = state.hr.emps; - for (Employee emp : emps) { - if (emp.empid == state.id) { - return emp.name; - } - } - return null; - } - - private static void close(ResultSet rs, Statement st) { - if (rs != null) { - try { rs.close(); } catch (SQLException e) { /**/ } - } - if (st != null) { - try { st.close(); } catch (SQLException e) { /**/ } - } - } - - /** Pojo schema containing "emps" and "depts" tables. */ - public static class HrSchema { - @Override public String toString() { - return "HrSchema"; - } - - public final Employee[] emps = { - new Employee(100, 10, "Bill", 10000, 1000), - new Employee(200, 20, "Eric", 8000, 500), - new Employee(150, 10, "Sebastian", 7000, null), - new Employee(110, 10, "Theodore", 11500, 250), - }; - public final Department[] depts = { - new Department(10, "Sales", Arrays.asList(emps[0], emps[2])), - new Department(30, "Marketing", Collections.<Employee>emptyList()), - new Department(40, "HR", Collections.singletonList(emps[1])), - }; - } - - /** Employee record. */ - public static class Employee { - public final int empid; - public final int deptno; - public final String name; - public final float salary; - public final Integer commission; - - public Employee(int empid, int deptno, String name, float salary, - Integer commission) { - this.empid = empid; - this.deptno = deptno; - this.name = name; - this.salary = salary; - this.commission = commission; - } - - public String toString() { - return "Employee [empid: " + empid + ", deptno: " + deptno - + ", name: " + name + "]"; - } - } - - /** Department record. */ - public static class Department { - public final int deptno; - public final String name; - public final List<Employee> employees; - - public Department( - int deptno, String name, List<Employee> employees) { - this.deptno = deptno; - this.name = name; - this.employees = employees; - } - - - public String toString() { - return "Department [deptno: " + deptno + ", name: " + name - + ", employees: " + employees + "]"; - } - } - -} - -// End StatementTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/4a29b3cc/ubenchmark/src/main/java/org/apache/calcite/benchmarks/FlightRecorderProfiler.java ---------------------------------------------------------------------- diff --git a/ubenchmark/src/main/java/org/apache/calcite/benchmarks/FlightRecorderProfiler.java b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/FlightRecorderProfiler.java new file mode 100644 index 0000000..451c004 --- /dev/null +++ b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/FlightRecorderProfiler.java @@ -0,0 +1,87 @@ +/* + * 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 + * + * http://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.calcite.benchmarks; + +import org.openjdk.jmh.infra.BenchmarkParams; +import org.openjdk.jmh.infra.IterationParams; +import org.openjdk.jmh.profile.ExternalProfiler; +import org.openjdk.jmh.results.BenchmarkResult; +import org.openjdk.jmh.results.Result; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +/** + * Captures Flight Recorder log. + * Note: Flight Recorder is available in OracleJDK only. + * Usage of Flight Recorder in production requires a LICENSE FEE, however Flight Recorder is free + * for use in test systems. + * It is assumed you would not use Calcite benchmarks for running a production system, thus it is + * believed to be safe. + */ +public class FlightRecorderProfiler implements ExternalProfiler { + @Override public Collection<String> addJVMInvokeOptions(BenchmarkParams params) { + return Collections.emptyList(); + } + + @Override public Collection<String> addJVMOptions(BenchmarkParams params) { + StringBuilder sb = new StringBuilder(); + for (String param : params.getParamsKeys()) { + if (sb.length() != 0) { + sb.append('-'); + } + sb.append(param).append('-').append(params.getParam(param)); + } + + long duration = + getDurationSeconds(params.getWarmup()) + getDurationSeconds(params.getMeasurement()); + return Arrays.asList( + "-XX:+UnlockCommercialFeatures", "-XX:+FlightRecorder", + "-XX:StartFlightRecording=settings=profile,duration=" + duration + "s,filename=" + + params.getBenchmark() + "_" + sb + ".jfr"); + } + + private long getDurationSeconds(IterationParams warmup) { + return warmup.getTime().convertTo(TimeUnit.SECONDS) * warmup.getCount(); + } + + @Override public void beforeTrial(BenchmarkParams benchmarkParams) { + + } + + @Override public Collection<? extends Result> afterTrial(BenchmarkResult br, long pid, + File stdOut, File stdErr) { + return Collections.emptyList(); + } + + @Override public boolean allowPrintOut() { + return true; + } + + @Override public boolean allowPrintErr() { + return true; + } + + @Override public String getDescription() { + return "Collects Java Flight Recorder profile"; + } +} + +// End FlightRecorderProfiler.java http://git-wip-us.apache.org/repos/asf/calcite/blob/4a29b3cc/ubenchmark/src/main/java/org/apache/calcite/benchmarks/ParserBenchmark.java ---------------------------------------------------------------------- diff --git a/ubenchmark/src/main/java/org/apache/calcite/benchmarks/ParserBenchmark.java b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/ParserBenchmark.java new file mode 100644 index 0000000..90e4e8a --- /dev/null +++ b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/ParserBenchmark.java @@ -0,0 +1,121 @@ +/* + * 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 + * + * http://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.calcite.benchmarks; + +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.parser.SqlParseException; +import org.apache.calcite.sql.parser.SqlParser; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.profile.GCProfiler; +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 java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * Benchmarks JavaCC-generated SQL parser + */ +@Fork(value = 1, jvmArgsPrepend = "-Xmx128m") +@Measurement(iterations = 7, time = 1, timeUnit = TimeUnit.SECONDS) +@Warmup(iterations = 7, time = 1, timeUnit = TimeUnit.SECONDS) +@State(Scope.Thread) +@Threads(1) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +public class ParserBenchmark { + + @Param({ "1000" }) + int length; + + @Param({ "true" }) + boolean comments; + + String sql; + SqlParser parser; + + @Setup + public void setup() throws SqlParseException { + StringBuilder sb = new StringBuilder((int) (length * 1.2)); + sb.append("select 1"); + Random rnd = new Random(); + rnd.setSeed(424242); + for (; sb.length() < length;) { + for (int i = 0; i < 7 && sb.length() < length; i++) { + sb.append(", "); + switch (rnd.nextInt(3)) { + case 0: + sb.append("?"); + break; + case 1: + sb.append(rnd.nextInt()); + break; + case 2: + sb.append('\'').append(rnd.nextLong()).append(rnd.nextLong()) + .append('\''); + break; + } + } + if (comments && sb.length() < length) { + sb.append("// sb.append('\\'').append(rnd.nextLong()).append(rnd.nextLong()).append(rnd" + + ".nextLong())"); + } + sb.append('\n'); + } + sb.append(" from dual"); + parser = SqlParser.create("values(1)"); + sql = sb.toString(); + } + + @Benchmark + public SqlNode parseCached() throws SqlParseException { + return parser.parseQuery(sql); + } + + @Benchmark + public SqlNode parseNonCached() throws SqlParseException { + return SqlParser.create(sql).parseQuery(); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(ParserBenchmark.class.getSimpleName()) + .addProfiler(GCProfiler.class) + .addProfiler(FlightRecorderProfiler.class) + .detectJvmArgs() + .build(); + + new Runner(opt).run(); + } + +} + +// End ParserBenchmark.java http://git-wip-us.apache.org/repos/asf/calcite/blob/4a29b3cc/ubenchmark/src/main/java/org/apache/calcite/benchmarks/PreconditionTest.java ---------------------------------------------------------------------- diff --git a/ubenchmark/src/main/java/org/apache/calcite/benchmarks/PreconditionTest.java b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/PreconditionTest.java new file mode 100644 index 0000000..c150a49 --- /dev/null +++ b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/PreconditionTest.java @@ -0,0 +1,56 @@ +/* + * 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 + * + * http://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.calcite.benchmarks; + +import com.google.common.base.Preconditions; + +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.openjdk.jmh.profile.GCProfiler; +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; + +/** + * Checks if silent precondition has noticeable overhead + */ +@BenchmarkMode(Mode.AverageTime) +@State(Scope.Benchmark) +public class PreconditionTest { + boolean fire = false; + String param = "world"; + + public void testPrecondition() { + Preconditions.checkState(fire, "Hello %s", param); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(PreconditionTest.class.getSimpleName()) + .addProfiler(GCProfiler.class) + .detectJvmArgs() + .build(); + + new Runner(opt).run(); + } + +} + +// End PreconditionTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/4a29b3cc/ubenchmark/src/main/java/org/apache/calcite/benchmarks/StatementTest.java ---------------------------------------------------------------------- diff --git a/ubenchmark/src/main/java/org/apache/calcite/benchmarks/StatementTest.java b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/StatementTest.java new file mode 100644 index 0000000..d716fdf --- /dev/null +++ b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/StatementTest.java @@ -0,0 +1,264 @@ +/* + * 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 + * + * http://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.calcite.benchmarks; + +import org.apache.calcite.adapter.java.ReflectiveSchema; +import org.apache.calcite.jdbc.CalciteConnection; +import org.apache.calcite.schema.SchemaPlus; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +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 java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.Random; + +/** + * Compares {@link java.sql.Statement} vs {@link java.sql.PreparedStatement}. + * + * <p>This package contains micro-benchmarks to test calcite performance. + * + * <p>To run this and other benchmarks: + * + * <blockquote> + * <code>mvn package && + * java -jar ./target/ubenchmarks.jar -wi 5 -i 5 -f 1</code> + * </blockquote> + * + * <p>To run with profiling: + * + * <blockquote> + * <code>java -Djmh.stack.lines=10 -jar ./target/ubenchmarks.jar + * -prof hs_comp,hs_gc,stack -f 1 -wi 5</code> + * </blockquote> + */ +public class StatementTest { + + /** + * Connection to be used during tests. + */ + @State(Scope.Thread) + @BenchmarkMode(Mode.AverageTime) + public static class HrConnection { + Connection con; + int id; + HrSchema hr = new HrSchema(); + Random rnd = new Random(); + { + try { + Class.forName("org.apache.calcite.jdbc.Driver"); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + } + Connection connection; + + try { + Properties info = new Properties(); + info.put("lex", "JAVA"); + info.put("quoting", "DOUBLE_QUOTE"); + connection = DriverManager.getConnection("jdbc:calcite:", info); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + CalciteConnection calciteConnection; + try { + calciteConnection = connection.unwrap(CalciteConnection.class); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + final SchemaPlus rootSchema = calciteConnection.getRootSchema(); + rootSchema.add("hr", new ReflectiveSchema(new HrSchema())); + try { + calciteConnection.setSchema("hr"); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + con = connection; + } + + @Setup(Level.Iteration) + public void pickEmployee() { + id = hr.emps[rnd.nextInt(4)].empid; + } + } + + /** + * Tests performance of reused execution of prepared statement. + */ + public static class HrPreparedStatement extends HrConnection { + PreparedStatement ps; + { + try { + ps = con.prepareStatement("select name from emps where empid = ?"); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + } + } + + @Benchmark + public String prepareBindExecute(HrConnection state) throws SQLException { + Connection con = state.con; + Statement st = null; + ResultSet rs = null; + String ename = null; + try { + final PreparedStatement ps = + con.prepareStatement("select name from emps where empid = ?"); + st = ps; + ps.setInt(1, state.id); + rs = ps.executeQuery(); + rs.next(); + ename = rs.getString(1); + } finally { + close(rs, st); + } + return ename; + } + + @Benchmark + public String bindExecute(HrPreparedStatement state) + throws SQLException { + PreparedStatement st = state.ps; + ResultSet rs = null; + String ename = null; + try { + st.setInt(1, state.id); + rs = st.executeQuery(); + rs.next(); + ename = rs.getString(1); + } finally { + close(rs, null); // Statement is not closed + } + return ename; + } + + @Benchmark + public String executeQuery(HrConnection state) throws SQLException { + Connection con = state.con; + Statement st = null; + ResultSet rs = null; + String ename = null; + try { + st = con.createStatement(); + rs = st.executeQuery("select name from emps where empid = " + state.id); + rs.next(); + ename = rs.getString(1); + } finally { + close(rs, st); + } + return ename; + } + + @Benchmark + public String forEach(HrConnection state) throws SQLException { + final Employee[] emps = state.hr.emps; + for (Employee emp : emps) { + if (emp.empid == state.id) { + return emp.name; + } + } + return null; + } + + private static void close(ResultSet rs, Statement st) { + if (rs != null) { + try { rs.close(); } catch (SQLException e) { /**/ } + } + if (st != null) { + try { st.close(); } catch (SQLException e) { /**/ } + } + } + + /** Pojo schema containing "emps" and "depts" tables. */ + public static class HrSchema { + @Override public String toString() { + return "HrSchema"; + } + + public final Employee[] emps = { + new Employee(100, 10, "Bill", 10000, 1000), + new Employee(200, 20, "Eric", 8000, 500), + new Employee(150, 10, "Sebastian", 7000, null), + new Employee(110, 10, "Theodore", 11500, 250), + }; + public final Department[] depts = { + new Department(10, "Sales", Arrays.asList(emps[0], emps[2])), + new Department(30, "Marketing", Collections.<Employee>emptyList()), + new Department(40, "HR", Collections.singletonList(emps[1])), + }; + } + + /** Employee record. */ + public static class Employee { + public final int empid; + public final int deptno; + public final String name; + public final float salary; + public final Integer commission; + + public Employee(int empid, int deptno, String name, float salary, + Integer commission) { + this.empid = empid; + this.deptno = deptno; + this.name = name; + this.salary = salary; + this.commission = commission; + } + + public String toString() { + return "Employee [empid: " + empid + ", deptno: " + deptno + + ", name: " + name + "]"; + } + } + + /** Department record. */ + public static class Department { + public final int deptno; + public final String name; + public final List<Employee> employees; + + public Department( + int deptno, String name, List<Employee> employees) { + this.deptno = deptno; + this.name = name; + this.employees = employees; + } + + + public String toString() { + return "Department [deptno: " + deptno + ", name: " + name + + ", employees: " + employees + "]"; + } + } + +} + +// End StatementTest.java http://git-wip-us.apache.org/repos/asf/calcite/blob/4a29b3cc/ubenchmark/src/main/java/org/apache/calcite/benchmarks/package-info.java ---------------------------------------------------------------------- diff --git a/ubenchmark/src/main/java/org/apache/calcite/benchmarks/package-info.java b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/package-info.java new file mode 100644 index 0000000..3146cae --- /dev/null +++ b/ubenchmark/src/main/java/org/apache/calcite/benchmarks/package-info.java @@ -0,0 +1,26 @@ +/* + * 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 + * + * http://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. + */ + +/** + * JMH benchmarks for Calcite + */ +@PackageMarker +package org.apache.calcite.benchmarks; + +import org.apache.calcite.avatica.util.PackageMarker; + +// End package-info.java
