This is an automated email from the ASF dual-hosted git repository. huajianlan pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.1 by this push: new 3f6c71e47b0 [enhancement](Nereids) fast compute hash code of deep expression tree to reduce conflict (#38981) (#39133) 3f6c71e47b0 is described below commit 3f6c71e47b0507c8e9a40f5ed0a75d353f760c06 Author: 924060929 <924060...@qq.com> AuthorDate: Fri Aug 9 16:28:24 2024 +0800 [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 --- .../post/CommonSubExpressionCollector.java | 14 +++++++++---- .../rules/exploration/mv/PredicatesSplitter.java | 8 ++++---- .../nereids/rules/rewrite/EliminateNotNull.java | 2 +- .../nereids/trees/expressions/Expression.java | 23 ++++++++++++++++++++-- .../nereids/trees/expressions/Placeholder.java | 10 ++++++++++ .../nereids/trees/expressions/SlotReference.java | 5 +++++ .../nereids/trees/expressions/literal/Literal.java | 5 +++++ .../org/apache/doris/qe/PointQueryExecutor.java | 2 +- .../rules/expression/PredicatesSplitterTest.java | 2 +- 9 files changed, 58 insertions(+), 13 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionCollector.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionCollector.java index 5abc5f6f60f..877e411a539 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionCollector.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/CommonSubExpressionCollector.java @@ -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<>()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.java index f1c0ae8f96a..f7182eeab73 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.java @@ -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) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateNotNull.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateNotNull.java index 22393cb55f8..b3fe2e1aa91 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateNotNull.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/EliminateNotNull.java @@ -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) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java index d7f400955c0..e355cd204cd 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java @@ -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(); } /** diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Placeholder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Placeholder.java index 0432beadd2c..c79c2d9db6d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Placeholder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Placeholder.java @@ -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; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java index 145d8dd29f7..679c8ab73bd 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java @@ -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); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java index fe53cbf0e2a..e8e37aaf697 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java @@ -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()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExecutor.java index b0af8431471..68b1eb6ab93 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExecutor.java @@ -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()); } } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java index 56c0a6b8341..0374244ce56 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java @@ -61,7 +61,7 @@ public class PredicatesSplitterTest extends ExpressionRewriteTestHelper { String expectedRangeExpr, String expectedResidualExpr) { - Map<String, Slot> mem = Maps.newHashMap(); + Map<String, Slot> mem = Maps.newLinkedHashMap(); Expression targetExpr = replaceUnboundSlot(PARSER.parseExpression(expression), mem); SplitPredicate splitPredicate = Predicates.splitPredicates(targetExpr); --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org