/* * * Copyright 2007-2008 University Of Southern California * * Licensed 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 edu.isi.pegasus.planner.provisioner; import java.util.*; /** * An instance of this class represents an independent task of a workflow. * A task has PET(predicted execution time), list of input data * sources(parent tasks), and list of output data(child tasks). This class * also used for BTS algorithm such as EST, LFT, schdeuled start/finish time * and core methods for BTS algorithm used by OccupationDiagram class. * * @author Eunkyu Byun * */ public class Node { public static final long DEFAULT_WEIGHT = 1; private LinkedList inEdges; private LinkedList outEdges; private Edge critEdge; private String id; private String taskName; private long weight; private long upLen; private long downLen; private HashSet ancestors; private HashSet descendants; public void init() { this.st = 0; this.ancET = -1; this.desET = -1; } /** * constructor * @param id the unique ID of this task * @param name task name(executable name) * @param w predicted execution time */ public Node(String id, String name, long w) { this(id, name, -1, w); } public Node(String id, String name, int n, long w) { this.upLen = -1; this.downLen = -1; this.id = id; this.taskName = name; this.weight = w; inEdges = new LinkedList(); outEdges = new LinkedList(); ancestors = new HashSet(); descendants = new HashSet(); init(); } public Node(String id) { this(id, "NullTask", DEFAULT_WEIGHT); } /** * set execution time of this task * @param w excution time */ public void setWeight(long w) { this.weight = w; } /** * return execution time of this task * @return excution time */ public long evalWeight() { return weight; } /** * add a data dependency edge to this task * @param e the edge to be added */ public void addIn(Edge e) { if( !inEdges.contains(e) ) { inEdges.add(e); } } /** * remove a specified data dependency edge from this task * @param e the edge to be removed */ public void removeIn(Edge e) { // System.out.println("in "+id+" edge "+e.getID() + " is removedIn"); inEdges.remove(e); } /** * add a data dependency edge from this task * @param e the edge to be added */ public void addOut(Edge e) { if( !outEdges.contains(e) ) { outEdges.add(e); } } /** * remove a specified data dependency edge from this task * @param e the edge to be removed */ public void removeOut(Edge e) { // System.out.println("in "+id+" edge "+e.getID() + " is removedOut"); outEdges.remove(e); } /** * check whether all parent tasks are finished. * @return true if all parent tasks are finished. */ public boolean checkIn() { boolean ret = true; Iterator iter = inEdges.iterator(); while(iter.hasNext()) { Edge next =(Edge)iter.next(); ret &= next.complete; st = Math.max(st, next.compTime + next.getCost() ); } return ret; } /** * return the list of incoming edges. * @return LinkedList of edges. */ public LinkedList getIn() { return inEdges; } /** * return the list of outgoing edges. * @return LinkedList of edges. */ public LinkedList getOut() { return outEdges; } /** notify child nodes that this task will finish at time et * @param et finish time */ public void initOut(boolean s,long et) { Iterator iter = outEdges.iterator(); while(iter.hasNext()) { Edge next =(Edge)iter.next(); next.complete = s; if(s) { next.compTime = Math.max(next.compTime,et); } else { next.compTime = et; } } } public long st = 0; //scheduled time, used for HEFT algorithm // Calculate UpLength private void updateUpLen() { if( isTop() ) { this.upLen = 0; return; } long maxUpLen = -1; Iterator parents = inEdges.iterator(); while( parents.hasNext() ) { Edge next = (Edge)parents.next(); Node cur = next.getFrom(); long curUpLen = cur.getUpLen() + cur.evalWeight() + next.getCost(); if( maxUpLen < curUpLen ) { maxUpLen = curUpLen; critEdge = next; } } this.upLen = maxUpLen; } /** * return the UpLength of this task. UpLength is the longest path from the entry task * @return UpLength */ public long getUpLen() { if( this.upLen < 0 ) { updateUpLen(); } return this.upLen; } private void updateDownLen() { if( isBottom() ) { downLen = 0; return; } Iterator iter = outEdges.iterator(); while( iter.hasNext() ) { Edge ce = (Edge)iter.next(); Node cn = ce.getTo(); downLen = Math.max( downLen, cn.getDownLen() + cn.evalWeight() + ce.getCost()); } } /** * return the DnLength of this task. DnLength is the longest path to the exit task * @return DnLength */ public long getDownLen() { if( downLen == -1 ) updateDownLen(); return this.downLen; } /** * Check whether this task is the entry task or not. * @return true or false */ public boolean isTop() { return (inEdges.size() == 0); } /** * Check whether this task is the exit task or not. * @return true or false */ public boolean isBottom() { return (outEdges.size() == 0); } public String getID() { return id; } /** * update EST(earliest start time) of this task. * @param nlb new EST * @param set set of ancestor tasks */ public void updateLeftBound(long nlb, /*LinkedList*/TreeSet set) { // System.out.print("updateLeftBound in "+id+" from "+lb); if( nlb > lb ) { // System.out.print(" to "+nlb+ "\r\n"); boolean contain = false; if( set != null ) contain = set.remove(this); this.lb = nlb; if(contain) { // System.out.println(id+ " is removed in updateLeftBound"); boolean test = set.add(this); // System.out.println(test); } for(int i = 0 ; i < outEdges.size() ; i++) { Edge ce = (Edge)outEdges.get(i); Node n = ce.getTo(); n.updateLeftBound(nlb + weight + ce.getCost(), set); } } //else System.out.print("\r\n"); } /** * update LFT(latest finish time) of this task. * @param nrb new LFT * @param set set of descendant tasks */ public void updateRightBound(long nrb, /*LinkedList*/TreeSet set) { // System.out.print("updateRightBound in "+id+" from "+rb); if( nrb < rb ) { // System.out.print(" to "+nrb+"\r\n"); boolean contain = false; if( set != null ) contain = set.remove(this); this.rb = nrb; if(contain) { // System.out.println(id+ " is removed in updateRightBound"); boolean test = set.add(this); // System.out.println(test); } for(int i = 0 ; i < inEdges.size() ; i++) { Edge ce = (Edge)inEdges.get(i); Node n = ce.getFrom(); n.updateRightBound(nrb - weight - ce.getCost() , set); } } //else System.out.print("\r\n"); } public boolean stacked = false; //bit for stacked in Occupation diagram public long lb; //left bound (EST) public long rb; //right bound (LFT) public long olb; //original left bound public long orb; //original right bound public long tempST; //scheduled start time public long tempFT; //scheduled finish time /** * Non-propagate redistribution of this task * @param timeMap List of scheduled tasks at each time slot. i.e., Occupation diagram itself * @param limit maximum height of Occupation diagram. The result of redistribution should cause taller height of this limit. * @param force Not used. * @return true if this task is redistributed. */ public boolean NPbalance(LinkedList[] timeMap, int limit, boolean force) { if( weight == 0 ) return false; if( !force && (tempST - lb) < weight && (rb - tempFT) < weight ) return false; int min = limit; int cursor = (int)lb; boolean rightBias = (getDesSize()<getAncSize()); for(int i = (int)lb ; i < rb-weight ; i++) { int localMax = 0; for(int j = i ; j < (i+weight); j++) { int overlap = (j >= tempST && j < tempFT) ? 1 : 0; localMax = Math.max(localMax, timeMap[j].size() - overlap); } if( min > localMax ) { min = localMax; cursor = i; } if( min == localMax && rightBias) { cursor = i; } } if( (min+1) >= limit && !force ) return false; // System.out.println("NPbalance ID:"+id+" limit:"+limit+" from: "+ tempST+" to: " + cursor); for(int i = (int)tempST ; i < tempFT ; i++) timeMap[i].remove(this); tempST = cursor; tempFT = cursor+weight; for(int i = (int)tempST ; i < tempFT ; i++) timeMap[i].add(this); for(int i = 0; i < outEdges.size() ;i++) { Edge ce = (Edge)outEdges.get(i); Node n = ce.getTo(); n.updateLeftBound(tempFT+ce.getCost(),null); } for(int i = 0; i < inEdges.size() ;i++) { Edge ce = (Edge)inEdges.get(i); Node n = ce.getFrom(); n.updateRightBound(tempST-ce.getCost(),null); } return (min<limit); } /** * Redistribute this task to the earlier time. This causes ancestor tasks to be redistributed too. * @param timeMap List of scheduled tasks at each time slot. i.e., Occupation diagram itself * @param limit maximum height of Occupation diagram. The result of redistribution should cause taller height of this limit. * @param bound Earliest time this tasks can be scheduled. * @return true if this task is redistributed. */ public boolean moveLeft(LinkedList[] timeMap, int limit, long bound) { boolean found = false; int cursor = (int)(bound - weight); for(int i = (int)(bound - weight) ; i >= olb ; i-- ) { int localMax = 0; for(int j = i ; j < (i+weight); j++) { int overlap = (j >= tempST && j < tempFT) ? 1 : 0; localMax = Math.max(localMax, timeMap[j].size() - overlap); } if( limit > (localMax + 1 - pushedParents(i) ) ) { cursor = i; found = true; break; } } // System.out.println("moveLeft ID:"+id+" limit:"+limit + " bound:"+ bound +" found:" + found+ " " + cursor); if( !found ) return false; //not possible to move // push parent nodes for(int i = 0; i < inEdges.size() && found ;i++) { Edge ce = (Edge)inEdges.get(i); Node n = ce.getFrom(); if( cursor >= lb ) { n.updateRightBound(cursor - ce.getCost(), null); } else { found &= n.moveLeft( timeMap, limit, cursor - ce.getCost() ); } } if( !found ) return false; //child block!! // re-schedule lb = Math.min(lb, cursor); for(int i = (int)tempST; i < tempFT ; i++) timeMap[i].remove(this); tempST = cursor; tempFT = cursor + weight; for(int i = (int)tempST; i < tempFT ; i++) timeMap[i].add(this); //release leftbound of child nodes for(int i = 0; i < outEdges.size() ;i++) { Edge ce = (Edge)outEdges.get(i); Node n = ce.getTo(); n.updateLeftBound(tempFT+ce.getCost(),null); } return true; } /** * Redistribute this task to the later time. This causes descensant tasks to be redistributed too. * @param timeMap List of scheduled tasks at each time slot. i.e., Occupation diagram itself * @param limit maximum height of Occupation diagram. The result of redistribution should cause taller height of this limit. * @param bound Lastest time this tasks can be scheduled. * @return true if this task is redistributed. */ public boolean moveRight(LinkedList[] timeMap, int limit, long bound) { boolean found = false; int cursor = (int)bound; for(int i = (int)bound ; i <= (orb-weight) ; i++ ) { int localMax = 0; for(int j = i ; j < (i+weight); j++) { int overlap = (j >= tempST && j < tempFT) ? 1 : 0; localMax = Math.max(localMax, timeMap[j].size() - overlap); } if( limit > (localMax + 1 - pushedChildren(i + weight)) ) { cursor = i; found = true; break; } } // System.out.println("moveRight ID:"+id+" limit:"+limit + " bound:"+ bound +" found:" + found+ " " + cursor); if( !found ) return false; //not possible to move // push child nodes for(int i = 0; i < outEdges.size() && found ;i++) { Edge ce = (Edge)outEdges.get(i); Node n = ce.getTo(); if( cursor+weight <= rb ) { n.updateLeftBound(cursor + weight + ce.getCost(), null); } else { found &= n.moveRight( timeMap, limit, cursor + weight + ce.getCost() ); } } if( !found ) return false; //child block!! // re-schedule rb = Math.max(rb, cursor); for(int i = (int)tempST; i < tempFT ; i++) timeMap[i].remove(this); tempST = cursor; tempFT = cursor + weight; for(int i = (int)tempST; i < tempFT ; i++) timeMap[i].add(this); //release leftbound of child nodes for(int i = 0; i < inEdges.size() ;i++) { Edge ce = (Edge)inEdges.get(i); Node n = ce.getFrom(); n.updateRightBound(tempST - ce.getCost(),null); } return true; } private int pushedParents(long timeLimit) { int result = 0; for(int i = 0 ; i < inEdges.size(); i++) { Edge ce = (Edge)inEdges.get(i); Node cn = ce.getFrom(); if( timeLimit < cn.tempFT ) { result++; } } return result; } private int pushedChildren(long timeLimit) { int result = 0; for(int i = 0 ; i < outEdges.size(); i++) { Edge ce = (Edge)outEdges.get(i); Node cn = ce.getTo(); if( timeLimit > cn.tempST ) { result++; } } return result; } /** * check all child task's start time is scheduled after the finishtime of this task */ public void checkIntegrity() { for(int i = 0 ; i < outEdges.size(); i++ ) { Edge ce = (Edge)outEdges.get(i); Node cn = ce.getTo(); if( (tempFT + ce.getCost()) > cn.tempST ) { System.out.println("Violation! from:"+id+"("+tempFT+") to "+cn.getID()+"("+cn.tempST+")"); } cn.checkIntegrity(); } } /** * build the list of descendant tasks * @return the list of descensant tasks */ public Set buildDescendants() { if( !isBottom() && descendants.size() == 0 ) { Iterator iter = outEdges.iterator(); while( iter.hasNext() ) { Edge ce = (Edge)iter.next(); Node cn = ce.getTo(); if( !cn.isBottom() ) { descendants.add(cn); descendants.addAll(cn.buildDescendants()); } } } return descendants; } /** * build the list of ancestor tasks * @return the list of ancestor tasks */ public Set buildAncestors() { if( !isTop() && ancestors.size() == 0 ) { Iterator iter = inEdges.iterator(); while( iter.hasNext() ) { Edge ce = (Edge)iter.next(); Node cn = ce.getFrom(); if( !cn.isTop() ) { ancestors.add(cn); ancestors.addAll(cn.buildAncestors()); } } } return ancestors; } public int getDepSize() { return descendants.size() + ancestors.size(); } public int getAncSize() { return ancestors.size(); } public int getDesSize() { return descendants.size(); } private long ancET; private long desET; /** * @return the sum of execution time of all ancestor tasks */ public long getAncET() { if( ancET < 0 ) { ancET = 0; Iterator iter = ancestors.iterator(); while( iter.hasNext() ) { Node cn = (Node)iter.next(); ancET += cn.evalWeight(); } } return ancET; } /** * @return the sum of execution time of all descendant tasks */ public long getDesET() { if( desET < 0 ) { desET = 0; Iterator iter = descendants.iterator(); while( iter.hasNext() ) { Node cn = (Node)iter.next(); desET += cn.evalWeight(); } } return desET; } /** * @return the sum of execution time of all dependent tasks */ public long getDepET() { return getAncET() + getDesET(); } public boolean isAnc(Node n) { return ancestors.contains(n); } public boolean isDes(Node n) { return descendants.contains(n); } public void print() { String inList = ""; Iterator iter = inEdges.iterator(); while(iter.hasNext() ) { inList += ((Edge)iter.next()).getID() + ","; } String outList = ""; iter = outEdges.iterator(); while(iter.hasNext() ) { outList += ((Edge)iter.next()).getID() + ","; } // System.out.println("Node_"+id+"("+ taskName +","+ weight +"sec) IN:"+inList+", OUT:"+outList); System.out.println("Node_"+id+"("+ taskName +","+ weight +") upLen:"+upLen+", downLen:" + downLen ); } public int cluster = -1; public long tlevel = 0; public boolean examined = false; /** * used for DSC algorithm */ public boolean isFree() { if( examined ) return false; boolean result = true; for(int i = 0 ; i < inEdges.size() ; i++ ) { Edge ce = (Edge)inEdges.get(i); Node cn = ce.getFrom(); if( !cn.examined ) { result = false; break; } } return result; } public boolean equals(Object e) { // System.out.println("comparison is called this"); if( e instanceof Node ) { Node cn = (Node)e; boolean same = cn.getID().equals(id); // System.out.println(id+ " " + cn.getID() + " " + same); return same; } else return false; } }