You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by pu...@apache.org on 2016/09/27 15:11:11 UTC

[3/5] incubator-rya git commit: RYA-151 Implemented a PCJOptimizer benchmark tool.

RYA-151 Implemented a PCJOptimizer benchmark tool.


Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/3e17a258
Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/3e17a258
Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/3e17a258

Branch: refs/heads/master
Commit: 3e17a2582a9ad50c2126c038f38389bf7a83c6aa
Parents: e77e839
Author: Kevin Chilton <ke...@parsons.com>
Authored: Fri Sep 2 16:30:00 2016 -0400
Committer: pujav65 <pu...@gmail.com>
Committed: Tue Sep 27 11:05:07 2016 -0400

----------------------------------------------------------------------
 .../java/mvm/rya/api/client/BatchUpdatePCJ.java |  18 +
 .../api/client/PCJDoesNotExistException.java    |  18 +
 .../client/accumulo/AccumuloBatchUpdatePCJ.java |  18 +
 .../tupleSet/SimpleExternalTupleSet.java        |  73 +---
 .../rya/indexing/pcj/matching/PCJOptimizer.java |   6 +-
 .../accumulo/AccumuloBatchUpdatePCJIT.java      |  18 +
 .../tupleSet/SimpleExternalTupleSetTest.java    | 173 ++++++++
 .../benchmark/query/PCJOptimizerBenchmark.java  | 421 +++++++++++++++++++
 8 files changed, 691 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3e17a258/common/rya.api/src/main/java/mvm/rya/api/client/BatchUpdatePCJ.java
----------------------------------------------------------------------
diff --git a/common/rya.api/src/main/java/mvm/rya/api/client/BatchUpdatePCJ.java b/common/rya.api/src/main/java/mvm/rya/api/client/BatchUpdatePCJ.java
index 20d90e0..d6f3454 100644
--- a/common/rya.api/src/main/java/mvm/rya/api/client/BatchUpdatePCJ.java
+++ b/common/rya.api/src/main/java/mvm/rya/api/client/BatchUpdatePCJ.java
@@ -1,3 +1,21 @@
+/*
+ * 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 mvm.rya.api.client;
 
 import javax.annotation.ParametersAreNonnullByDefault;

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3e17a258/common/rya.api/src/main/java/mvm/rya/api/client/PCJDoesNotExistException.java
----------------------------------------------------------------------
diff --git a/common/rya.api/src/main/java/mvm/rya/api/client/PCJDoesNotExistException.java b/common/rya.api/src/main/java/mvm/rya/api/client/PCJDoesNotExistException.java
index 63efe0c..89f095f 100644
--- a/common/rya.api/src/main/java/mvm/rya/api/client/PCJDoesNotExistException.java
+++ b/common/rya.api/src/main/java/mvm/rya/api/client/PCJDoesNotExistException.java
@@ -1,3 +1,21 @@
+/*
+ * 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 mvm.rya.api.client;
 
 import javax.annotation.ParametersAreNonnullByDefault;

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3e17a258/extras/indexing/src/main/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJ.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJ.java b/extras/indexing/src/main/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJ.java
index ee773b0..53f29f4 100644
--- a/extras/indexing/src/main/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJ.java
+++ b/extras/indexing/src/main/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJ.java
@@ -1,3 +1,21 @@
+/*
+ * 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 mvm.rya.api.client.accumulo;
 
 import static java.util.Objects.requireNonNull;

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3e17a258/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/SimpleExternalTupleSet.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/SimpleExternalTupleSet.java b/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/SimpleExternalTupleSet.java
index 2c5ef44..ccdb7a8 100644
--- a/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/SimpleExternalTupleSet.java
+++ b/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/SimpleExternalTupleSet.java
@@ -1,5 +1,3 @@
-package mvm.rya.indexing.external.tupleSet;
-
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -18,13 +16,10 @@ package mvm.rya.indexing.external.tupleSet;
  * specific language governing permissions and limitations
  * under the License.
  */
+package mvm.rya.indexing.external.tupleSet;
 
-import info.aduna.iteration.CloseableIteration;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.openrdf.query.BindingSet;
 import org.openrdf.query.QueryEvaluationException;
@@ -32,51 +27,46 @@ import org.openrdf.query.algebra.Projection;
 import org.openrdf.query.algebra.QueryModelVisitor;
 
 import com.google.common.base.Joiner;
-import com.google.common.collect.Sets;
+
+import info.aduna.iteration.CloseableIteration;
 
 /**
  *  This a testing class to create mock pre-computed join nodes in order to
  *  test the {@link PrecompJoinOptimizer} for query planning.
- *
  */
-
 public class SimpleExternalTupleSet extends ExternalTupleSet {
 
-	public SimpleExternalTupleSet(Projection tuple) {
+    /**
+     * Constructs an instance of {@link SimpleExternalTupleSet}.
+     *
+     * @param tuple - An expression that represents the PCJ. (not null)
+     */
+	public SimpleExternalTupleSet(final Projection tuple) {
 		this.setProjectionExpr(tuple);
 		setSupportedVarOrders();
 	}
 
 	private void setSupportedVarOrders() {
+	    final List<String> varOrders = new ArrayList<>();
 
-		final Set<String> varSet = Sets.newHashSet();
-		final Map<String, Set<String>> supportedVarOrders = new HashMap<>();
-		String t = "";
-
-		for (final String s : this.getTupleExpr().getAssuredBindingNames()) {
-			if (t.length() == 0) {
-				t = s;
-			} else {
-				t = t + VAR_ORDER_DELIM + s;
-			}
+	    String varOrder = "";
+	    for(final String var : this.getTupleExpr().getAssuredBindingNames()) {
+	        varOrder = varOrder.isEmpty() ? var : varOrder + VAR_ORDER_DELIM + var;
+	        varOrders.add( varOrder );
+	    }
 
-			varSet.add(s);
-			supportedVarOrders.put(t, new HashSet<String>(varSet));
-
-		}
-		this.setSupportedVariableOrderMap(supportedVarOrders);
+	    this.setSupportedVariableOrderMap(varOrders);
 	}
 
 	@Override
-	public <X extends Exception> void visit(QueryModelVisitor<X> visitor)
+	public <X extends Exception> void visit(final QueryModelVisitor<X> visitor)
 			throws X {
 		visitor.meetOther(this);
 	}
 
 	@Override
-	public CloseableIteration<BindingSet, QueryEvaluationException> evaluate(
-			BindingSet bindings) throws QueryEvaluationException {
-		// TODO Auto-generated method stub
+	public CloseableIteration<BindingSet, QueryEvaluationException> evaluate( final BindingSet bindings) throws QueryEvaluationException {
+		// Intentionally does nothing.
 		return null;
 	}
 
@@ -88,23 +78,4 @@ public class SimpleExternalTupleSet extends ExternalTupleSet {
 								.getElements()).replaceAll("\\s+", " ");
 
 	}
-
-	@Override
-	public boolean equals(Object other) {
-
-		if (!(other instanceof SimpleExternalTupleSet)) {
-			return false;
-		} else {
-
-			final SimpleExternalTupleSet arg = (SimpleExternalTupleSet) other;
-			if (this.getTupleExpr().equals(arg.getTupleExpr())) {
-				return true;
-			} else {
-				return false;
-			}
-
-		}
-
-	}
-
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3e17a258/extras/indexing/src/main/java/mvm/rya/indexing/pcj/matching/PCJOptimizer.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/pcj/matching/PCJOptimizer.java b/extras/indexing/src/main/java/mvm/rya/indexing/pcj/matching/PCJOptimizer.java
index 046bd53..8ce89bf 100644
--- a/extras/indexing/src/main/java/mvm/rya/indexing/pcj/matching/PCJOptimizer.java
+++ b/extras/indexing/src/main/java/mvm/rya/indexing/pcj/matching/PCJOptimizer.java
@@ -83,8 +83,8 @@ import mvm.rya.indexing.external.tupleSet.ExternalTupleSet;
  *
  */
 public class PCJOptimizer implements QueryOptimizer, Configurable {
-
     private static final Logger log = Logger.getLogger(PCJOptimizer.class);
+
     private List<ExternalTupleSet> indexSet;
     private Configuration conf;
     private boolean init = false;
@@ -104,7 +104,7 @@ public class PCJOptimizer implements QueryOptimizer, Configurable {
         } catch (MalformedQueryException | SailException
                 | QueryEvaluationException | TableNotFoundException
                 | AccumuloException | AccumuloSecurityException | PcjException e) {
-            e.printStackTrace();
+            log.error(e.getMessage(), e);
         }
         init = true;
     }
@@ -352,7 +352,7 @@ public class PCJOptimizer implements QueryOptimizer, Configurable {
         //use table name sparql map (indexTables) to create {@link AccumuloIndexSet}
         final List<ExternalTupleSet> index = Lists.newArrayList();
         if (indexTables.isEmpty()) {
-            System.out.println("No Index found");
+            log.info("No Index found");
         } else {
             for (final String table : indexTables.keySet()) {
                 final String indexSparqlString = indexTables.get(table);

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3e17a258/extras/indexing/src/test/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJIT.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/test/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJIT.java b/extras/indexing/src/test/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJIT.java
index f23f1c4..1f98f88 100644
--- a/extras/indexing/src/test/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJIT.java
+++ b/extras/indexing/src/test/java/mvm/rya/api/client/accumulo/AccumuloBatchUpdatePCJIT.java
@@ -1,3 +1,21 @@
+/*
+ * 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 mvm.rya.api.client.accumulo;
 
 import static org.junit.Assert.assertEquals;

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3e17a258/extras/indexing/src/test/java/mvm/rya/indexing/external/tupleSet/SimpleExternalTupleSetTest.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/test/java/mvm/rya/indexing/external/tupleSet/SimpleExternalTupleSetTest.java b/extras/indexing/src/test/java/mvm/rya/indexing/external/tupleSet/SimpleExternalTupleSetTest.java
new file mode 100644
index 0000000..6354490
--- /dev/null
+++ b/extras/indexing/src/test/java/mvm/rya/indexing/external/tupleSet/SimpleExternalTupleSetTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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 mvm.rya.indexing.external.tupleSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.algebra.Projection;
+import org.openrdf.query.parser.ParsedQuery;
+import org.openrdf.query.parser.sparql.SPARQLParser;
+
+/**
+ * Tests {@link SimpleExternalTupleSet}.
+ */
+public class SimpleExternalTupleSetTest {
+
+    @Test
+    public void equals_equals() throws MalformedQueryException {
+        // The common PCJ expression.
+        final String sparql =
+                "SELECT ?f ?m ?d { " +
+                    "?f <urn:talksTo> ?m . " +
+                    "?m <uri:associatesWith> ?d . " +
+                "}";
+
+        final ParsedQuery query = new SPARQLParser().parseQuery(sparql, null);
+        final Projection pcjExpression = (Projection) query.getTupleExpr();
+
+        // Create two SimpleExternalTupleSet pbjects using the same expression.
+        final SimpleExternalTupleSet testSet = new SimpleExternalTupleSet(pcjExpression);
+        final SimpleExternalTupleSet identicalTestSet = new SimpleExternalTupleSet(pcjExpression);
+
+        // Show that they are equal.
+        assertEquals(testSet, identicalTestSet);
+    }
+
+    @Test
+    public void equals_notEquals() throws MalformedQueryException {
+        // Create the first SimpleExternalTupleSet object.
+        final String sparql1 =
+                "SELECT ?f ?m ?d { " +
+                    "?f <urn:talksTo> ?m . " +
+                    "?m <uri:associatesWith> ?d . " +
+                "}";
+
+        final ParsedQuery query1 = new SPARQLParser().parseQuery(sparql1, null);
+        final Projection pcjExpression1 = (Projection) query1.getTupleExpr();
+        final SimpleExternalTupleSet set1 = new SimpleExternalTupleSet(pcjExpression1);
+
+        // Create another one using a different expression.
+        final String sparql2 =
+                "SELECT ?f ?m ?d { " +
+                    "?f <urn:talksTo> ?m . " +
+                    "?m <uri:friendsWith> ?d . " +
+                "}";
+
+        final ParsedQuery query2 = new SPARQLParser().parseQuery(sparql2, null);
+        final Projection pcjExpression2 = (Projection) query2.getTupleExpr();
+        final SimpleExternalTupleSet set2 = new SimpleExternalTupleSet(pcjExpression2);
+
+        // Show they are not equal.
+        assertNotEquals(set1, set2);
+    }
+
+    @Test
+    public void hashCode_same() throws MalformedQueryException {
+        // The common PCJ expression.
+        final String sparql =
+                "SELECT ?f ?m ?d { " +
+                    "?f <urn:talksTo> ?m . " +
+                    "?m <uri:associatesWith> ?d . " +
+                "}";
+
+        final ParsedQuery query = new SPARQLParser().parseQuery(sparql, null);
+        final Projection pcjExpression = (Projection) query.getTupleExpr();
+
+        // Create two SimpleExternalTupleSet pbjects using the same expression.
+        final SimpleExternalTupleSet testSet = new SimpleExternalTupleSet(pcjExpression);
+        final SimpleExternalTupleSet identicalTestSet = new SimpleExternalTupleSet(pcjExpression);
+
+        // Show that they are equal.
+        assertEquals(testSet.hashCode(), identicalTestSet.hashCode());
+    }
+
+    public void hashCode_notSame() throws MalformedQueryException {
+        // Create the first SimpleExternalTupleSet object.
+        final String sparql1 =
+                "SELECT ?f ?m ?d { " +
+                    "?f <urn:talksTo> ?m . " +
+                    "?m <uri:associatesWith> ?d . " +
+                "}";
+
+        final ParsedQuery query1 = new SPARQLParser().parseQuery(sparql1, null);
+        final Projection pcjExpression1 = (Projection) query1.getTupleExpr();
+        final SimpleExternalTupleSet set1 = new SimpleExternalTupleSet(pcjExpression1);
+
+        // Create another one using a different expression.
+        final String sparql2 =
+                "SELECT ?f ?m ?d { " +
+                    "?f <urn:talksTo> ?m . " +
+                    "?m <uri:friendsWith> ?d . " +
+                "}";
+
+        final ParsedQuery query2 = new SPARQLParser().parseQuery(sparql2, null);
+        final Projection pcjExpression2 = (Projection) query2.getTupleExpr();
+        final SimpleExternalTupleSet set2 = new SimpleExternalTupleSet(pcjExpression2);
+
+        // Show they are not equal.
+        assertNotEquals(set1.hashCode(), set2.hashCode());
+    }
+
+    @Test
+    public void getSupportedVariableOrderMap() throws MalformedQueryException {
+        // Create the PCJ expression.
+        final String sparql =
+                "SELECT ?f ?m ?d { " +
+                    "?f <urn:talksTo> ?m . " +
+                    "?m <uri:associatesWith> ?d . " +
+                "}";
+
+        final ParsedQuery query = new SPARQLParser().parseQuery(sparql, null);
+        final Projection pcjExpression = (Projection) query.getTupleExpr();
+
+        // Create the object that is being tested.
+        final SimpleExternalTupleSet testSet = new SimpleExternalTupleSet(pcjExpression);
+
+        // Verify the correct Supported Variable Order Map is created.
+        final Map<String, Set<String>> expected = new HashMap<>();
+
+        String varOrder = "f";
+        Set<String> vars = new HashSet<>();
+        vars.add("f");
+        expected.put(varOrder, vars);
+
+        varOrder = "f;m";
+        vars = new HashSet<>();
+        vars.add("f");
+        vars.add("m");
+        expected.put(varOrder, vars);
+
+        varOrder = "f;m;d";
+        vars = new HashSet<>();
+        vars.add("f");
+        vars.add("m");
+        vars.add("d");
+        expected.put(varOrder, vars);
+
+        assertEquals(expected, testSet.getSupportedVariableOrderMap());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/3e17a258/extras/rya.benchmark/src/main/java/org/apache/rya/benchmark/query/PCJOptimizerBenchmark.java
----------------------------------------------------------------------
diff --git a/extras/rya.benchmark/src/main/java/org/apache/rya/benchmark/query/PCJOptimizerBenchmark.java b/extras/rya.benchmark/src/main/java/org/apache/rya/benchmark/query/PCJOptimizerBenchmark.java
new file mode 100644
index 0000000..ff6285b
--- /dev/null
+++ b/extras/rya.benchmark/src/main/java/org/apache/rya/benchmark/query/PCJOptimizerBenchmark.java
@@ -0,0 +1,421 @@
+/**
+ * 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.rya.benchmark.query;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
+import org.openjdk.jmh.annotations.Benchmark;
+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.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.CommandLineOptionException;
+import org.openjdk.jmh.runner.options.CommandLineOptions;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.algebra.Projection;
+import org.openrdf.query.algebra.TupleExpr;
+import org.openrdf.query.parser.sparql.SPARQLParser;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+
+import mvm.rya.indexing.external.tupleSet.ExternalTupleSet;
+import mvm.rya.indexing.external.tupleSet.SimpleExternalTupleSet;
+import mvm.rya.indexing.pcj.matching.PCJOptimizer;
+
+/**
+ * A benchmark that may be used to evaluate the performance of {@link PCJOptimizer}.
+ * It pivots over three dimensions:
+ * <ul>
+ *     <li>How many Statement Patterns the optimized query has.</li>
+ *     <li>How many PCJ indices the optimizer has available to it.</li>
+ *     <li>How many Statement Patterns each PCJ has.</li>
+ * </ul>
+ * To execute this benchmark, build the project by executing:
+ * <pre>
+ * mvn clean install
+ * </pre>
+ * Transport the "target/benchmarking.jar" file to the system that will execute
+ * the benchmark, write the configuration file, and then execute:
+ * <pre>
+ * java -cp benchmarks.jar org.apache.rya.benchmark.query.PCJOptimizerBenchmark
+ * </pre>
+ */
+@State(Scope.Thread)
+@ParametersAreNonnullByDefault
+public class PCJOptimizerBenchmark {
+
+    /**
+     * Variables that may be used when building SPARQL queries.
+     */
+    private static final List<String> variables = Lists.newArrayList("?a","?b",
+            "?c","?d","?e","?f","?g","?h","?i","?j","?k","?l","?m","?n","?o",
+            "?p","?q","?r","?s","?t","?u","?v","?w","?x","?y","?z");
+
+    // Parameters that effect which PCJs are used by the benchmark.
+    @Param({"0", "1", "2", "3", "4", "5", "6"})
+    public int numPCJs;
+
+    @Param({"2", "3", "4", "5", "6"})
+    public int pcjSPCount;
+
+    // Parameters that effect the Query that is being optimized by the benchmark.
+    @Param({"1", "2", "3", "4", "5", "6"})
+    public int querySPCount;
+
+    // Cached benchmark data that is generated during the setup phase.
+    private final Map<BenchmarkParams, BenchmarkValues> chainedBenchmarkValues = new HashMap<>();
+    private final Map<BenchmarkParams, BenchmarkValues> unchainedBenchmarkValues = new HashMap<>();
+
+    @Setup
+    public void buildBenchmarkValues() throws MalformedQueryException {
+        for(int numPCJs = 0; numPCJs <= 6; numPCJs++) {
+            for(int pcjSPCount = 2; pcjSPCount <= 6; pcjSPCount++) {
+                for(int querySPCount = 1; querySPCount <= 6; querySPCount++) {
+                    final BenchmarkParams benchmarkParams = new BenchmarkParams(numPCJs, pcjSPCount, querySPCount);
+
+                    final BenchmarkValues chainedValues = new BenchmarkValues(
+                            makeChainedQuery(benchmarkParams),
+                            makeChainedPCJOptimizer(benchmarkParams));
+                    this.chainedBenchmarkValues.put(benchmarkParams, chainedValues);
+
+                    final BenchmarkValues unchainedValues = new BenchmarkValues(
+                            makeUnchainedQuery(benchmarkParams),
+                            makeUnchainedPCJOptimizer(benchmarkParams));
+                    this.unchainedBenchmarkValues.put(benchmarkParams, unchainedValues);
+                }
+            }
+        }
+    }
+
+    @Benchmark
+    public void optimizeQuery_unchained() throws MalformedQueryException {
+        // Fetch the pieces that benchmark uses.
+        final BenchmarkValues values = unchainedBenchmarkValues.get( new BenchmarkParams(numPCJs, pcjSPCount, querySPCount) );
+        final PCJOptimizer pcjOptimizer = values.getPCJOptimizer();
+        final TupleExpr query = values.getQuery();
+
+        // Perform the optimization.
+        pcjOptimizer.optimize(query, null, null);
+    }
+
+    @Benchmark
+    public void optimizeQuery_chained() throws MalformedQueryException {
+        // Fetch the pieces that benchmark uses.
+        final BenchmarkValues values = chainedBenchmarkValues.get( new BenchmarkParams(numPCJs, pcjSPCount, querySPCount) );
+        final PCJOptimizer pcjOptimizer = values.getPCJOptimizer();
+        final TupleExpr query = values.getQuery();
+
+        // Perform the optimization.
+        pcjOptimizer.optimize(query, null, null);
+    }
+
+    private static TupleExpr makeUnchainedQuery(final BenchmarkParams params) throws MalformedQueryException {
+        final Queue<String> varQueue= Lists.newLinkedList(variables);
+        final SPARQLParser parser = new SPARQLParser();
+
+        final List<String> queryVars = new ArrayList<>();
+
+        // The first statement pattern has two variables.
+        queryVars.add( varQueue.remove() );
+        queryVars.add( varQueue.remove() );
+
+        // The each extra statement pattern joins with the previous one, so only need one more variable each.
+        for(int i = 1; i < params.getQuerySPCount(); i++) {
+            queryVars.add( varQueue.remove() );
+            queryVars.add( varQueue.remove() );
+        }
+
+        final String sparql = buildUnchainedSPARQL(queryVars);
+        return parser.parseQuery(sparql, null).getTupleExpr();
+    }
+
+    private static TupleExpr makeChainedQuery(final BenchmarkParams params) throws MalformedQueryException {
+        final Queue<String> varQueue= Lists.newLinkedList(variables);
+        final SPARQLParser parser = new SPARQLParser();
+
+        final List<String> queryVars = new ArrayList<>();
+
+        // The first statement pattern has two variables.
+        queryVars.add( varQueue.remove() );
+        queryVars.add( varQueue.remove() );
+
+        // The each extra statement pattern joins with the previous one, so only need one more variable each.
+        for(int i = 1; i < params.getQuerySPCount(); i++) {
+            queryVars.add( varQueue.remove() );
+        }
+
+        final String sparql = buildChainedSPARQL(queryVars);
+        return parser.parseQuery(sparql, null).getTupleExpr();
+    }
+
+    private static PCJOptimizer makeUnchainedPCJOptimizer(final BenchmarkParams params) throws MalformedQueryException {
+        final Queue<String> varQueue= Lists.newLinkedList(variables);
+        final SPARQLParser parser = new SPARQLParser();
+
+        final List<ExternalTupleSet> indices = new ArrayList<>();
+
+        // Create the first PCJ.
+        final List<String> pcjVars = new ArrayList<>();
+        pcjVars.add( varQueue.remove() );
+        pcjVars.add( varQueue.remove() );
+
+        for(int spI = 1; spI < params.getPCJSPCount(); spI++) {
+            pcjVars.add( varQueue.remove() );
+            pcjVars.add( varQueue.remove() );
+        }
+
+        String pcjSparql = buildUnchainedSPARQL(pcjVars);
+        Projection projection = (Projection) parser.parseQuery(pcjSparql, null).getTupleExpr();
+        indices.add( new SimpleExternalTupleSet(projection) );
+
+        // Add the rest of the PCJs.
+        for(int pcjI = 1; pcjI < params.getNumPCJS(); pcjI++) {
+            // Remove the previous PCJs first variable.
+            pcjVars.remove(0);
+            pcjVars.remove(0);
+
+            // And add a new one to the end of it.
+            pcjVars.add( varQueue.remove() );
+            pcjVars.add( varQueue.remove() );
+
+            // Build the index.
+            pcjSparql = buildUnchainedSPARQL(pcjVars);
+            projection = (Projection) parser.parseQuery(pcjSparql, null).getTupleExpr();
+            indices.add( new SimpleExternalTupleSet(projection) );
+        }
+
+        // Create the optimizer.
+        return new PCJOptimizer(indices, false);
+    }
+
+    private static PCJOptimizer makeChainedPCJOptimizer(final BenchmarkParams params) throws MalformedQueryException {
+        final Queue<String> varQueue= Lists.newLinkedList(variables);
+        final SPARQLParser parser = new SPARQLParser();
+
+        final List<ExternalTupleSet> indices = new ArrayList<>();
+
+        // Create the first PCJ.
+        final List<String> pcjVars = new ArrayList<>();
+        pcjVars.add( varQueue.remove() );
+        pcjVars.add( varQueue.remove() );
+
+        for(int spI = 1; spI < params.getPCJSPCount(); spI++) {
+            pcjVars.add( varQueue.remove() );
+        }
+
+        String pcjSparql = buildChainedSPARQL(pcjVars);
+        Projection projection = (Projection) parser.parseQuery(pcjSparql, null).getTupleExpr();
+        indices.add( new SimpleExternalTupleSet(projection) );
+
+        // Add the rest of the PCJs.
+        for(int pcjI = 1; pcjI < params.getNumPCJS(); pcjI++) {
+            // Remove the previous PCJs first variable.
+            pcjVars.remove(0);
+
+            // And add a new one to the end of it.
+            pcjVars.add( varQueue.remove() );
+
+            // Build the index.
+            pcjSparql = buildChainedSPARQL(pcjVars);
+            projection = (Projection) parser.parseQuery(pcjSparql, null).getTupleExpr();
+            indices.add( new SimpleExternalTupleSet(projection) );
+        }
+
+        // Create the optimizer.
+        return new PCJOptimizer(indices, false);
+    }
+
+    private static String buildUnchainedSPARQL(final List<String> vars) {
+        checkArgument(vars.size() % 2 == 0);
+
+        final Queue<String> varQueue= Lists.newLinkedList(vars);
+        final List<String> statementPatterns = new ArrayList<>();
+
+        // Create the first SP.
+        String var1 = varQueue.remove();
+        String var2 = varQueue.remove();
+        statementPatterns.add( var1 + " <urn:predicate> " + var2);
+
+        // Need two more variables for every following statement pattern.
+        while(!varQueue.isEmpty()) {
+            var1 = varQueue.remove();
+            var2 = varQueue.remove();
+            statementPatterns.add( var1 + " <urn:predicate> " + var2);
+        }
+
+        return "select " + Joiner.on(" ").join(vars) + " where { " +
+                    Joiner.on(" . ").join(statementPatterns) +
+                " . }" ;
+    }
+
+    private static String buildChainedSPARQL(final List<String> vars) {
+        final Queue<String> varQueue= Lists.newLinkedList(vars);
+        final List<String> statementPatterns = new ArrayList<>();
+
+        // Create the first SP.
+        final String var1 = varQueue.remove();
+        final String var2 = varQueue.remove();
+        statementPatterns.add( var1 + " <urn:predicate> " + var2);
+
+        // Chain the rest of the SPs off of each other.
+        String lastVar = var2;
+
+        while(!varQueue.isEmpty()) {
+            final String var = varQueue.remove();
+            statementPatterns.add( lastVar + " <urn:predicate> " + var);
+            lastVar = var;
+        }
+
+        // Build the SPARQL query from the pieces.
+        return "select " + Joiner.on(" ").join(vars) + " where { " +
+                    Joiner.on(" . ").join(statementPatterns) +
+                " . }" ;
+    }
+
+    /**
+     * The parameter values used by the benchmark. Used to lookup a benchmark' {@link BenchmarkValues}.
+     */
+    @ParametersAreNonnullByDefault
+    public static class BenchmarkParams {
+        private final int numPCJs;
+        private final int pcjSPCount;
+        private final int querySPCount;
+
+        /**
+         * Constructs an instance of {@link BenchmarkParams}.
+         *
+         * @param numPCJs - The number of PCJs that will be available to the {@link PCJOptimizer}. (not null)
+         * @param pcjSPCount - The number of Statement Patterns that are in each PCJs. (not null)
+         * @param querySPCount - The number of Statement Patterns that are in the query that will be optimized. (not null)
+         */
+        public BenchmarkParams(final int numPCJs, final int pcjSPCount, final int querySPCount){
+            this.numPCJs = numPCJs;
+            this.pcjSPCount = pcjSPCount;
+            this.querySPCount = querySPCount;
+        }
+
+        /**
+         * @return The number of PCJs that will be available to the {@link PCJOptimizer}.
+         */
+        public int getNumPCJS() {
+            return numPCJs;
+        }
+
+        /**
+         * @return The number of Statement Patterns that are in each PCJs.
+         */
+        public int getPCJSPCount() {
+            return pcjSPCount;
+        }
+
+        /**
+         * @return The number of Statement Patterns that are in the query that will be optimized.
+         */
+        public int getQuerySPCount() {
+            return querySPCount;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(numPCJs, pcjSPCount, querySPCount);
+        }
+
+        @Override
+        public boolean equals(final Object other) {
+            if(this == other) {
+                return true;
+            }
+            if(other instanceof BenchmarkParams) {
+                final BenchmarkParams key = (BenchmarkParams) other;
+                return numPCJs == key.numPCJs &&
+                        pcjSPCount == key.pcjSPCount &&
+                        querySPCount == key.querySPCount;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Holds onto the SPARQL query that will be optimized as well as the optimizers
+     * that will be used to optimize the query.
+     */
+    @ParametersAreNonnullByDefault
+    public static class BenchmarkValues {
+        private final TupleExpr query;
+        private final PCJOptimizer optimizer;
+
+        /**
+         * Constructs an isntance of {@link BenchmarkValues}.
+         *
+         * @param query - The SPARQL query to optimize.
+         * @param optimizer - The optimizer used to optimize the query.
+         */
+        public BenchmarkValues(final TupleExpr query, final PCJOptimizer optimizer) {
+            this.query = requireNonNull(query);
+            this.optimizer = requireNonNull(optimizer);
+        }
+
+        /**
+         * @return The SPARQL query to optimize.
+         */
+        public TupleExpr getQuery() {
+            return query;
+        }
+
+        /**
+         * @return The optimizer used to optimize the query.
+         */
+        public PCJOptimizer getPCJOptimizer() {
+            return optimizer;
+        }
+    }
+
+    /**
+     * Runs the PCJOptimizer benchmarks.
+     * </p>
+     * Example command line:
+     * <pre>
+     * java -cp benchmarks.jar org.apache.rya.benchmark.query.PCJOptimizerBenchmark
+     * </pre>
+     *
+     * @param args - The command line arguments that will be fed into the benchmark.
+     * @throws Exception The benchmark could not be run.
+     */
+    public static void main(final String[] args) throws RunnerException, MalformedQueryException, CommandLineOptionException {
+        final OptionsBuilder opts = new OptionsBuilder();
+        opts.parent( new CommandLineOptions(args) );
+        opts.include(PCJOptimizerBenchmark.class.getSimpleName());
+
+        new Runner(opts.build()).run();
+    }
+}
\ No newline at end of file