Search in sources :

Example 11 with RelOptCost

use of org.apache.calcite.plan.RelOptCost in project calcite by apache.

the class LoptOptimizeJoinRule method addFactorToTree.

/**
 * Adds a new factor into the current join tree. The factor is either pushed
 * down into one of the subtrees of the join recursively, or it is added to
 * the top of the current tree, whichever yields a better ordering.
 *
 * @param multiJoin join factors being optimized
 * @param semiJoinOpt optimal semijoins for each factor
 * @param joinTree current join tree
 * @param factorToAdd new factor to be added
 * @param factorsNeeded factors that must precede the factor to be added
 * @param filtersToAdd filters remaining to be added; filters added to the
 * new join tree are removed from the list
 * @param selfJoin true if the join being created is a self-join that's
 * removable
 *
 * @return optimal join tree with the new factor added if it is possible to
 * add the factor; otherwise, null is returned
 */
private LoptJoinTree addFactorToTree(RelMetadataQuery mq, RelBuilder relBuilder, LoptMultiJoin multiJoin, LoptSemiJoinOptimizer semiJoinOpt, LoptJoinTree joinTree, int factorToAdd, BitSet factorsNeeded, List<RexNode> filtersToAdd, boolean selfJoin) {
    // join that can be removed, then create a replacement join
    if (multiJoin.isRemovableOuterJoinFactor(factorToAdd)) {
        return createReplacementJoin(relBuilder, multiJoin, semiJoinOpt, joinTree, -1, factorToAdd, ImmutableIntList.of(), null, filtersToAdd);
    }
    // table is in the current join tree
    if (multiJoin.getJoinRemovalFactor(factorToAdd) != null) {
        return createReplacementSemiJoin(relBuilder, multiJoin, semiJoinOpt, joinTree, factorToAdd, filtersToAdd);
    }
    // the single factor
    if (joinTree == null) {
        return new LoptJoinTree(semiJoinOpt.getChosenSemiJoin(factorToAdd), factorToAdd);
    }
    // Create a temporary copy of the filter list as we may need the
    // original list to pass into addToTop().  However, if no tree was
    // created by addToTop() because the factor being added is part of
    // a self-join, then pass the original filter list so the added
    // filters will still be removed from the list.
    final List<RexNode> tmpFilters = new ArrayList<>(filtersToAdd);
    LoptJoinTree topTree = addToTop(mq, relBuilder, multiJoin, semiJoinOpt, joinTree, factorToAdd, filtersToAdd, selfJoin);
    LoptJoinTree pushDownTree = pushDownFactor(mq, relBuilder, multiJoin, semiJoinOpt, joinTree, factorToAdd, factorsNeeded, (topTree == null) ? filtersToAdd : tmpFilters, selfJoin);
    // pick the lower cost option, and replace the join ordering with
    // the ordering associated with the best option
    LoptJoinTree bestTree;
    RelOptCost costPushDown = null;
    RelOptCost costTop = null;
    if (pushDownTree != null) {
        costPushDown = mq.getCumulativeCost(pushDownTree.getJoinTree());
    }
    if (topTree != null) {
        costTop = mq.getCumulativeCost(topTree.getJoinTree());
    }
    if (pushDownTree == null) {
        bestTree = topTree;
    } else if (topTree == null) {
        bestTree = pushDownTree;
    } else {
        if (costPushDown.isEqWithEpsilon(costTop)) {
            // around the wider rows further up in the tree
            if (rowWidthCost(pushDownTree.getJoinTree()) < rowWidthCost(topTree.getJoinTree())) {
                bestTree = pushDownTree;
            } else {
                bestTree = topTree;
            }
        } else if (costPushDown.isLt(costTop)) {
            bestTree = pushDownTree;
        } else {
            bestTree = topTree;
        }
    }
    return bestTree;
}
Also used : RelOptCost(org.apache.calcite.plan.RelOptCost) ArrayList(java.util.ArrayList) RexNode(org.apache.calcite.rex.RexNode)

Example 12 with RelOptCost

use of org.apache.calcite.plan.RelOptCost in project calcite by apache.

the class RelSubset method propagateCostImprovements0.

void propagateCostImprovements0(VolcanoPlanner planner, RelMetadataQuery mq, RelNode rel, Set<RelSubset> activeSet) {
    ++timestamp;
    if (!activeSet.add(this)) {
        // This subset is already in the chain being propagated to. This
        // means that the graph is cyclic, and therefore the cost of this
        // relational expression - not this subset - must be infinite.
        LOGGER.trace("cyclic: {}", this);
        return;
    }
    try {
        final RelOptCost cost = planner.getCost(rel, mq);
        if (cost.isLt(bestCost)) {
            LOGGER.trace("Subset cost improved: subset [{}] cost was {} now {}", this, bestCost, cost);
            bestCost = cost;
            best = rel;
            // Lower cost means lower importance. Other nodes will change
            // too, but we'll get to them later.
            planner.ruleQueue.recompute(this);
            for (RelNode parent : getParents()) {
                final RelSubset parentSubset = planner.getSubset(parent);
                parentSubset.propagateCostImprovements(planner, mq, parent, activeSet);
            }
            planner.checkForSatisfiedConverters(set, rel);
        }
    } finally {
        activeSet.remove(this);
    }
}
Also used : AbstractRelNode(org.apache.calcite.rel.AbstractRelNode) RelNode(org.apache.calcite.rel.RelNode) RelOptCost(org.apache.calcite.plan.RelOptCost)

Example 13 with RelOptCost

use of org.apache.calcite.plan.RelOptCost in project calcite by apache.

the class VolcanoPlanner method findBestExp.

/**
 * Finds the most efficient expression to implement the query given via
 * {@link org.apache.calcite.plan.RelOptPlanner#setRoot(org.apache.calcite.rel.RelNode)}.
 *
 * <p>The algorithm executes repeatedly in a series of phases. In each phase
 * the exact rules that may be fired varies. The mapping of phases to rule
 * sets is maintained in the {@link #ruleQueue}.
 *
 * <p>In each phase, the planner sets the initial importance of the existing
 * RelSubSets ({@link #setInitialImportance()}). The planner then iterates
 * over the rule matches presented by the rule queue until:
 *
 * <ol>
 * <li>The rule queue becomes empty.</li>
 * <li>For ambitious planners: No improvements to the plan have been made
 * recently (specifically within a number of iterations that is 10% of the
 * number of iterations necessary to first reach an implementable plan or 25
 * iterations whichever is larger).</li>
 * <li>For non-ambitious planners: When an implementable plan is found.</li>
 * </ol>
 *
 * <p>Furthermore, after every 10 iterations without an implementable plan,
 * RelSubSets that contain only logical RelNodes are given an importance
 * boost via {@link #injectImportanceBoost()}. Once an implementable plan is
 * found, the artificially raised importance values are cleared (see
 * {@link #clearImportanceBoost()}).
 *
 * @return the most efficient RelNode tree found for implementing the given
 * query
 */
public RelNode findBestExp() {
    ensureRootConverters();
    registerMaterializations();
    int cumulativeTicks = 0;
    for (VolcanoPlannerPhase phase : VolcanoPlannerPhase.values()) {
        setInitialImportance();
        RelOptCost targetCost = costFactory.makeHugeCost();
        int tick = 0;
        int firstFiniteTick = -1;
        int splitCount = 0;
        int giveUpTick = Integer.MAX_VALUE;
        while (true) {
            ++tick;
            ++cumulativeTicks;
            if (root.bestCost.isLe(targetCost)) {
                if (firstFiniteTick < 0) {
                    firstFiniteTick = cumulativeTicks;
                    clearImportanceBoost();
                }
                if (ambitious) {
                    // Choose a slightly more ambitious target cost, and
                    // try again. If it took us 1000 iterations to find our
                    // first finite plan, give ourselves another 100
                    // iterations to reduce the cost by 10%.
                    targetCost = root.bestCost.multiplyBy(0.9);
                    ++splitCount;
                    if (impatient) {
                        if (firstFiniteTick < 10) {
                            // It's possible pre-processing can create
                            // an implementable plan -- give us some time
                            // to actually optimize it.
                            giveUpTick = cumulativeTicks + 25;
                        } else {
                            giveUpTick = cumulativeTicks + Math.max(firstFiniteTick / 10, 25);
                        }
                    }
                } else {
                    break;
                }
            } else if (cumulativeTicks > giveUpTick) {
                // We haven't made progress recently. Take the current best.
                break;
            } else if (root.bestCost.isInfinite() && ((tick % 10) == 0)) {
                injectImportanceBoost();
            }
            LOGGER.debug("PLANNER = {}; TICK = {}/{}; PHASE = {}; COST = {}", this, cumulativeTicks, tick, phase.toString(), root.bestCost);
            VolcanoRuleMatch match = ruleQueue.popMatch(phase);
            if (match == null) {
                break;
            }
            assert match.getRule().matches(match);
            match.onMatch();
            // The root may have been merged with another
            // subset. Find the new root subset.
            root = canonize(root);
        }
        ruleQueue.phaseCompleted(phase);
    }
    if (LOGGER.isTraceEnabled()) {
        StringWriter sw = new StringWriter();
        final PrintWriter pw = new PrintWriter(sw);
        dump(pw);
        pw.flush();
        LOGGER.trace(sw.toString());
    }
    RelNode cheapest = root.buildCheapestPlan(this);
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Cheapest plan:\n{}", RelOptUtil.toString(cheapest, SqlExplainLevel.ALL_ATTRIBUTES));
        LOGGER.debug("Provenance:\n{}", provenance(cheapest));
    }
    return cheapest;
}
Also used : StringWriter(java.io.StringWriter) RelNode(org.apache.calcite.rel.RelNode) RelOptCost(org.apache.calcite.plan.RelOptCost) PrintWriter(java.io.PrintWriter)

Example 14 with RelOptCost

use of org.apache.calcite.plan.RelOptCost in project calcite by apache.

the class HepPlanner method applyTransformationResults.

private HepRelVertex applyTransformationResults(HepRelVertex vertex, HepRuleCall call, RelTrait parentTrait) {
    assert !call.getResults().isEmpty();
    RelNode bestRel = null;
    if (call.getResults().size() == 1) {
        // No costing required; skip it to minimize the chance of hitting
        // rels without cost information.
        bestRel = call.getResults().get(0);
    } else {
        RelOptCost bestCost = null;
        final RelMetadataQuery mq = call.getMetadataQuery();
        for (RelNode rel : call.getResults()) {
            RelOptCost thisCost = getCost(rel, mq);
            if (LOGGER.isTraceEnabled()) {
                // Keep in the isTraceEnabled for the getRowCount method call
                LOGGER.trace("considering {} with cumulative cost={} and rowcount={}", rel, thisCost, mq.getRowCount(rel));
            }
            if ((bestRel == null) || thisCost.isLt(bestCost)) {
                bestRel = rel;
                bestCost = thisCost;
            }
        }
    }
    ++nTransformations;
    notifyTransformation(call, bestRel, true);
    // Before we add the result, make a copy of the list of vertex's
    // parents.  We'll need this later during contraction so that
    // we only update the existing parents, not the new parents
    // (otherwise loops can result).  Also take care of filtering
    // out parents by traits in case we're dealing with a converter rule.
    final List<HepRelVertex> allParents = Graphs.predecessorListOf(graph, vertex);
    final List<HepRelVertex> parents = new ArrayList<>();
    for (HepRelVertex parent : allParents) {
        if (parentTrait != null) {
            RelNode parentRel = parent.getCurrentRel();
            if (parentRel instanceof Converter) {
                // the multi-parent DAG case.
                continue;
            }
            if (!parentRel.getTraitSet().contains(parentTrait)) {
                // This parent does not want the converted result.
                continue;
            }
        }
        parents.add(parent);
    }
    HepRelVertex newVertex = addRelToGraph(bestRel);
    // There's a chance that newVertex is the same as one
    // of the parents due to common subexpression recognition
    // (e.g. the LogicalProject added by JoinCommuteRule).  In that
    // case, treat the transformation as a nop to avoid
    // creating a loop.
    int iParentMatch = parents.indexOf(newVertex);
    if (iParentMatch != -1) {
        newVertex = parents.get(iParentMatch);
    } else {
        contractVertices(newVertex, vertex, parents);
    }
    if (getListener() != null) {
        // Assume listener doesn't want to see garbage.
        collectGarbage();
    }
    notifyTransformation(call, bestRel, false);
    dumpGraph();
    return newVertex;
}
Also used : RelMetadataQuery(org.apache.calcite.rel.metadata.RelMetadataQuery) RelNode(org.apache.calcite.rel.RelNode) RelOptCost(org.apache.calcite.plan.RelOptCost) ArrayList(java.util.ArrayList) Converter(org.apache.calcite.rel.convert.Converter)

Aggregations

RelOptCost (org.apache.calcite.plan.RelOptCost)14 RelNode (org.apache.calcite.rel.RelNode)9 RelMetadataQuery (org.apache.calcite.rel.metadata.RelMetadataQuery)4 ArrayList (java.util.ArrayList)3 AbstractRelNode (org.apache.calcite.rel.AbstractRelNode)2 RexNode (org.apache.calcite.rex.RexNode)2 ImmutableBitSet (org.apache.calcite.util.ImmutableBitSet)2 PrintWriter (java.io.PrintWriter)1 StringWriter (java.io.StringWriter)1 HashMap (java.util.HashMap)1 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)1 RelSubset (org.apache.calcite.plan.volcano.RelSubset)1 Converter (org.apache.calcite.rel.convert.Converter)1 Aggregate (org.apache.calcite.rel.core.Aggregate)1 AggregateCall (org.apache.calcite.rel.core.AggregateCall)1 Join (org.apache.calcite.rel.core.Join)1 JaninoRelMetadataProvider (org.apache.calcite.rel.metadata.JaninoRelMetadataProvider)1 RelDataType (org.apache.calcite.rel.type.RelDataType)1 RexBuilder (org.apache.calcite.rex.RexBuilder)1 SqlAggFunction (org.apache.calcite.sql.SqlAggFunction)1