[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:
924060929
2024-08-09 16:28:24 +08:00
committed by GitHub
parent 8a682d43ec
commit 3f6c71e47b
9 changed files with 58 additions and 13 deletions

View File

@ -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<>());
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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();
}
/**

View File

@ -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;

View File

@ -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);

View File

@ -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());

View File

@ -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());
}
}
}