[feature](Nereids): Add cache to avoid repeatly calculation in DPhyp (#14585)
This commit is contained in:
@ -36,6 +36,7 @@ public class Edge {
|
||||
// left and right may not overlap, and both must have at least one bit set.
|
||||
private BitSet left = Bitmap.newBitmap();
|
||||
private BitSet right = Bitmap.newBitmap();
|
||||
private BitSet referenceNodes = Bitmap.newBitmap();
|
||||
|
||||
/**
|
||||
* Create simple edge.
|
||||
@ -56,21 +57,25 @@ public class Edge {
|
||||
|
||||
public void addLeftNode(BitSet left) {
|
||||
Bitmap.or(this.left, left);
|
||||
Bitmap.or(referenceNodes, left);
|
||||
}
|
||||
|
||||
public void addLeftNodes(BitSet... bitSets) {
|
||||
for (BitSet bitSet : bitSets) {
|
||||
Bitmap.or(this.left, bitSet);
|
||||
Bitmap.or(referenceNodes, bitSet);
|
||||
}
|
||||
}
|
||||
|
||||
public void addRightNode(BitSet right) {
|
||||
Bitmap.or(this.right, right);
|
||||
Bitmap.or(referenceNodes, right);
|
||||
}
|
||||
|
||||
public void addRightNodes(BitSet... bitSets) {
|
||||
for (BitSet bitSet : bitSets) {
|
||||
Bitmap.or(this.right, bitSet);
|
||||
Bitmap.or(referenceNodes, bitSet);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +84,7 @@ public class Edge {
|
||||
}
|
||||
|
||||
public void setLeft(BitSet left) {
|
||||
referenceNodes.clear();
|
||||
this.left = left;
|
||||
}
|
||||
|
||||
@ -87,18 +93,21 @@ public class Edge {
|
||||
}
|
||||
|
||||
public void setRight(BitSet right) {
|
||||
referenceNodes.clear();
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
public boolean isSub(Edge edge) {
|
||||
// When this join reference nodes is a subset of other join, then this join must appear before that join
|
||||
BitSet bitSet = getReferenceNodes();
|
||||
BitSet otherBitset = edge.getReferenceNodes();
|
||||
return Bitmap.isSubset(bitSet, otherBitset);
|
||||
return Bitmap.isSubset(getReferenceNodes(), otherBitset);
|
||||
}
|
||||
|
||||
public BitSet getReferenceNodes() {
|
||||
return Bitmap.newBitmapUnion(this.left, this.right);
|
||||
if (referenceNodes.cardinality() == 0) {
|
||||
referenceNodes = Bitmap.newBitmapUnion(left, right);
|
||||
}
|
||||
return referenceNodes;
|
||||
}
|
||||
|
||||
public Edge reverse(int index) {
|
||||
|
||||
@ -74,7 +74,7 @@ public class GraphSimplifier {
|
||||
simplifications.add(bestSimplification);
|
||||
}
|
||||
for (Node node : graph.getNodes()) {
|
||||
cachePlan.put(node.getBitSet(), node.getPlan());
|
||||
cachePlan.put(node.getNodeMap(), node.getPlan());
|
||||
}
|
||||
circleDetector = new CircleDetector(edgeSize);
|
||||
}
|
||||
@ -208,12 +208,12 @@ public class GraphSimplifier {
|
||||
SimplificationStep bestStep = bestSimplification.getStep();
|
||||
while (bestSimplification.bestNeighbor == -1 || !circleDetector.tryAddDirectedEdge(bestStep.beforeIndex,
|
||||
bestStep.afterIndex)) {
|
||||
processNeighbors(bestStep.afterIndex, 0, edgeSize);
|
||||
if (priorityQueue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
bestSimplification = priorityQueue.poll();
|
||||
bestSimplification.isInQueue = false;
|
||||
processNeighbors(bestStep.afterIndex, 0, edgeSize);
|
||||
bestStep = bestSimplification.getStep();
|
||||
}
|
||||
return bestStep;
|
||||
|
||||
@ -61,12 +61,6 @@ public class HyperGraph {
|
||||
return nodes.get(index);
|
||||
}
|
||||
|
||||
public void splitEdgesForNodes() {
|
||||
for (Node node : nodes) {
|
||||
node.splitEdges();
|
||||
}
|
||||
}
|
||||
|
||||
public void addNode(Group group) {
|
||||
Preconditions.checkArgument(!group.isJoinGroup());
|
||||
// TODO: replace plan with group expression or others
|
||||
|
||||
@ -79,7 +79,7 @@ public class Node {
|
||||
return index;
|
||||
}
|
||||
|
||||
public BitSet getBitSet() {
|
||||
public BitSet getNodeMap() {
|
||||
return Bitmap.newBitmap(index);
|
||||
}
|
||||
|
||||
@ -87,34 +87,6 @@ public class Node {
|
||||
return group.getLogicalExpression().getPlan();
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach all edge in this node if the edge references this node
|
||||
*
|
||||
@ -133,31 +105,6 @@ public class Node {
|
||||
edges.remove(edge);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function split edge into complex edges and simple edges
|
||||
* We do it after constructing HyperGraph because the edge may be modified
|
||||
* by graph simplifier.
|
||||
*/
|
||||
public void splitEdges() {
|
||||
simpleEdges.clear();
|
||||
Bitmap.clear(simpleNeighborhood);
|
||||
complexEdges.clear();
|
||||
Bitmap.clear(complexNeighborhood);
|
||||
for (Edge edge : edges) {
|
||||
if (edge.isSimple()) {
|
||||
simpleEdges.add(edge);
|
||||
Bitmap.or(simpleNeighborhood, edge.getLeft());
|
||||
Bitmap.or(simpleNeighborhood, edge.getRight());
|
||||
} else {
|
||||
complexEdges.add(edge);
|
||||
Bitmap.or(complexNeighborhood, edge.getLeft());
|
||||
Bitmap.or(complexNeighborhood, edge.getRight());
|
||||
}
|
||||
}
|
||||
Bitmap.unset(simpleNeighborhood, index);
|
||||
Bitmap.unset(complexNeighborhood, index);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return getPlan().getType().name() + index;
|
||||
}
|
||||
|
||||
@ -21,7 +21,11 @@ import org.apache.doris.nereids.jobs.joinorder.hypergraph.bitmap.Bitmap;
|
||||
import org.apache.doris.nereids.jobs.joinorder.hypergraph.bitmap.SubsetIterator;
|
||||
import org.apache.doris.nereids.jobs.joinorder.hypergraph.receiver.AbstractReceiver;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -34,6 +38,9 @@ public class SubgraphEnumerator {
|
||||
AbstractReceiver receiver;
|
||||
//The enumerated hyperGraph
|
||||
HyperGraph hyperGraph;
|
||||
EdgeCalculator edgeCalculator;
|
||||
NeighborhoodCalculator neighborhoodCalculator;
|
||||
// These caches are used to avoid repetitive computation
|
||||
|
||||
public SubgraphEnumerator(AbstractReceiver receiver, HyperGraph hyperGraph) {
|
||||
this.receiver = receiver;
|
||||
@ -50,12 +57,20 @@ public class SubgraphEnumerator {
|
||||
List<Node> nodes = hyperGraph.getNodes();
|
||||
// Init all nodes in Receiver
|
||||
for (Node node : nodes) {
|
||||
receiver.addGroup(node.getBitSet(), node.getGroup());
|
||||
receiver.addGroup(node.getNodeMap(), node.getGroup());
|
||||
}
|
||||
hyperGraph.splitEdgesForNodes();
|
||||
int size = nodes.size();
|
||||
|
||||
// We skip the last element due to it can't generate valid csg-cmp pair
|
||||
// Init edgeCalculator
|
||||
edgeCalculator = new EdgeCalculator(hyperGraph.getEdges());
|
||||
for (Node node : nodes) {
|
||||
edgeCalculator.initSubgraph(node.getNodeMap());
|
||||
}
|
||||
|
||||
// Init neighborhoodCalculator
|
||||
neighborhoodCalculator = new NeighborhoodCalculator();
|
||||
|
||||
// We skip the last element because it can't generate valid csg-cmp pair
|
||||
BitSet forbiddenNodes = Bitmap.newBitmapBetween(0, size - 1);
|
||||
for (int i = size - 2; i >= 0; i--) {
|
||||
BitSet csg = Bitmap.newBitmap(i);
|
||||
@ -70,11 +85,12 @@ public class SubgraphEnumerator {
|
||||
// The general purpose of EnumerateCsgRec is to extend a given set csg, which
|
||||
// induces a connected subgraph of G to a larger set with the same property.
|
||||
private boolean enumerateCsgRec(BitSet csg, BitSet forbiddenNodes) {
|
||||
BitSet neighborhood = calcNeighborhood(csg, forbiddenNodes);
|
||||
BitSet neighborhood = neighborhoodCalculator.calcNeighborhood(csg, forbiddenNodes, edgeCalculator);
|
||||
SubsetIterator subsetIterator = Bitmap.getSubsetIterator(neighborhood);
|
||||
for (BitSet subset : subsetIterator) {
|
||||
BitSet newCsg = Bitmap.newBitmapUnion(csg, subset);
|
||||
if (receiver.contain(newCsg)) {
|
||||
edgeCalculator.unionEdges(csg, subset);
|
||||
if (!emitCsg(newCsg)) {
|
||||
return false;
|
||||
}
|
||||
@ -92,22 +108,20 @@ public class SubgraphEnumerator {
|
||||
}
|
||||
|
||||
private boolean enumerateCmpRec(BitSet csg, BitSet cmp, BitSet forbiddenNodes) {
|
||||
BitSet neighborhood = calcNeighborhood(cmp, forbiddenNodes);
|
||||
BitSet neighborhood = neighborhoodCalculator.calcNeighborhood(cmp, forbiddenNodes, edgeCalculator);
|
||||
SubsetIterator subsetIterator = new SubsetIterator(neighborhood);
|
||||
for (BitSet subset : subsetIterator) {
|
||||
BitSet newCmp = Bitmap.newBitmapUnion(cmp, subset);
|
||||
// We need to check whether Cmp is connected and then try to find hyper edge
|
||||
if (receiver.contain(newCmp)) {
|
||||
// We check all edges for finding an edge. That is inefficient.
|
||||
// MySQL use full neighborhood for it. Or a hashMap may be useful
|
||||
for (Edge edge : hyperGraph.getEdges()) {
|
||||
if (Bitmap.isSubset(edge.getLeft(), csg) && Bitmap.isSubset(edge.getRight(), newCmp) || (
|
||||
Bitmap.isSubset(edge.getLeft(), newCmp) && Bitmap.isSubset(edge.getRight(), csg))) {
|
||||
if (!receiver.emitCsgCmp(csg, newCmp, edge)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
edgeCalculator.unionEdges(cmp, subset);
|
||||
// We check all edges for finding an edge.
|
||||
List<Edge> edges = edgeCalculator.connectCsgCmp(csg, newCmp);
|
||||
if (edges.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!receiver.emitCsgCmp(csg, newCmp, edges)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -128,17 +142,18 @@ public class SubgraphEnumerator {
|
||||
private boolean emitCsg(BitSet csg) {
|
||||
BitSet forbiddenNodes = Bitmap.newBitmapBetween(0, Bitmap.nextSetBit(csg, 0));
|
||||
Bitmap.or(forbiddenNodes, csg);
|
||||
BitSet neighborhoods = calcNeighborhood(csg, Bitmap.newBitmap(forbiddenNodes));
|
||||
BitSet neighborhoods = neighborhoodCalculator.calcNeighborhood(csg, Bitmap.newBitmap(forbiddenNodes),
|
||||
edgeCalculator);
|
||||
|
||||
for (int nodeIndex : Bitmap.getReverseIterator(neighborhoods)) {
|
||||
BitSet cmp = Bitmap.newBitmap(nodeIndex);
|
||||
// whether there is an edge between csg and cmp
|
||||
Node cmpNode = hyperGraph.getNode(nodeIndex);
|
||||
Edge edge = cmpNode.tryGetEdgeWith(csg);
|
||||
if (edge != null) {
|
||||
if (!receiver.emitCsgCmp(csg, cmp, edge)) {
|
||||
return false;
|
||||
}
|
||||
List<Edge> edges = edgeCalculator.connectCsgCmp(csg, cmp);
|
||||
if (edges.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!receiver.emitCsgCmp(csg, cmp, edges)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// In order to avoid enumerate repeated cmp, e.g.,
|
||||
@ -160,33 +175,185 @@ public class SubgraphEnumerator {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function is used to calculate neighborhoods of given subgraph.
|
||||
// Though a direct way is to add all nodes u that satisfies:
|
||||
// <u, v> \in E && v \in subgraph && v \intersect X = empty
|
||||
// We don't used it because they can cause some repeated subgraph when
|
||||
// expand csg and cmp. In fact, we just need a seed node that can be expanded
|
||||
// to all subgraph. That is any one node of hyper nodes. In fact, the neighborhoods
|
||||
// is the minimum set that we choose one node from above v.
|
||||
class NeighborhoodCalculator {
|
||||
// This function is used to calculate neighborhoods of given subgraph.
|
||||
// Though a direct way is to add all nodes u that satisfies:
|
||||
// <u, v> \in E && v \in subgraph && v \intersect X = empty
|
||||
// We don't used it because they can cause some repeated subgraph when
|
||||
// expand csg and cmp. In fact, we just need a seed node that can be expanded
|
||||
// to all subgraph. That is any one node of hyper nodes. In fact, the neighborhoods
|
||||
// is the minimum set that we choose one node from above v.
|
||||
public BitSet calcNeighborhood(BitSet subgraph, BitSet forbiddenNodes, EdgeCalculator edgeCalculator) {
|
||||
BitSet neighborhoods = Bitmap.newBitmap();
|
||||
edgeCalculator.foundSimpleEdgesContain(subgraph)
|
||||
.forEach(edge -> neighborhoods.or(edge.getReferenceNodes()));
|
||||
Bitmap.or(forbiddenNodes, subgraph);
|
||||
Bitmap.andNot(neighborhoods, forbiddenNodes);
|
||||
Bitmap.or(forbiddenNodes, neighborhoods);
|
||||
|
||||
// Note there are many tricks implemented in MySQL, such as neighbor cache, complex edges
|
||||
// We hope implement them after a benchmark.
|
||||
private BitSet calcNeighborhood(BitSet subGraph, BitSet forbiddenNodes) {
|
||||
BitSet neighborhoods = Bitmap.newBitmap();
|
||||
Bitmap.getIterator(subGraph)
|
||||
.forEach(nodeIndex -> Bitmap.or(neighborhoods, hyperGraph.getNode(nodeIndex).getSimpleNeighborhood()));
|
||||
Bitmap.andNot(neighborhoods, forbiddenNodes);
|
||||
Bitmap.or(forbiddenNodes, subGraph);
|
||||
Bitmap.or(forbiddenNodes, neighborhoods);
|
||||
|
||||
for (Edge edge : hyperGraph.getEdges()) {
|
||||
BitSet left = edge.getLeft();
|
||||
BitSet right = edge.getRight();
|
||||
if (Bitmap.isSubset(left, subGraph) && !Bitmap.isOverlap(right, forbiddenNodes)) {
|
||||
Bitmap.set(neighborhoods, right.nextSetBit(0));
|
||||
} else if (Bitmap.isSubset(right, subGraph) && !Bitmap.isOverlap(left, forbiddenNodes)) {
|
||||
Bitmap.set(neighborhoods, left.nextSetBit(0));
|
||||
for (Edge edge : edgeCalculator.foundComplexEdgesContain(subgraph)) {
|
||||
BitSet left = edge.getLeft();
|
||||
BitSet right = edge.getRight();
|
||||
if (Bitmap.isSubset(left, subgraph) && !Bitmap.isOverlap(right, forbiddenNodes)) {
|
||||
Bitmap.set(neighborhoods, right.nextSetBit(0));
|
||||
} else if (Bitmap.isSubset(right, subgraph) && !Bitmap.isOverlap(left, forbiddenNodes)) {
|
||||
Bitmap.set(neighborhoods, left.nextSetBit(0));
|
||||
}
|
||||
}
|
||||
return neighborhoods;
|
||||
}
|
||||
}
|
||||
|
||||
class EdgeCalculator {
|
||||
final List<Edge> edges;
|
||||
// It cached all edges that contained by this subgraph, Note we always
|
||||
// use bitset store edge map because the number of edges can be very large
|
||||
// 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. These data will be calculated before enumerate.
|
||||
|
||||
HashMap<BitSet, BitSet> containSimpleEdges = new HashMap<>();
|
||||
HashMap<BitSet, BitSet> containComplexEdges = new HashMap<>();
|
||||
// It cached all edges that overlap by this subgraph. All this edges must be
|
||||
// complex edges
|
||||
HashMap<BitSet, BitSet> overlapEdges = new HashMap<>();
|
||||
|
||||
EdgeCalculator(List<Edge> edges) {
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public void initSubgraph(BitSet subgraph) {
|
||||
BitSet simpleContains = new BitSet();
|
||||
BitSet complexContains = new BitSet();
|
||||
BitSet overlaps = new BitSet();
|
||||
for (Edge edge : edges) {
|
||||
if (isContainEdge(subgraph, edge)) {
|
||||
if (edge.isSimple()) {
|
||||
simpleContains.set(edge.getIndex());
|
||||
} else {
|
||||
complexContains.set(edge.getIndex());
|
||||
}
|
||||
} else if (isOverlapEdge(subgraph, edge)) {
|
||||
overlaps.set(edge.getIndex());
|
||||
}
|
||||
}
|
||||
if (containSimpleEdges.containsKey(subgraph)) {
|
||||
complexContains.or(containComplexEdges.get(subgraph));
|
||||
simpleContains.or(containSimpleEdges.get(subgraph));
|
||||
}
|
||||
if (overlapEdges.containsKey(subgraph)) {
|
||||
overlaps.or(overlapEdges.get(subgraph));
|
||||
}
|
||||
overlapEdges.put(subgraph, overlaps);
|
||||
containSimpleEdges.put(subgraph, simpleContains);
|
||||
containComplexEdges.put(subgraph, complexContains);
|
||||
}
|
||||
|
||||
public void unionEdges(BitSet bitSet1, BitSet bitSet2) {
|
||||
// When union two sub graphs, we only need to check overlap edges.
|
||||
// However, if all reference nodes are contained by the subgraph,
|
||||
// we should remove it.
|
||||
if (!containSimpleEdges.containsKey(bitSet1)) {
|
||||
initSubgraph(bitSet1);
|
||||
}
|
||||
if (!containSimpleEdges.containsKey(bitSet2)) {
|
||||
initSubgraph(bitSet2);
|
||||
}
|
||||
BitSet subgraph = Bitmap.newBitmapUnion(bitSet1, bitSet2);
|
||||
if (containSimpleEdges.containsKey(subgraph)) {
|
||||
return;
|
||||
}
|
||||
BitSet simpleContains = Bitmap.newBitmapUnion(containSimpleEdges.get(bitSet1),
|
||||
containSimpleEdges.get(bitSet2));
|
||||
BitSet complexContains = Bitmap.newBitmapUnion(containComplexEdges.get(bitSet1),
|
||||
containComplexEdges.get(bitSet2));
|
||||
BitSet overlaps = Bitmap.newBitmapUnion(overlapEdges.get(bitSet1),
|
||||
overlapEdges.get(bitSet2));
|
||||
for (int index : overlaps.stream().toArray()) {
|
||||
Edge edge = edges.get(index);
|
||||
if (isContainEdge(subgraph, edge)) {
|
||||
overlaps.set(index, false);
|
||||
if (edge.isSimple()) {
|
||||
simpleContains.set(index);
|
||||
} else {
|
||||
complexContains.set(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
simpleContains = removeInvalidEdges(subgraph, simpleContains);
|
||||
complexContains = removeInvalidEdges(subgraph, complexContains);
|
||||
containSimpleEdges.put(subgraph, simpleContains);
|
||||
containComplexEdges.put(subgraph, complexContains);
|
||||
overlapEdges.put(subgraph, overlaps);
|
||||
}
|
||||
|
||||
public List<Edge> connectCsgCmp(BitSet bitSet1, BitSet bitSet2) {
|
||||
Preconditions.checkArgument(
|
||||
containSimpleEdges.containsKey(bitSet1) && containSimpleEdges.containsKey(bitSet2));
|
||||
List<Edge> foundEdges = new ArrayList<>();
|
||||
BitSet edgeMap = Bitmap.newBitmapIntersect(containSimpleEdges.get(bitSet1),
|
||||
containSimpleEdges.get(bitSet2));
|
||||
Bitmap.or(edgeMap, Bitmap.newBitmapIntersect(containComplexEdges.get(bitSet1),
|
||||
containComplexEdges.get(bitSet2)));
|
||||
edgeMap.stream().forEach(index -> foundEdges.add(edges.get(index)));
|
||||
return foundEdges;
|
||||
}
|
||||
|
||||
public List<Edge> foundEdgesContain(BitSet subgraph) {
|
||||
Preconditions.checkArgument(containSimpleEdges.containsKey(subgraph));
|
||||
BitSet edgeMap = containSimpleEdges.get(subgraph);
|
||||
edgeMap.or(containComplexEdges.get(subgraph));
|
||||
List<Edge> foundEdges = new ArrayList<>();
|
||||
edgeMap.stream().forEach(index -> foundEdges.add(edges.get(index)));
|
||||
return foundEdges;
|
||||
}
|
||||
|
||||
public List<Edge> foundSimpleEdgesContain(BitSet subgraph) {
|
||||
List<Edge> foundEdges = new ArrayList<>();
|
||||
if (!containSimpleEdges.containsKey(subgraph)) {
|
||||
return foundEdges;
|
||||
}
|
||||
BitSet edgeMap = containSimpleEdges.get(subgraph);
|
||||
edgeMap.stream().forEach(index -> foundEdges.add(edges.get(index)));
|
||||
return foundEdges;
|
||||
}
|
||||
|
||||
public List<Edge> foundComplexEdgesContain(BitSet subgraph) {
|
||||
List<Edge> foundEdges = new ArrayList<>();
|
||||
if (!containComplexEdges.containsKey(subgraph)) {
|
||||
return foundEdges;
|
||||
}
|
||||
BitSet edgeMap = containComplexEdges.get(subgraph);
|
||||
edgeMap.stream().forEach(index -> foundEdges.add(edges.get(index)));
|
||||
return foundEdges;
|
||||
}
|
||||
|
||||
public int getEdgeSizeContain(BitSet subgraph) {
|
||||
Preconditions.checkArgument(containSimpleEdges.containsKey(subgraph));
|
||||
return containSimpleEdges.get(subgraph).cardinality() + containSimpleEdges.get(subgraph).cardinality();
|
||||
}
|
||||
|
||||
private boolean isContainEdge(BitSet subgraph, Edge edge) {
|
||||
int containLeft = Bitmap.isSubset(edge.getLeft(), subgraph) ? 0 : 1;
|
||||
int containRight = Bitmap.isSubset(edge.getRight(), subgraph) ? 0 : 1;
|
||||
return containLeft + containRight == 1;
|
||||
}
|
||||
|
||||
private boolean isOverlapEdge(BitSet subgraph, Edge edge) {
|
||||
int overlapLeft = Bitmap.isOverlap(edge.getLeft(), subgraph) ? 0 : 1;
|
||||
int overlapRight = Bitmap.isOverlap(edge.getRight(), subgraph) ? 0 : 1;
|
||||
return overlapLeft + overlapRight == 1;
|
||||
}
|
||||
|
||||
private BitSet removeInvalidEdges(BitSet subgraph, BitSet edgeMap) {
|
||||
for (int index : edgeMap.stream().toArray()) {
|
||||
Edge edge = edges.get(index);
|
||||
if (!isOverlapEdge(subgraph, edge)) {
|
||||
edgeMap.set(index, false);
|
||||
}
|
||||
}
|
||||
return edgeMap;
|
||||
}
|
||||
return neighborhoods;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,8 +26,8 @@ public class Bitmap {
|
||||
public static boolean isSubset(BitSet bitSet1, BitSet bitSet2) {
|
||||
BitSet bitSet = new BitSet();
|
||||
bitSet.or(bitSet1);
|
||||
bitSet.or(bitSet2);
|
||||
return bitSet.equals(bitSet2);
|
||||
bitSet.andNot(bitSet2);
|
||||
return bitSet.cardinality() == 0;
|
||||
}
|
||||
|
||||
public static BitSet newBitmap(int... values) {
|
||||
@ -64,6 +64,14 @@ public class Bitmap {
|
||||
return u;
|
||||
}
|
||||
|
||||
//return bitset1 ∩ bitset2
|
||||
public static BitSet newBitmapIntersect(BitSet bitSet1, BitSet bitSet2) {
|
||||
BitSet intersect = newBitmap();
|
||||
intersect.or(bitSet1);
|
||||
intersect.and(bitSet2);
|
||||
return intersect;
|
||||
}
|
||||
|
||||
public static BitSet newBitmapBetween(int start, int end) {
|
||||
BitSet bitSet = new BitSet();
|
||||
bitSet.set(start, end);
|
||||
@ -121,5 +129,6 @@ public class Bitmap {
|
||||
public static SubsetIterator getSubsetIterator(BitSet bitSet) {
|
||||
return new SubsetIterator(bitSet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -21,12 +21,13 @@ import org.apache.doris.nereids.jobs.joinorder.hypergraph.Edge;
|
||||
import org.apache.doris.nereids.memo.Group;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A interface of receiver
|
||||
*/
|
||||
public interface AbstractReceiver {
|
||||
public boolean emitCsgCmp(BitSet csg, BitSet cmp, Edge edge);
|
||||
public boolean emitCsgCmp(BitSet csg, BitSet cmp, List<Edge> edges);
|
||||
|
||||
public void addGroup(BitSet bitSet, Group group);
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The Receiver is used for cached the plan that has been emitted and build the new plan
|
||||
@ -47,10 +48,10 @@ public class Counter implements AbstractReceiver {
|
||||
*
|
||||
* @param left the bitmap of left child tree
|
||||
* @param right the bitmap of the right child tree
|
||||
* @param edge the join operator
|
||||
* @param edges the join operator
|
||||
* @return the left and the right can be connected by the edge
|
||||
*/
|
||||
public boolean emitCsgCmp(BitSet left, BitSet right, Edge edge) {
|
||||
public boolean emitCsgCmp(BitSet left, BitSet right, List<Edge> edges) {
|
||||
Preconditions.checkArgument(counter.containsKey(left));
|
||||
Preconditions.checkArgument(counter.containsKey(right));
|
||||
emitCount += 1;
|
||||
|
||||
@ -23,14 +23,17 @@ import org.apache.doris.nereids.memo.Group;
|
||||
import org.apache.doris.nereids.memo.GroupExpression;
|
||||
import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
import org.apache.doris.nereids.stats.StatsCalculator;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The Receiver is used for cached the plan that has been emitted and build the new plan
|
||||
@ -54,11 +57,11 @@ public class PlanReceiver implements AbstractReceiver {
|
||||
*
|
||||
* @param left the bitmap of left child tree
|
||||
* @param right the bitmap of the right child tree
|
||||
* @param edge the join operator
|
||||
* @param edges the join conditions that can be added in this operator
|
||||
* @return the left and the right can be connected by the edge
|
||||
*/
|
||||
@Override
|
||||
public boolean emitCsgCmp(BitSet left, BitSet right, Edge edge) {
|
||||
public boolean emitCsgCmp(BitSet left, BitSet right, List<Edge> edges) {
|
||||
Preconditions.checkArgument(planTable.containsKey(left));
|
||||
Preconditions.checkArgument(planTable.containsKey(right));
|
||||
emitCount += 1;
|
||||
@ -66,8 +69,8 @@ public class PlanReceiver implements AbstractReceiver {
|
||||
return false;
|
||||
}
|
||||
BitSet fullKey = Bitmap.newBitmapUnion(left, right);
|
||||
Group group1 = constructGroup(left, right, edge);
|
||||
Group group2 = constructGroup(right, left, edge);
|
||||
Group group1 = constructGroup(left, right, edges);
|
||||
Group group2 = constructGroup(right, left, edges);
|
||||
Group winnerGroup;
|
||||
if (group1.getLogicalExpression().getCostByProperties(PhysicalProperties.ANY) < group2.getLogicalExpression()
|
||||
.getCostByProperties(PhysicalProperties.ANY)) {
|
||||
@ -113,7 +116,7 @@ public class PlanReceiver implements AbstractReceiver {
|
||||
return plan.getGroupExpression().get().getCostByProperties(PhysicalProperties.ANY);
|
||||
}
|
||||
|
||||
private Group constructGroup(BitSet left, BitSet right, Edge edge) {
|
||||
private Group constructGroup(BitSet left, BitSet right, List<Edge> edges) {
|
||||
Preconditions.checkArgument(planTable.containsKey(left));
|
||||
Preconditions.checkArgument(planTable.containsKey(right));
|
||||
Group leftGroup = planTable.get(left);
|
||||
@ -122,7 +125,11 @@ public class PlanReceiver implements AbstractReceiver {
|
||||
Plan rightPlan = rightGroup.getLogicalExpression().getPlan();
|
||||
|
||||
double cost = getSimpleCost(leftPlan) + getSimpleCost(rightPlan);
|
||||
LogicalJoin newJoin = new LogicalJoin(edge.getJoin().getJoinType(), edge.getJoin().getExpressions(),
|
||||
List<Expression> conditions = new ArrayList<>();
|
||||
for (Edge edge : edges) {
|
||||
conditions.addAll(edge.getJoin().getExpressions());
|
||||
}
|
||||
LogicalJoin newJoin = new LogicalJoin(edges.get(0).getJoin().getJoinType(), conditions,
|
||||
leftGroup.getLogicalExpression().getPlan(),
|
||||
rightGroup.getLogicalExpression().getPlan());
|
||||
|
||||
|
||||
@ -164,10 +164,36 @@ public class GraphSimplifierTest {
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test
|
||||
void testComplexQuery() {
|
||||
HyperGraph hyperGraph = new HyperGraphBuilder()
|
||||
.init(6, 2, 1, 3, 5, 4)
|
||||
.addEdge(JoinType.INNER_JOIN, 3, 4)
|
||||
.addEdge(JoinType.INNER_JOIN, 3, 5)
|
||||
.addEdge(JoinType.INNER_JOIN, 2, 3)
|
||||
.addEdge(JoinType.INNER_JOIN, 2, 5)
|
||||
.addEdge(JoinType.INNER_JOIN, 2, 4)
|
||||
.addEdge(JoinType.INNER_JOIN, 1, 5)
|
||||
.addEdge(JoinType.INNER_JOIN, 1, 4)
|
||||
.addEdge(JoinType.INNER_JOIN, 0, 2)
|
||||
.build();
|
||||
GraphSimplifier graphSimplifier = new GraphSimplifier(hyperGraph);
|
||||
graphSimplifier.initFirstStep();
|
||||
while (graphSimplifier.applySimplificationStep()) {
|
||||
}
|
||||
Counter counter = new Counter();
|
||||
SubgraphEnumerator subgraphEnumerator = new SubgraphEnumerator(counter, hyperGraph);
|
||||
subgraphEnumerator.enumerate();
|
||||
for (int count : counter.getAllCount().values()) {
|
||||
Assertions.assertTrue(count < 1000);
|
||||
}
|
||||
Assertions.assertTrue(graphSimplifier.isTotalOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRandomQuery() {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
HyperGraph hyperGraph = new HyperGraphBuilder().randomBuildWith(20, 40);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
HyperGraph hyperGraph = new HyperGraphBuilder().randomBuildWith(6, 6);
|
||||
GraphSimplifier graphSimplifier = new GraphSimplifier(hyperGraph);
|
||||
graphSimplifier.initFirstStep();
|
||||
while (graphSimplifier.applySimplificationStep()) {
|
||||
@ -175,9 +201,6 @@ public class GraphSimplifierTest {
|
||||
Counter counter = new Counter();
|
||||
SubgraphEnumerator subgraphEnumerator = new SubgraphEnumerator(counter, hyperGraph);
|
||||
subgraphEnumerator.enumerate();
|
||||
for (int count : counter.getAllCount().values()) {
|
||||
Assertions.assertTrue(count < 1000);
|
||||
}
|
||||
Assertions.assertTrue(graphSimplifier.isTotalOrder());
|
||||
}
|
||||
}
|
||||
@ -195,6 +218,5 @@ public class GraphSimplifierTest {
|
||||
subgraphEnumerator.enumerate();
|
||||
Assertions.assertTrue(counter.getLimit() >= 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,9 +35,9 @@ public class SubgraphEnumeratorTest {
|
||||
void testStarQuery() {
|
||||
// t2
|
||||
// |
|
||||
//t3-- t1 -- t4
|
||||
//t3-- t0 -- t4
|
||||
// |
|
||||
// t5
|
||||
// t1
|
||||
HyperGraph hyperGraph = new HyperGraphBuilder()
|
||||
.init(10, 20, 30, 40, 50)
|
||||
.addEdge(JoinType.INNER_JOIN, 0, 1)
|
||||
@ -96,8 +96,8 @@ public class SubgraphEnumeratorTest {
|
||||
|
||||
@Test
|
||||
void testTime() {
|
||||
int tableNum = 10;
|
||||
int edgeNum = 40;
|
||||
int tableNum = 20;
|
||||
int edgeNum = 21;
|
||||
HyperGraph hyperGraph = new HyperGraphBuilder().randomBuildWith(tableNum, edgeNum);
|
||||
|
||||
Counter counter = new Counter();
|
||||
|
||||
Reference in New Issue
Block a user