[feature](Nereids): add rule for matching plan into HyperGraph. (#13805)

This commit is contained in:
jakevin
2022-11-01 14:57:25 +08:00
committed by GitHub
parent 942611c185
commit 83e55cade8
12 changed files with 266 additions and 67 deletions

View File

@ -33,7 +33,7 @@ import org.apache.doris.nereids.memo.GroupExpression;
import org.apache.doris.nereids.processor.post.PlanPostProcessors;
import org.apache.doris.nereids.processor.pre.PlanPreprocessors;
import org.apache.doris.nereids.properties.PhysicalProperties;
import org.apache.doris.nereids.rules.exploration.join.JoinReorderRule;
import org.apache.doris.nereids.rules.joinreorder.HyperGraphJoinReorder;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
@ -173,7 +173,7 @@ public class NereidsPlanner extends Planner {
private void joinReorder() {
new RewriteTopDownJob(
getRoot(),
(new JoinReorderRule()).buildRules(),
(new HyperGraphJoinReorder()).buildRules(),
cascadesContext.getCurrentJobContext()
).execute();
}

View File

@ -36,6 +36,9 @@ public class JoinReorderContext {
private boolean hasRightAssociate = false;
private boolean hasLeftAssociate = false;
// mark for whether it has applied HyperGraph.
private boolean hasHyperReorder = false;
public JoinReorderContext() {
}
@ -110,4 +113,12 @@ public class JoinReorderContext {
public void setHasCommuteZigZag(boolean hasCommuteZigZag) {
this.hasCommuteZigZag = hasCommuteZigZag;
}
public boolean hasHyperReorder() {
return hasHyperReorder;
}
public void setHasHyperReorder(boolean hasHyperReorder) {
this.hasHyperReorder = hasHyperReorder;
}
}

View File

@ -0,0 +1,49 @@
// 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.doris.nereids.rules.joinreorder;
import org.apache.doris.nereids.rules.Rule;
import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.joinreorder.hypergraph.HyperGraph;
import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
/**
* This rule is for Join Reorder (non Cascades Transfrom Join Reorder).
*/
public class HyperGraphJoinReorder extends OneRewriteRuleFactory {
@Override
public Rule build() {
// TODO: we need a pattern to match a subtree of join and mark the node in this tree ordered
return logicalJoin(
subTree(LogicalJoin.class, LogicalProject.class),
subTree(LogicalJoin.class, LogicalProject.class))
.thenApply(ctx -> {
LogicalJoin<? extends Plan, ? extends Plan> rootJoin = ctx.root;
// TODO: check mark.
HyperGraph graph = HyperGraph.fromPlan(rootJoin);
System.out.println(graph.toDottyHyperGraph());
if (graph.optimize()) {
return graph.toPlan();
}
return null;
}).toRule(RuleType.JOIN_REORDER);
}
}

View File

@ -15,26 +15,34 @@
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.nereids.rules.exploration.join;
package org.apache.doris.nereids.rules.joinreorder;
import org.apache.doris.nereids.rules.Rule;
import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.exploration.join.hypergraph.HyperGraph;
import org.apache.doris.nereids.rules.joinreorder.hypergraph.HyperGraph;
import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
/**
* Join Reorder Rule.
* This rule is for Join Reorder (non Cascades Transfrom Join Reorder).
*/
public class JoinReorderRule extends OneRewriteRuleFactory {
public class HyperGraphJoinReorderGroupPlan extends OneRewriteRuleFactory {
@Override
public Rule build() {
// TODO: we need a pattern to match a subtree of join and mark the node in this tree ordered
return logicalJoin(group(), group()).thenApply(ctx -> {
HyperGraph graph = HyperGraph.fromPlan(ctx.root);
if (graph.optimize()) {
return graph.toPlan();
}
return ctx.root;
}).toRule(RuleType.JOIN_REORDER);
return logicalJoin(
subTree(LogicalJoin.class, LogicalProject.class),
group())
.thenApply(ctx -> {
LogicalJoin<? extends Plan, ? extends Plan> rootJoin = ctx.root;
HyperGraph graph = HyperGraph.fromPlan(rootJoin);
System.out.println(graph.toDottyHyperGraph());
if (graph.optimize()) {
return graph.toPlan();
}
return null;
}).toRule(RuleType.JOIN_REORDER);
}
}

View File

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.nereids.rules.exploration.join.hypergraph;
package org.apache.doris.nereids.rules.joinreorder.hypergraph;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;

View File

@ -15,14 +15,16 @@
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.nereids.rules.exploration.join.hypergraph;
package org.apache.doris.nereids.rules.joinreorder.hypergraph;
import org.apache.doris.nereids.trees.expressions.EqualTo;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.plans.GroupPlan;
import org.apache.doris.nereids.trees.plans.JoinType;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import java.util.ArrayList;
import java.util.BitSet;
@ -64,17 +66,31 @@ public class HyperGraph {
}
private void buildGraph(Plan plan) {
if (!(plan instanceof LogicalJoin)) {
if ((plan instanceof LogicalProject && plan.child(0) instanceof GroupPlan)
|| plan instanceof GroupPlan) {
nodes.add(new Node(nodes.size(), plan));
return;
}
LogicalJoin<? extends Plan, ? extends Plan> join = (LogicalJoin<? extends Plan, ? extends Plan>) plan;
// Now we only support inner join
LogicalJoin<? extends Plan, ? extends Plan> join;
if (plan instanceof LogicalProject) {
LogicalProject<? extends Plan> project = (LogicalProject<? extends Plan>) plan;
join = (LogicalJoin<? extends Plan, ? extends Plan>) project.child();
// Handle project
// Ignore the projection expression just using for selection column.
// TODO: how to handle Alias and complex project expression
} else {
join = (LogicalJoin<? extends Plan, ? extends Plan>) plan;
}
// Now we only support inner join with Inside-Project
// TODO: Other joins can be added according CD-C algorithm
if (join.getJoinType() != JoinType.INNER_JOIN) {
nodes.add(new Node(nodes.size(), plan));
return;
}
buildGraph(join.left());
buildGraph(join.right());
addEdge(join);
@ -83,9 +99,9 @@ public class HyperGraph {
private BitSet findNode(Set<Slot> slots) {
BitSet bitSet = new BitSet();
for (Node node : nodes) {
for (Slot slot : node.plan.getOutput()) {
for (Slot slot : node.getPlan().getOutput()) {
if (slots.contains(slot)) {
bitSet.set(node.index);
bitSet.set(node.getIndex());
break;
}
}
@ -124,7 +140,7 @@ public class HyperGraph {
builder.append(String.format("digraph G { # %d edges\n", edges.size() / 2));
List<String> graphvisNodes = new ArrayList<>();
for (Node node : nodes) {
String nodeName = node.plan.getType().name() + node.index;
String nodeName = node.getPlan().getType().name() + node.getIndex();
// nodeID is used to identify the node with the same name
String nodeID = nodeName;
while (graphvisNodes.contains(nodeID)) {

View File

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.nereids.rules.exploration.join.hypergraph;
package org.apache.doris.nereids.rules.joinreorder.hypergraph;
import org.apache.doris.nereids.trees.plans.Plan;
@ -28,25 +28,57 @@ import java.util.List;
* HyperGraph Node.
*/
class Node {
final int index;
Plan plan;
private final int index;
private Plan plan;
// We split these into simple edges (only one node on each side) and complex edges (others)
// because we can often quickly discard all simple edges by testing the set of interesting nodes
// against the simple_neighborhood bitmap.
List<Edge> complexEdges = Lists.newArrayList();
List<Edge> simpleEdges = Lists.newArrayList();
private List<Edge> complexEdges = Lists.newArrayList();
private List<Edge> simpleEdges = Lists.newArrayList();
BitSet simpleNeighborhood = new BitSet();
private BitSet simpleNeighborhood = new BitSet();
public Node(int index, Plan plan) {
this.plan = plan;
this.index = index;
}
public int getIndex() {
return index;
}
public Plan getPlan() {
return plan;
}
public void setPlan(Plan plan) {
this.plan = plan;
}
public List<Edge> getComplexEdges() {
return complexEdges;
}
public void setComplexEdges(List<Edge> complexEdges) {
this.complexEdges = complexEdges;
}
public List<Edge> getSimpleEdges() {
return simpleEdges;
}
public void setSimpleEdges(List<Edge> simpleEdges) {
this.simpleEdges = simpleEdges;
}
public BitSet getSimpleNeighborhood() {
return simpleNeighborhood;
}
public void setSimpleNeighborhood(BitSet simpleNeighborhood) {
this.simpleNeighborhood = simpleNeighborhood;
}
public void attachEdge(Edge edge) {
if (edge.isSimple()) {
simpleEdges.add(edge);

View File

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.nereids.rules.exploration.join.hypergraph;
package org.apache.doris.nereids.rules.joinreorder.hypergraph;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
@ -26,7 +26,7 @@ import java.util.BitSet;
import java.util.HashMap;
/**
* The Receiver is used for cached the plan that has been emit and build the new plan
* The Receiver is used for cached the plan that has been emitted and build the new plan
*/
public class Receiver {
// limit define the max number of csg-cmp pair in this Receiver
@ -60,7 +60,7 @@ public class Receiver {
public void addPlan(Node node) {
BitSet bitSet = new BitSet();
bitSet.set(node.index);
bitSet.set(node.getIndex());
planMap.put(bitSet, node.getPlan());
}

View File

@ -1,26 +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.doris.nereids.rules.exploration.join;
import org.junit.jupiter.api.Test;
class JoinReorderTest {
@Test
void testHyperGraph() {
}
}

View File

@ -0,0 +1,51 @@
// 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.doris.nereids.rules.joinreorder;
import org.apache.doris.common.Pair;
import org.apache.doris.nereids.trees.plans.JoinType;
import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.util.LogicalPlanBuilder;
import org.apache.doris.nereids.util.MemoTestUtils;
import org.apache.doris.nereids.util.PlanChecker;
import org.apache.doris.nereids.util.PlanConstructor;
import org.junit.jupiter.api.Test;
class HyperGraphJoinReorderGroupPlanTest {
private final LogicalOlapScan scan1 = PlanConstructor.newLogicalOlapScan(0, "t1", 0);
private final LogicalOlapScan scan2 = PlanConstructor.newLogicalOlapScan(1, "t2", 0);
private final LogicalOlapScan scan3 = PlanConstructor.newLogicalOlapScan(2, "t3", 0);
private final LogicalOlapScan scan4 = PlanConstructor.newLogicalOlapScan(3, "t4", 0);
private final LogicalOlapScan scan5 = PlanConstructor.newLogicalOlapScan(4, "t5", 0);
@Test
void test() {
LogicalPlan plan = new LogicalPlanBuilder(scan1)
.hashJoinUsing(scan2, JoinType.INNER_JOIN, Pair.of(0, 1))
.hashJoinUsing(scan3, JoinType.INNER_JOIN, Pair.of(0, 1))
.hashJoinUsing(scan4, JoinType.INNER_JOIN, Pair.of(0, 1))
.hashJoinUsing(scan5, JoinType.INNER_JOIN, Pair.of(0, 1))
.build();
PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
.applyTopDown(new HyperGraphJoinReorderGroupPlan())
.printlnTree();
}
}

View File

@ -0,0 +1,55 @@
// 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.doris.nereids.rules.joinreorder;
import org.apache.doris.common.Pair;
import org.apache.doris.nereids.trees.plans.JoinType;
import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.util.LogicalPlanBuilder;
import org.apache.doris.nereids.util.MemoTestUtils;
import org.apache.doris.nereids.util.PlanChecker;
import org.apache.doris.nereids.util.PlanConstructor;
import org.junit.jupiter.api.Test;
class HyperGraphJoinReorderTest {
private final LogicalOlapScan scan1 = PlanConstructor.newLogicalOlapScan(0, "t1", 0);
private final LogicalOlapScan scan2 = PlanConstructor.newLogicalOlapScan(1, "t2", 0);
private final LogicalOlapScan scan3 = PlanConstructor.newLogicalOlapScan(2, "t3", 0);
private final LogicalOlapScan scan4 = PlanConstructor.newLogicalOlapScan(3, "t4", 0);
private final LogicalOlapScan scan5 = PlanConstructor.newLogicalOlapScan(4, "t5", 0);
@Test
void test() {
LogicalPlan plan = new LogicalPlanBuilder(scan1)
.hashJoinUsing(scan2, JoinType.INNER_JOIN, Pair.of(0, 1))
.hashJoinUsing(scan3, JoinType.INNER_JOIN, Pair.of(0, 1))
.hashJoinUsing(
new LogicalPlanBuilder(scan4)
.hashJoinUsing(scan5, JoinType.INNER_JOIN, Pair.of(0, 1))
.build(),
JoinType.INNER_JOIN, Pair.of(0, 1)
)
.build();
PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
.applyTopDown(new HyperGraphJoinReorder())
.printlnTree();
}
}

View File

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.nereids.rules.exploration.join.hypergraph;
package org.apache.doris.nereids.rules.joinreorder.hypergraph;
import org.apache.doris.common.Pair;
import org.apache.doris.nereids.trees.plans.JoinType;
@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.util.LogicalPlanBuilder;
import org.apache.doris.nereids.util.PlanConstructor;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
public class HyperGraphTest {
@ -34,6 +35,7 @@ public class HyperGraphTest {
private final LogicalOlapScan scan5 = PlanConstructor.newLogicalOlapScan(4, "t5", 0);
@Test
@Disabled
void testDottyHyperGraph() {
LogicalPlan joinCluster = new LogicalPlanBuilder(scan1)
.hashJoinUsing(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
@ -41,19 +43,20 @@ public class HyperGraphTest {
.hashJoinUsing(scan4, JoinType.INNER_JOIN, Pair.of(0, 0))
.hashJoinUsing(scan5, JoinType.INNER_JOIN, Pair.of(0, 0))
.build();
HyperGraph hyperGraph = HyperGraph.fromPlan(joinCluster);
String dottyGraph = hyperGraph.toDottyHyperGraph();
// This is a star join, which can be transformed to a image by graphviz.
assert dottyGraph.equals("digraph G { # 4 edges\n"
+ " LOGICAL_OLAP_SCAN0 [label=\"LOGICAL_OLAP_SCAN0\"];\n"
+ " LOGICAL_OLAP_SCAN1 [label=\"LOGICAL_OLAP_SCAN1\"];\n"
+ " LOGICAL_OLAP_SCAN2 [label=\"LOGICAL_OLAP_SCAN2\"];\n"
+ " LOGICAL_OLAP_SCAN3 [label=\"LOGICAL_OLAP_SCAN3\"];\n"
+ " LOGICAL_OLAP_SCAN4 [label=\"LOGICAL_OLAP_SCAN4\"];\n"
+ "LOGICAL_OLAP_SCAN0 -> LOGICAL_OLAP_SCAN1 [label=\"1.0\",arrowhead=none]\n"
+ "LOGICAL_OLAP_SCAN0 -> LOGICAL_OLAP_SCAN2 [label=\"1.0\",arrowhead=none]\n"
+ "LOGICAL_OLAP_SCAN0 -> LOGICAL_OLAP_SCAN3 [label=\"1.0\",arrowhead=none]\n"
+ "LOGICAL_OLAP_SCAN0 -> LOGICAL_OLAP_SCAN4 [label=\"1.0\",arrowhead=none]\n"
+ "}\n") : dottyGraph;
+ " LOGICAL_OLAP_SCAN0 [label=\"LOGICAL_OLAP_SCAN0\"];\n"
+ " LOGICAL_OLAP_SCAN1 [label=\"LOGICAL_OLAP_SCAN1\"];\n"
+ " LOGICAL_OLAP_SCAN2 [label=\"LOGICAL_OLAP_SCAN2\"];\n"
+ " LOGICAL_OLAP_SCAN3 [label=\"LOGICAL_OLAP_SCAN3\"];\n"
+ " LOGICAL_OLAP_SCAN4 [label=\"LOGICAL_OLAP_SCAN4\"];\n"
+ "LOGICAL_OLAP_SCAN0 -> LOGICAL_OLAP_SCAN1 [label=\"1.0\",arrowhead=none]\n"
+ "LOGICAL_OLAP_SCAN0 -> LOGICAL_OLAP_SCAN2 [label=\"1.0\",arrowhead=none]\n"
+ "LOGICAL_OLAP_SCAN0 -> LOGICAL_OLAP_SCAN3 [label=\"1.0\",arrowhead=none]\n"
+ "LOGICAL_OLAP_SCAN0 -> LOGICAL_OLAP_SCAN4 [label=\"1.0\",arrowhead=none]\n"
+ "}\n") : dottyGraph;
}
}