blob: 427fe1f209930a8a19eb7625a36e56cbae5875d8 [file]
/*
* 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.sysds.hops.cost;
import org.apache.sysds.api.DMLException;
import org.apache.sysds.common.Types;
import org.apache.sysds.common.Types.ExecType;
import org.apache.sysds.hops.Hop;
import org.apache.sysds.hops.fedplanner.FTypes;
import org.apache.sysds.hops.fedplanner.FTypes.FType;
import org.apache.sysds.hops.fedplanner.MemoTable;
import org.apache.sysds.hops.rewrite.HopRewriteUtils;
import org.apache.sysds.runtime.instructions.fed.FEDInstruction;
import org.apache.sysds.runtime.instructions.fed.FEDInstruction.FederatedOutput;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* HopRel provides a representation of the relation between a hop, the cost of setting a given FederatedOutput value,
* and the input dependency with the given FederatedOutput value.
* The HopRel class is used when building and selecting an optimal federated execution plan in IPAPassRewriteFederatedPlan.
* The input dependency is needed to hold the valid and optimal FederatedOutput values for the inputs.
*/
public class HopRel {
protected final Hop hopRef;
protected final FEDInstruction.FederatedOutput fedOut;
protected ExecType execType;
protected FTypes.FType fType;
protected FederatedCost cost;
protected final Set<Long> costPointerSet = new HashSet<>();
protected List<Hop> inputHops;
protected List<HopRel> inputDependency = new ArrayList<>();
/**
* Constructs a HopRel with input dependency and cost estimate based on entries in hopRelMemo.
* @param associatedHop hop associated with this HopRel
* @param fedOut FederatedOutput value assigned to this HopRel
* @param hopRelMemo memo table storing other HopRels including the inputs of associatedHop
*/
public HopRel(Hop associatedHop, FEDInstruction.FederatedOutput fedOut, MemoTable hopRelMemo){
this(associatedHop, fedOut, null, hopRelMemo,associatedHop.getInput());
}
/**
* Constructs a HopRel with input dependency and cost estimate based on entries in hopRelMemo.
* @param associatedHop hop associated with this HopRel
* @param fedOut FederatedOutput value assigned to this HopRel
* @param hopRelMemo memo table storing other HopRels including the inputs of associatedHop
* @param inputs hop inputs which input dependencies and cost is based on
*/
public HopRel(Hop associatedHop, FEDInstruction.FederatedOutput fedOut, MemoTable hopRelMemo, ArrayList<Hop> inputs){
this(associatedHop, fedOut, null, hopRelMemo, inputs);
}
private HopRel(Hop associatedHop, FEDInstruction.FederatedOutput fedOut, FType fType, List<Hop> inputs){
hopRef = associatedHop;
this.fedOut = fedOut;
this.fType = fType;
this.inputHops = inputs;
}
/**
* Constructs a HopRel with input dependency and cost estimate based on entries in hopRelMemo.
* @param associatedHop hop associated with this HopRel
* @param fedOut FederatedOutput value assigned to this HopRel
* @param fType Federated Type of the output of this hopRel
* @param hopRelMemo memo table storing other HopRels including the inputs of associatedHop
* @param inputs hop inputs which input dependencies and cost is based on
*/
public HopRel(Hop associatedHop, FEDInstruction.FederatedOutput fedOut, FType fType, MemoTable hopRelMemo, ArrayList<Hop> inputs){
this(associatedHop, fedOut, fType, inputs);
setInputDependency(hopRelMemo);
cost = FederatedCostEstimator.costEstimate(this, hopRelMemo);
setExecType();
}
public HopRel(Hop associatedHop, FEDInstruction.FederatedOutput fedOut, FType fType, MemoTable hopRelMemo, List<Hop> inputs, List<FType> inputDependency){
this(associatedHop, fedOut, fType, inputs);
setInputFTypeDependency(inputs, inputDependency, hopRelMemo);
cost = FederatedCostEstimator.costEstimate(this, hopRelMemo);
setExecType();
}
private void setInputFTypeDependency(List<Hop> inputs, List<FType> inputDependency, MemoTable hopRelMemo){
for ( int i = 0; i < inputs.size(); i++ ){
this.inputDependency.add(hopRelMemo.getHopRel(inputs.get(i), inputDependency.get(i)));
}
validateInputDependency();
}
private void setExecType(){
if ( inputDependency.stream().anyMatch(HopRel::hasFederatedOutput)
|| HopRewriteUtils.isData(hopRef, Types.OpOpData.FEDERATED))
execType = ExecType.FED;
}
/**
* Adds hopID to set of hops pointing to this HopRel.
* By storing the hopID it can later be determined if the cost
* stored in this HopRel is already used as input cost in another HopRel.
* @param hopID added to set of stored cost pointers
*/
public void addCostPointer(long hopID){
costPointerSet.add(hopID);
}
/**
* Checks if another Hop is refering to this HopRel in memo table.
* A reference from a HopRel with same Hop ID is allowed, so this
* ID is ignored when checking references.
* @param currentHopID to ignore when checking references
* @return true if another Hop refers to this HopRel in memo table
*/
public boolean existingCostPointer(long currentHopID){
if ( costPointerSet.contains(currentHopID) )
return costPointerSet.size() > 1;
else return costPointerSet.size() > 0;
}
public boolean hasLocalOutput(){
return fedOut == FederatedOutput.LOUT;
}
public boolean hasFederatedOutput(){
return fedOut == FederatedOutput.FOUT;
}
public FederatedOutput getFederatedOutput(){
return fedOut;
}
public List<HopRel> getInputDependency(){
return inputDependency;
}
public Hop getHopRef(){
return hopRef;
}
public FType getFType(){
return fType;
}
public void setFType(FType fType){
this.fType = fType;
}
public ExecType getExecType(){
return execType;
}
/**
* Returns FOUT HopRel for given hop found in hopRelMemo or returns null if HopRel not found.
* @param hop to look for in hopRelMemo
* @param hopRelMemo memo table storing HopRels
* @return FOUT HopRel found in hopRelMemo
*/
private HopRel getFOUTHopRel(Hop hop, MemoTable hopRelMemo){
return hopRelMemo.getFederatedOutputAlternativeOrNull(hop);
}
/**
* Set valid and optimal input dependency for this HopRel as a field.
* @param hopRelMemo memo table storing input HopRels
*/
private void setInputDependency(MemoTable hopRelMemo){
if (inputHops != null && inputHops.size() > 0) {
if ( fedOut == FederatedOutput.FOUT && !hopRef.isFederatedDataOp() ) {
int lowestFOUTIndex = 0;
HopRel lowestFOUTHopRel = getFOUTHopRel(inputHops.get(0), hopRelMemo);
for(int i = 1; i < inputHops.size(); i++) {
Hop input = inputHops.get(i);
HopRel foutHopRel = getFOUTHopRel(input, hopRelMemo);
if(lowestFOUTHopRel == null) {
lowestFOUTHopRel = foutHopRel;
lowestFOUTIndex = i;
}
else if(foutHopRel != null) {
if(foutHopRel.getCost() < lowestFOUTHopRel.getCost()) {
lowestFOUTHopRel = foutHopRel;
lowestFOUTIndex = i;
}
}
}
HopRel[] inputHopRels = new HopRel[inputHops.size()];
for(int i = 0; i < inputHops.size(); i++) {
if(i != lowestFOUTIndex) {
Hop input = inputHops.get(i);
inputHopRels[i] = hopRelMemo.getMinCostAlternative(input);
}
else {
inputHopRels[i] = lowestFOUTHopRel;
}
}
inputDependency.addAll(Arrays.asList(inputHopRels));
} else {
inputDependency.addAll(
inputHops.stream()
.map(hopRelMemo::getMinCostAlternative)
.collect(Collectors.toList()));
}
}
validateInputDependency();
}
/**
* Throws exception if any input dependency is null.
* If any of the input dependencies are null, it is not possible to build a federated execution plan.
* If this null-state is not found here, an exception will be thrown at another difficult-to-debug place.
*/
private void validateInputDependency(){
for ( int i = 0; i < inputDependency.size(); i++){
if ( inputDependency.get(i) == null)
throw new DMLException("HopRel input number " + i + " (" + hopRef.getInput(i) + ")"
+ " is null for root: \n" + this);
}
}
/**
* Get total cost as double
* @return cost as double
*/
public double getCost(){
return cost.getTotal();
}
/**
* Get cost object
* @return cost object
*/
public FederatedCost getCostObject(){
return cost;
}
@Override
public String toString(){
StringBuilder strB = new StringBuilder();
strB.append(this.getClass().getSimpleName());
strB.append(" {HopID: ");
strB.append(hopRef.getHopID());
strB.append(", Opcode: ");
strB.append(hopRef.getOpString());
strB.append(", FedOut: ");
strB.append(fedOut);
strB.append(", Cost: ");
strB.append(cost.getTotal());
strB.append(", Inputs: ");
strB.append(inputDependency.stream().map(i -> "{" + i.getHopRef().getHopID() +
", " + i.getFederatedOutput() + "}").collect(Collectors.toList()));
strB.append("}");
return strB.toString();
}
}