org.apache.flink.optimizer.costs.Costs Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.flink.optimizer.costs;
/**
* Simple class to represent the costs of an operation. The costs are currently tracking, network, I/O and CPU costs.
*
* Costs are composed of two parts of cost contributors:
*
* - Quantifiable costs. Those costs are used when estimates are available and track a quantifiable
* measure, such as the number of bytes for network or I/O
* - Heuristic costs. Those costs are used when no estimates are available. They can be used to track that
* an operator used a special operation which is heuristically considered more expensive than another
* operation.
*
*
* The quantifiable costs may frequently be unknown, which is represented by a {@code -1} as a value for the unknown
* components of the cost. In that case, all operations' costs are unknown and hence it is not decidable which
* operation to favor during pruning. In that case, the heuristic costs should contain a value to make sure that
* operators with different strategies are comparable, even in the absence of estimates. The heuristic
* costs are hence the system's mechanism of realizing pruning heuristics that favor some operations over others.
*/
public class Costs implements Comparable, Cloneable {
public static final double UNKNOWN = -1;
private double networkCost; // network cost, in transferred bytes
private double diskCost; // bytes to be written and read, in bytes
private double cpuCost; // CPU costs
private double heuristicNetworkCost;
private double heuristicDiskCost;
private double heuristicCpuCost;
// --------------------------------------------------------------------------------------------
/**
* Default constructor. Initializes all costs to 0;
*/
public Costs() {
}
/**
* Creates a new costs object using the given values for the network and storage cost.
*
* @param networkCost The network cost, in bytes to be transferred.
* @param diskCost The cost for disk, in bytes to be written and read.
*/
public Costs(double networkCost, double diskCost) {
setNetworkCost(networkCost);
setDiskCost(diskCost);
}
/**
* Creates a new costs object using the given values for the network and storage cost.
*
* @param networkCost The network cost, in bytes to be transferred.
* @param diskCost The cost for disk, in bytes to be written and read.
* @param cpuCost The cost for CPU operations.
*/
public Costs(double networkCost, double diskCost, double cpuCost) {
setNetworkCost(networkCost);
setDiskCost(diskCost);
setCpuCost(cpuCost);
}
// --------------------------------------------------------------------------------------------
/**
* Gets the network cost.
*
* @return The network cost, in bytes to be transferred.
*/
public double getNetworkCost() {
return networkCost;
}
/**
* Sets the network cost for this Costs object.
*
* @param bytes
* The network cost to set, in bytes to be transferred.
*/
public void setNetworkCost(double bytes) {
if (bytes == UNKNOWN || bytes >= 0) {
this.networkCost = bytes;
} else {
throw new IllegalArgumentException();
}
}
/**
* Adds the costs for network to the current network costs
* for this Costs object.
*
* @param bytes The network cost to add, in bytes to be transferred.
*/
public void addNetworkCost(double bytes) {
this.networkCost = (this.networkCost < 0 || bytes < 0) ? UNKNOWN : this.networkCost + bytes;
}
/**
* Gets the costs for disk.
*
* @return The disk cost, in bytes to be written and read.
*/
public double getDiskCost() {
return diskCost;
}
/**
* Sets the costs for disk for this Costs object.
*
* @param bytes The disk cost to set, in bytes to be written and read.
*/
public void setDiskCost(double bytes) {
if (bytes == UNKNOWN || bytes >= 0) {
this.diskCost = bytes;
} else {
throw new IllegalArgumentException();
}
}
/**
* Adds the costs for disk to the current disk costs
* for this Costs object.
*
* @param bytes The disk cost to add, in bytes to be written and read.
*/
public void addDiskCost(double bytes) {
this.diskCost =
(this.diskCost < 0 || bytes < 0) ? UNKNOWN : this.diskCost + bytes;
}
/**
* Gets the cost for the CPU.
*
* @return The CPU Cost.
*/
public double getCpuCost() {
return this.cpuCost;
}
/**
* Sets the cost for the CPU.
*
* @param cost The CPU Cost.
*/
public void setCpuCost(double cost) {
if (cost == UNKNOWN || cost >= 0) {
this.cpuCost = cost;
} else {
throw new IllegalArgumentException();
}
}
/**
* Adds the given CPU cost to the current CPU cost for this Costs object.
*
* @param cost The CPU cost to add.
*/
public void addCpuCost(double cost) {
this.cpuCost =
(this.cpuCost < 0 || cost < 0) ? UNKNOWN : this.cpuCost + cost;
}
// --------------------------------------------------------------------------------------------
/**
* Gets the heuristic network cost.
*
* @return The heuristic network cost, in bytes to be transferred.
*/
public double getHeuristicNetworkCost() {
return this.heuristicNetworkCost;
}
/**
* Sets the heuristic network cost for this Costs object.
*
* @param cost The heuristic network cost to set, in bytes to be transferred.
*/
public void setHeuristicNetworkCost(double cost) {
if (cost <= 0) {
throw new IllegalArgumentException("Heuristic costs must be positive.");
}
this.heuristicNetworkCost = cost;
}
/**
* Adds the heuristic costs for network to the current heuristic network costs
* for this Costs object.
*
* @param cost The heuristic network cost to add.
*/
public void addHeuristicNetworkCost(double cost) {
if (cost <= 0) {
throw new IllegalArgumentException("Heuristic costs must be positive.");
}
this.heuristicNetworkCost += cost;
// check for overflow
if (this.heuristicNetworkCost < 0) {
this.heuristicNetworkCost = Double.MAX_VALUE;
}
}
/**
* Gets the heuristic costs for disk.
*
* @return The heuristic disk cost.
*/
public double getHeuristicDiskCost() {
return this.heuristicDiskCost;
}
/**
* Sets the heuristic costs for disk for this Costs object.
*
* @param cost The heuristic disk cost to set.
*/
public void setHeuristicDiskCost(double cost) {
if (cost <= 0) {
throw new IllegalArgumentException("Heuristic costs must be positive.");
}
this.heuristicDiskCost = cost;
}
/**
* Adds the heuristic costs for disk to the current heuristic disk costs
* for this Costs object.
*
* @param cost The heuristic disk cost to add.
*/
public void addHeuristicDiskCost(double cost) {
if (cost <= 0) {
throw new IllegalArgumentException("Heuristic costs must be positive.");
}
this.heuristicDiskCost += cost;
// check for overflow
if (this.heuristicDiskCost < 0) {
this.heuristicDiskCost = Double.MAX_VALUE;
}
}
/**
* Gets the heuristic cost for the CPU.
*
* @return The heuristic CPU Cost.
*/
public double getHeuristicCpuCost() {
return this.heuristicCpuCost;
}
/**
* Sets the heuristic cost for the CPU.
*
* @param cost The heuristic CPU Cost.
*/
public void setHeuristicCpuCost(double cost) {
if (cost <= 0) {
throw new IllegalArgumentException("Heuristic costs must be positive.");
}
this.heuristicCpuCost = cost;
}
/**
* Adds the given heuristic CPU cost to the current heuristic CPU cost for this Costs object.
*
* @param cost The heuristic CPU cost to add.
*/
public void addHeuristicCpuCost(double cost) {
if (cost <= 0) {
throw new IllegalArgumentException("Heuristic costs must be positive.");
}
this.heuristicCpuCost += cost;
// check for overflow
if (this.heuristicCpuCost < 0) {
this.heuristicCpuCost = Double.MAX_VALUE;
}
}
// --------------------------------------------------------------------------------------------
/**
* Adds the given costs to these costs. If for one of the different cost components (network, disk),
* the costs are unknown, the resulting costs will be unknown.
*
* @param other The costs to add.
*/
public void addCosts(Costs other) {
// ---------- quantifiable costs ----------
if (this.networkCost == UNKNOWN || other.networkCost == UNKNOWN) {
this.networkCost = UNKNOWN;
} else {
this.networkCost += other.networkCost;
}
if (this.diskCost == UNKNOWN || other.diskCost == UNKNOWN) {
this.diskCost = UNKNOWN;
} else {
this.diskCost += other.diskCost;
}
if (this.cpuCost == UNKNOWN || other.cpuCost == UNKNOWN) {
this.cpuCost = UNKNOWN;
} else {
this.cpuCost += other.cpuCost;
}
// ---------- heuristic costs ----------
this.heuristicNetworkCost += other.heuristicNetworkCost;
this.heuristicDiskCost += other.heuristicDiskCost;
this.heuristicCpuCost += other.heuristicCpuCost;
}
/**
* Subtracts the given costs from these costs. If the given costs are unknown, then these costs are remain unchanged.
*
* @param other The costs to subtract.
*/
public void subtractCosts(Costs other) {
if (this.networkCost != UNKNOWN && other.networkCost != UNKNOWN) {
this.networkCost -= other.networkCost;
if (this.networkCost < 0) {
throw new IllegalArgumentException("Cannot subtract more cost then there is.");
}
}
if (this.diskCost != UNKNOWN && other.diskCost != UNKNOWN) {
this.diskCost -= other.diskCost;
if (this.diskCost < 0) {
throw new IllegalArgumentException("Cannot subtract more cost then there is.");
}
}
if (this.cpuCost != UNKNOWN && other.cpuCost != UNKNOWN) {
this.cpuCost -= other.cpuCost;
if (this.cpuCost < 0) {
throw new IllegalArgumentException("Cannot subtract more cost then there is.");
}
}
// ---------- relative costs ----------
this.heuristicNetworkCost -= other.heuristicNetworkCost;
if (this.heuristicNetworkCost < 0) {
throw new IllegalArgumentException("Cannot subtract more cost then there is.");
}
this.heuristicDiskCost -= other.heuristicDiskCost;
if (this.heuristicDiskCost < 0) {
throw new IllegalArgumentException("Cannot subtract more cost then there is.");
}
this.heuristicCpuCost -= other.heuristicCpuCost;
if (this.heuristicCpuCost < 0) {
throw new IllegalArgumentException("Cannot subtract more cost then there is.");
}
}
public void multiplyWith(int factor) {
this.networkCost = this.networkCost < 0 ? -1 : this.networkCost * factor;
this.diskCost = this.diskCost < 0 ? -1 : this.diskCost * factor;
this.cpuCost = this.cpuCost < 0 ? -1 : this.cpuCost * factor;
this.heuristicNetworkCost = this.heuristicNetworkCost < 0 ? -1 : this.heuristicNetworkCost * factor;
this.heuristicDiskCost = this.heuristicDiskCost < 0 ? -1 : this.heuristicDiskCost * factor;
this.heuristicCpuCost = this.heuristicCpuCost < 0 ? -1 : this.heuristicCpuCost * factor;
}
public void divideBy(int factor) {
this.networkCost = this.networkCost < 0 ? -1 : this.networkCost / factor;
this.diskCost = this.diskCost < 0 ? -1 : this.diskCost / factor;
this.cpuCost = this.cpuCost < 0 ? -1 : this.cpuCost / factor;
this.heuristicNetworkCost = this.heuristicNetworkCost < 0 ? -1 : this.heuristicNetworkCost / factor;
this.heuristicDiskCost = this.heuristicDiskCost < 0 ? -1 : this.heuristicDiskCost / factor;
this.heuristicCpuCost = this.heuristicCpuCost < 0 ? -1 : this.heuristicCpuCost / factor;
}
// --------------------------------------------------------------------------------------------
/**
* The order of comparison is: network first, then disk, then CPU. The comparison here happens each time
* primarily after the heuristic costs, then after the quantifiable costs.
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(Costs o) {
// check the network cost. if we have actual costs on both, use them, otherwise use the heuristic costs.
if (this.networkCost != UNKNOWN && o.networkCost != UNKNOWN) {
if (this.networkCost != o.networkCost) {
return this.networkCost < o.networkCost ? -1 : 1;
}
} else if (this.heuristicNetworkCost < o.heuristicNetworkCost) {
return -1;
} else if (this.heuristicNetworkCost > o.heuristicNetworkCost) {
return 1;
}
// next, check the disk cost. again, if we have actual costs on both, use them, otherwise use the heuristic costs.
if (this.diskCost != UNKNOWN && o.diskCost != UNKNOWN) {
if (this.diskCost != o.diskCost) {
return this.diskCost < o.diskCost ? -1 : 1;
}
} else if (this.heuristicDiskCost < o.heuristicDiskCost) {
return -1;
} else if (this.heuristicDiskCost > o.heuristicDiskCost) {
return 1;
}
// next, check the disk cost. again, if we have actual costs on both, use them, otherwise use the heuristic costs.
if (this.cpuCost != UNKNOWN && o.cpuCost != UNKNOWN) {
return this.cpuCost < o.cpuCost ? -1 : this.cpuCost > o.cpuCost ? 1 : 0;
} else if (this.heuristicCpuCost < o.heuristicCpuCost) {
return -1;
} else if (this.heuristicCpuCost > o.heuristicCpuCost) {
return 1;
} else {
return 0;
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long cpuCostBits = Double.doubleToLongBits(cpuCost);
long heuristicCpuCostBits = Double.doubleToLongBits(heuristicCpuCost);
long heuristicNetworkCostBits = Double.doubleToLongBits(heuristicNetworkCost);
long heuristicDiskCostBits = Double.doubleToLongBits(heuristicDiskCost);
long networkCostBits = Double.doubleToLongBits(networkCost);
long diskCostBits = Double.doubleToLongBits(diskCost);
result = prime * result + (int) (cpuCostBits ^ (cpuCostBits >>> 32));
result = prime * result + (int) (heuristicCpuCostBits ^ (heuristicCpuCostBits >>> 32));
result = prime * result + (int) (heuristicNetworkCostBits ^ (heuristicNetworkCostBits >>> 32));
result = prime * result + (int) (heuristicDiskCostBits ^ (heuristicDiskCostBits >>> 32));
result = prime * result + (int) (networkCostBits ^ (networkCostBits >>> 32));
result = prime * result + (int) (diskCostBits ^ (diskCostBits >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (obj.getClass() == getClass()) {
final Costs other = (Costs) obj;
return this.networkCost == other.networkCost &
this.diskCost == other.diskCost &
this.cpuCost == other.cpuCost &
this.heuristicNetworkCost == other.heuristicNetworkCost &
this.heuristicDiskCost == other.heuristicDiskCost &
this.heuristicCpuCost == other.heuristicCpuCost;
} else {
return false;
}
}
@Override
public String toString() {
return "Costs [networkCost=" + networkCost + ", diskCost=" + diskCost +
", cpuCost=" + cpuCost + ", heuristicNetworkCost=" + heuristicNetworkCost +
", heuristicDiskCost=" + heuristicDiskCost + ", heuristicCpuCost=" + heuristicCpuCost + "]";
}
@Override
public Costs clone() {
try {
return (Costs) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e); // should never happen
}
}
}