[enhancement](Nereids) fast compute hash code of deep expression tree to reduce conflict (#38981) (#39133)
The Expression.hashCode default is getClass().hashCode(), just contains one level information, so the lots of expressions which is same type will return the same hash code and conflict, then compare deeply in the HashMap cause inefficient and hold table lock for long time. This pr support fast compute hash code by the bottom literal and slot, reduce the compare expression time because of the conflict of hash code In my test case, the sql planner time can reduce from 20 minutes(not finished) to 35 seconds
This commit is contained in:
@ -37,8 +37,15 @@ public class CommonSubExpressionCollector extends ExpressionVisitor<Integer, Voi
|
||||
if (expr.children().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
return collectCommonExpressionByDepth(expr.children().stream().map(child ->
|
||||
child.accept(this, context)).reduce(Math::max).map(m -> m + 1).orElse(1), expr);
|
||||
return collectCommonExpressionByDepth(
|
||||
expr.children()
|
||||
.stream()
|
||||
.map(child -> child.accept(this, context))
|
||||
.reduce(Math::max)
|
||||
.map(m -> m + 1)
|
||||
.orElse(1),
|
||||
expr
|
||||
);
|
||||
}
|
||||
|
||||
private int collectCommonExpressionByDepth(int depth, Expression expr) {
|
||||
@ -53,7 +60,6 @@ public class CommonSubExpressionCollector extends ExpressionVisitor<Integer, Voi
|
||||
|
||||
public static Set<Expression> getExpressionsFromDepthMap(
|
||||
int depth, Map<Integer, Set<Expression>> depthMap) {
|
||||
depthMap.putIfAbsent(depth, new LinkedHashSet<>());
|
||||
return depthMap.get(depth);
|
||||
return depthMap.computeIfAbsent(depth, d -> new LinkedHashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ import org.apache.doris.nereids.trees.expressions.literal.Literal;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor;
|
||||
import org.apache.doris.nereids.util.ExpressionUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@ -39,9 +39,9 @@ import java.util.Set;
|
||||
*/
|
||||
public class PredicatesSplitter {
|
||||
|
||||
private final Set<Expression> equalPredicates = new HashSet<>();
|
||||
private final Set<Expression> rangePredicates = new HashSet<>();
|
||||
private final Set<Expression> residualPredicates = new HashSet<>();
|
||||
private final Set<Expression> equalPredicates = new LinkedHashSet<>();
|
||||
private final Set<Expression> rangePredicates = new LinkedHashSet<>();
|
||||
private final Set<Expression> residualPredicates = new LinkedHashSet<>();
|
||||
private final List<Expression> conjunctExpressions;
|
||||
|
||||
public PredicatesSplitter(Expression target) {
|
||||
|
||||
@ -81,7 +81,7 @@ public class EliminateNotNull implements RewriteRuleFactory {
|
||||
// predicatesNotContainIsNotNull infer nonNullable slots: `id`
|
||||
// slotsFromIsNotNull: `id`, `name`
|
||||
// remove `name` (it's generated), remove `id` (because `id > 0` already contains it)
|
||||
Set<Expression> predicatesNotContainIsNotNull = Sets.newHashSet();
|
||||
Set<Expression> predicatesNotContainIsNotNull = Sets.newLinkedHashSet();
|
||||
List<Slot> slotsFromIsNotNull = Lists.newArrayList();
|
||||
|
||||
for (Expression expr : exprs) {
|
||||
|
||||
@ -70,6 +70,7 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
|
||||
private final boolean compareWidthAndDepth;
|
||||
private final Supplier<Set<Slot>> inputSlots = Suppliers.memoize(
|
||||
() -> collect(e -> e instanceof Slot && !(e instanceof ArrayItemSlot)));
|
||||
private final int fastChildrenHashCode;
|
||||
|
||||
protected Expression(Expression... children) {
|
||||
super(children);
|
||||
@ -80,12 +81,14 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
|
||||
this.depth = 1;
|
||||
this.width = 1;
|
||||
this.compareWidthAndDepth = supportCompareWidthAndDepth();
|
||||
this.fastChildrenHashCode = 0;
|
||||
break;
|
||||
case 1:
|
||||
Expression child = children[0];
|
||||
this.depth = child.depth + 1;
|
||||
this.width = child.width;
|
||||
this.compareWidthAndDepth = child.compareWidthAndDepth && supportCompareWidthAndDepth();
|
||||
this.fastChildrenHashCode = child.fastChildrenHashCode() + 1;
|
||||
break;
|
||||
case 2:
|
||||
Expression left = children[0];
|
||||
@ -94,21 +97,25 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
|
||||
this.width = left.width + right.width;
|
||||
this.compareWidthAndDepth =
|
||||
left.compareWidthAndDepth && right.compareWidthAndDepth && supportCompareWidthAndDepth();
|
||||
this.fastChildrenHashCode = left.fastChildrenHashCode() + right.fastChildrenHashCode() + 2;
|
||||
break;
|
||||
default:
|
||||
int maxChildDepth = 0;
|
||||
int sumChildWidth = 0;
|
||||
boolean compareWidthAndDepth = true;
|
||||
int fastChildrenHashCode = 0;
|
||||
for (Expression expression : children) {
|
||||
child = expression;
|
||||
maxChildDepth = Math.max(child.depth, maxChildDepth);
|
||||
sumChildWidth += child.width;
|
||||
hasUnbound |= child.hasUnbound;
|
||||
compareWidthAndDepth &= child.compareWidthAndDepth;
|
||||
fastChildrenHashCode = fastChildrenHashCode + expression.fastChildrenHashCode() + 1;
|
||||
}
|
||||
this.depth = maxChildDepth + 1;
|
||||
this.width = sumChildWidth;
|
||||
this.compareWidthAndDepth = compareWidthAndDepth;
|
||||
this.fastChildrenHashCode = fastChildrenHashCode;
|
||||
}
|
||||
|
||||
checkLimit();
|
||||
@ -129,12 +136,14 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
|
||||
this.depth = 1;
|
||||
this.width = 1;
|
||||
this.compareWidthAndDepth = supportCompareWidthAndDepth();
|
||||
this.fastChildrenHashCode = 0;
|
||||
break;
|
||||
case 1:
|
||||
Expression child = children.get(0);
|
||||
this.depth = child.depth + 1;
|
||||
this.width = child.width;
|
||||
this.compareWidthAndDepth = child.compareWidthAndDepth && supportCompareWidthAndDepth();
|
||||
this.fastChildrenHashCode = child.fastChildrenHashCode() + 1;
|
||||
break;
|
||||
case 2:
|
||||
Expression left = children.get(0);
|
||||
@ -143,21 +152,25 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
|
||||
this.width = left.width + right.width;
|
||||
this.compareWidthAndDepth =
|
||||
left.compareWidthAndDepth && right.compareWidthAndDepth && supportCompareWidthAndDepth();
|
||||
this.fastChildrenHashCode = left.fastChildrenHashCode() + right.fastChildrenHashCode() + 2;
|
||||
break;
|
||||
default:
|
||||
int maxChildDepth = 0;
|
||||
int sumChildWidth = 0;
|
||||
boolean compareWidthAndDepth = true;
|
||||
int fastChildrenhashCode = 0;
|
||||
for (Expression expression : children) {
|
||||
child = expression;
|
||||
maxChildDepth = Math.max(child.depth, maxChildDepth);
|
||||
sumChildWidth += child.width;
|
||||
hasUnbound |= child.hasUnbound;
|
||||
compareWidthAndDepth &= child.compareWidthAndDepth;
|
||||
fastChildrenhashCode = fastChildrenhashCode + expression.fastChildrenHashCode() + 1;
|
||||
}
|
||||
this.depth = maxChildDepth + 1;
|
||||
this.width = sumChildWidth;
|
||||
this.compareWidthAndDepth = compareWidthAndDepth && supportCompareWidthAndDepth();
|
||||
this.fastChildrenHashCode = fastChildrenhashCode;
|
||||
}
|
||||
|
||||
checkLimit();
|
||||
@ -211,6 +224,10 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
|
||||
return checkInputDataTypesInternal();
|
||||
}
|
||||
|
||||
public int fastChildrenHashCode() {
|
||||
return fastChildrenHashCode;
|
||||
}
|
||||
|
||||
protected TypeCheckResult checkInputDataTypesInternal() {
|
||||
return TypeCheckResult.SUCCESS;
|
||||
}
|
||||
@ -406,7 +423,9 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
|
||||
return false;
|
||||
}
|
||||
Expression that = (Expression) o;
|
||||
if ((compareWidthAndDepth && (this.width != that.width || this.depth != that.depth))
|
||||
if ((compareWidthAndDepth
|
||||
&& (this.width != that.width || this.depth != that.depth
|
||||
|| this.fastChildrenHashCode != that.fastChildrenHashCode))
|
||||
|| arity() != that.arity() || !extraEquals(that)) {
|
||||
return false;
|
||||
}
|
||||
@ -430,7 +449,7 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
return getClass().hashCode() + fastChildrenHashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -60,11 +60,21 @@ public class Placeholder extends Expression implements LeafExpression {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "$" + placeholderId.asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql() {
|
||||
return "?";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fastChildrenHashCode() {
|
||||
return placeholderId.asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType() throws UnboundException {
|
||||
return NullType.INSTANCE;
|
||||
|
||||
@ -237,6 +237,11 @@ public class SlotReference extends Slot {
|
||||
return exprId.asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fastChildrenHashCode() {
|
||||
return exprId.asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitSlotReference(this, context);
|
||||
|
||||
@ -365,6 +365,11 @@ public abstract class Literal extends Expression implements LeafExpression, Comp
|
||||
return Objects.hashCode(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fastChildrenHashCode() {
|
||||
return Objects.hashCode(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(getValue());
|
||||
|
||||
@ -136,7 +136,7 @@ public class PointQueryExecutor implements CoordInterface {
|
||||
} else if (binaryPredicate.getChild(1) instanceof LiteralExpr) {
|
||||
binaryPredicate.setChild(1, conjunctVals.get(i));
|
||||
} else {
|
||||
Preconditions.checkState(false, "Should conatains literal in " + binaryPredicate.toSqlImpl());
|
||||
Preconditions.checkState(false, "Should contains literal in " + binaryPredicate.toSqlImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user