public CardinalityEstimate plus(CardinalityEstimate that) { return new CardinalityEstimate( addSafe(this.getLowerEstimate(), that.getLowerEstimate()), addSafe(this.getUpperEstimate(), that.getUpperEstimate()), Math.min(this.getCorrectnessProbability(), that.getCorrectnessProbability()) ); }
/** * Extracts the geometric mean values of the given {@link CardinalityEstimate}s. * * @param estimates the input {@link CardinalityEstimate}s * @return an array containing the average estimates * @see CardinalityEstimate#getGeometricMeanEstimate() */ private static long[] extractMeanValues(CardinalityEstimate[] estimates) { long[] averages = new long[estimates.length]; for (int i = 0; i < estimates.length; i++) { CardinalityEstimate inputEstimate = estimates[i]; if (inputEstimate == null) inputEstimate = CardinalityEstimate.EMPTY_ESTIMATE; averages[i] = inputEstimate.getGeometricMeanEstimate(); } return averages; }
/** * Normalize the given estimates by dividing them by a number of executions. * * @param estimates that should be normalized * @param numExecutions the number execution * @return the normalized estimates (and {@code estimates} if {@code numExecution == 1} */ static CardinalityEstimate[] normalize(CardinalityEstimate[] estimates, int numExecutions) { if (numExecutions == 1 || estimates.length == 0) return estimates; CardinalityEstimate[] normalizedEstimates = new CardinalityEstimate[estimates.length]; for (int i = 0; i < estimates.length; i++) { final CardinalityEstimate estimate = estimates[i]; if (estimate != null) normalizedEstimates[i] = estimate.divideBy(numExecutions); } return normalizedEstimates; }
private long[] extractEstimateValues(CardinalityEstimate[] inputEstimates) { long[] lowerAndUpperEstimates = new long[this.numInputs * 2]; for (int i = 0; i < this.numInputs; i++) { final CardinalityEstimate inputEstimate = inputEstimates[i]; lowerAndUpperEstimates[i << 1] = inputEstimate.getLowerEstimate(); lowerAndUpperEstimates[(i << 1) + 1] = inputEstimate.getUpperEstimate(); } return lowerAndUpperEstimates; } }
@Override public CardinalityEstimate estimate(OptimizationContext optimizationContext, CardinalityEstimate... inputEstimates) { double probability = .5d; long upperEstimate = 1L; for (CardinalityEstimate inputEstimate : inputEstimates) { if (inputEstimate == null) { inputEstimate = CardinalityEstimate.EMPTY_ESTIMATE; } probability *= inputEstimate.getCorrectnessProbability(); upperEstimate *= 1 + 10 * inputEstimate.getUpperEstimate(); if (upperEstimate < 0L) { upperEstimate = Long.MAX_VALUE; } } return new CardinalityEstimate(1L, upperEstimate, probability); }
/** * Serializes this instance to the given {@link JSONObject}. * * @param json to which this instance should be serialized * @return {@code json} */ public JSONObject toJson(JSONObject json) { json.put("lowerBound", this.getLowerEstimate()); json.put("upperBound", this.getUpperEstimate()); json.put("confidence", this.getCorrectnessProbability()); return json; }
@Override public CardinalityEstimate estimate(OptimizationContext optimizationContext, CardinalityEstimate... inputEstimates) { return new CardinalityEstimate(this.outputSize, this.outputSize, 1d, this.isOverride); } }
/** * Merge two {@link CardinalityEstimate} vectors (point-wise). */ protected CardinalityEstimate choose(CardinalityEstimate estimate1, CardinalityEstimate estimate2) { // Make sure there are no nulls. if (estimate1 == null) { return estimate2; } else if (estimate2 == null) { return estimate1; } // Check for overrides. if (estimate1.isOverride()) return estimate1; if (estimate2.isOverride()) return estimate2; // Actually compare the two estimates. // TODO: Check if this is a good way. There are other palpable approaches (e.g., weighted average). if (estimate2.getCorrectnessProbability() > estimate1.getCorrectnessProbability()) { return estimate2; } return estimate1; }
/** * Injects the measured {@code cardinality}. */ private void injectMeasuredCardinality(long cardinality, OptimizationContext.OperatorContext targetOperatorContext, int outputIndex) { // Build the new CardinalityEstimate. final CardinalityEstimate newCardinality = new CardinalityEstimate(cardinality, cardinality, 1d, true); final CardinalityEstimate oldCardinality = targetOperatorContext.getOutputCardinality(outputIndex); if (!newCardinality.equals(oldCardinality)) { if (this.logger.isInfoEnabled()) { this.logger.info("Updating cardinality of {}'s output {} from {} to {}.", targetOperatorContext.getOperator(), outputIndex, oldCardinality, newCardinality ); } targetOperatorContext.setOutputCardinality(outputIndex, newCardinality); } }
@Override public CardinalityEstimate estimate(OptimizationContext optimizationContext, CardinalityEstimate... inputEstimates) { CardinalityEstimate forwardEstimate = null; for (int switchInputIndex : this.switchInputIndices) { final CardinalityEstimate inputEstimate = inputEstimates[switchInputIndex]; if (inputEstimate != null) { if (forwardEstimate != null) { LoggerFactory.getLogger(this.getClass()).error("Conflicting estimates {} and {}.", forwardEstimate, inputEstimate); } if (forwardEstimate == null || forwardEstimate.getCorrectnessProbability() > inputEstimate.getCorrectnessProbability()) { forwardEstimate = inputEstimate; } } } assert forwardEstimate != null; return forwardEstimate; }
cardinalityEstimate0.getUpperEstimate() <= cardinalityEstimate1.getUpperEstimate();
@Override public String toString() { return String.format( "(%,d..%,d, %s)", this.getLowerEstimate(), this.getUpperEstimate(), Formats.formatPercentage(this.getCorrectnessProbability()) ); } }
public double calculateSpread(CardinalityEstimate cardinalityEstimate) { return ((double) cardinalityEstimate.getUpperEstimate() + this.spreadSmoothing) / ((double) cardinalityEstimate.getLowerEstimate() + this.spreadSmoothing); }
/** * @deprecated These method does not do anything, anymore. */ @Deprecated public CardinalityEstimate getCardinalityEstimate() { return new CardinalityEstimate(42, 42, 0.42); } //
@Override public CardinalityEstimate estimate(OptimizationContext optimizationContext, CardinalityEstimate... inputEstimates) { // Simply use the estimate with the highest correctness probability. // TODO: Check if this is a good way. There are other palpable approaches (e.g., weighted average). return this.alternativeEstimators.stream() .map(alternativeEstimator -> alternativeEstimator.estimate(optimizationContext, inputEstimates)) .sorted((estimate1, estimate2) -> Double.compare(estimate2.getCorrectnessProbability(), estimate1.getCorrectnessProbability())) .findFirst() .orElseThrow(IllegalStateException::new); } }
/** * Divides the estimate values, not the probability. * * @param denominator by which this instance should be divided * @return the quotient */ public CardinalityEstimate divideBy(double denominator) { return new CardinalityEstimate( (long) Math.ceil(this.getLowerEstimate() / denominator), (long) Math.ceil(this.getUpperEstimate() / denominator), this.getCorrectnessProbability() ); }
private void write(OptimizationContext.OperatorContext operatorContext, OutputSlot<?> output, long outputCardinality) { JSONArray jsonInputCardinalities = new JSONArray(); final Operator operator = operatorContext.getOperator(); for (int inputIndex = 0; inputIndex < operator.getNumInputs(); inputIndex++) { final InputSlot<?> input = operator.getInput(inputIndex); final CardinalityEstimate inputEstimate = operatorContext.getInputCardinality(inputIndex); JSONObject jsonInputCardinality = new JSONObject(); jsonInputCardinality.put("name", input.getName()); jsonInputCardinality.put("index", input.getIndex()); jsonInputCardinality.put("isBroadcast", input.isBroadcast()); jsonInputCardinality.put("lowerBound", inputEstimate.getLowerEstimate()); jsonInputCardinality.put("upperBound", inputEstimate.getUpperEstimate()); jsonInputCardinality.put("confidence", inputEstimate.getCorrectnessProbability()); jsonInputCardinalities.put(jsonInputCardinality); } JSONObject jsonOperator = new JSONObject(); jsonOperator.put("class", operator.getClass().getCanonicalName()); // TODO: UDFs? How can we reference them? JSONObject jsonOutput = new JSONObject(); jsonOutput.put("name", output.getName()); jsonOutput.put("index", output.getIndex()); jsonOutput.put("cardinality", outputCardinality); JSONObject jsonMeasurement = new JSONObject(); jsonMeasurement.put("inputs", jsonInputCardinalities); jsonMeasurement.put("operator", jsonOperator); jsonMeasurement.put("output", jsonOutput); this.write(jsonMeasurement); }
for (int pos = 0; pos < cardinalityEstimates.length; pos++) { final CardinalityEstimate cardinalityEstimate = this.replaceNullCardinality(cardinalityEstimates[pos]); combinations[0][pos] = cardinalityEstimate.getLowerEstimate(); combinations[1][pos] = cardinalityEstimate.getUpperEstimate();
/** * Parses the given {@link JSONObject} to create a new instance. * * @param json that should be parsed * @return the new instance */ public static CardinalityEstimate fromJson(JSONObject json) { return new CardinalityEstimate(json.getLong("lowerBound"), json.getLong("upperBound"), json.getDouble("confidence")); }