Poold
)
* consists of determining all possible resource moves across all
* resources. Once all moves are known, all registered objectives
* (i.e. instances of this interface) are called and asked to value
* the move.
*
* Note, the output of this method is constrained to be between -1 and
* 1, representing minimum and maximum desirability of this move in
* terms of this objective. This is enforced by Poold
and
* an IllegalOFValueException
will be thrown if this
* constraint is broken.
*/
interface Objective
{
/**
* Return the contribution of this objective. The contribution
* is constrainted to be a value between -1 and +1 to ensure
* that no objective can make a disproportionate contribution
* to the total result.
*
* The more desirable this move appears in terms of this
* objective, the closer to +1 will be the value. A value of 0
* indicates that the move is neutral in terms of the
* objective. A negative value indicates that the move is
* undesirable.
*
* @param conf The configuration which is being examined
* @param move The move under consideration
* @param elem The element to which the objective applies
*
* @throws PoolsException if there is an error manipulating
* the configuration
*/
public double calculate(Configuration conf, Move move, Element elem)
throws PoolsException;
/**
* Set the objective's expression to the supplied parameter.
*
* @param exp An expression for this objective.
*/
public void setExpression(Expression exp);
/**
* Get the objective's expression.
*/
public Expression getExpression();
}
/**
* This interface must be implemented by all Objectives which are
* workload dependent. The examine method is used by a Solver to
* determine if the objective is still being satisfied.
*/
interface WorkloadDependentObjective extends Objective
{
/**
* This method returns true if the Objective is no longer
* satisfied. If the objective is still satisfied, then return
* false.
*
* @param conf The configuration to be examined
* @param solver The solving interface used to get utilization
* information
* @param elem The element to which the objective belongs
*
* @throws PoolsException if there is an error examining the
* pool configuration
* @throws StaleMonitorException if there is an error accessing
* the element's ResourceMonitor
*/
public boolean examine(Configuration conf, Solver solver,
Element elem) throws PoolsException, StaleMonitorException;
}
/**
* This class provides a skeletal implementation of the
* Objective
interface to minimize the effort required
* to implement this interface.
*
* To implement an objective, the programmer need only to extend this
* class and add the name of the class into the appropriate element
* objectives property in the poold.properties
file.
*/
abstract class AbstractObjective implements Objective
{
abstract public double calculate(Configuration conf, Move move,
Element elem) throws PoolsException;
/**
* The objectives which are recognized by this class
*/
private static Map objectives;
/**
* The expression associated with this objective
*/
private Expression exp;
/**
* Set the objective's expression to the supplied parameter.
*
* @param exp An expression for this objective.
*/
public void setExpression(Expression exp)
{
this.exp = exp;
}
/**
* Get the objective's expression.
*/
public Expression getExpression()
{
return (exp);
}
/**
* A factory method which returns a created objective which is
* associated with the supplied expression. The type and the
* expression are used to identify valid types of objectives
* to which this expression may be applied. If an acceptable
* objective cannot be found for the supplied type, then an
* IllegalArgumentException
will be thrown.
*
* @param type The element type for which an objective must be
* found
* @param exp The expression which will be associated with the
* objective
*
* @throws IllegalArgumentExcetion if the supplied expression
* cannot be associated with an objective of the supplied type
*/
public static Objective getInstance(String type, Expression exp)
throws IllegalArgumentException
{
Objective ret = null;
Map typeObjs = null;
initMapIfNecessary();
typeObjs = (Map)objectives.get(type);
if (typeObjs != null) {
Class objClass = (Class)typeObjs.get(exp.getName());
if (objClass != null) {
try {
ret = (Objective) objClass.
newInstance();
} catch (Exception e) {
Poold.utility.die(Poold.OPT_LOG, e,
true);
}
ret.setExpression(exp);
}
}
if (ret == null)
throw new IllegalArgumentException(
"unrecognized objective name for " + type + ": " +
exp.toString());
return (ret);
}
/**
* Return a string representation of this objective.
*/
public String toString()
{
return (exp.toString());
}
/**
* Initialize the implementation map the first time it's
* called.
*/
private static void initMapIfNecessary()
{
/*
* Setup the objectives map for the known classes
*/
if (objectives == null) {
objectives = new HashMap();
Properties props = new Properties();
try {
props.load(
new FileInputStream(
Poold.POOLD_PROPERTIES_PATH));
} catch (IOException ioe) {
Poold.utility.die(Poold.CONF_LOG, ioe);
}
registerObjectives(props, objectives, "system");
registerObjectives(props, objectives, "pset");
}
}
/**
* Add the objectives contained in the supplied properties to
* the set of valid objectives. The objectives are updated
* with objectives of the supplied type contained in the
* properties.
*
* @param props The properties containing the objectives
* @param objectives The objectives to be updated
* @param type The type of objectives to be added
*/
private static void registerObjectives(Properties props,
Map objectives, String type)
{
Map typeObjs = new HashMap();
String objs = props.getProperty(type + ".objectives");
String objNames[] = objs.split(",");
for (int i = 0; i < objNames.length; i++) {
String objName = objNames[i].trim();
try {
Class clazz = Class.forName(objName);
Field field = clazz.getDeclaredField("name");
String key = (String) field.get(null);
typeObjs.put(key, clazz);
} catch (ClassNotFoundException cnfe) {
Poold.utility.die(Poold.CONF_LOG, cnfe);
} catch (NoSuchFieldException nsfe) {
Poold.utility.die(Poold.CONF_LOG, nsfe);
} catch (IllegalAccessException iae) {
Poold.utility.die(Poold.CONF_LOG, iae);
}
}
objectives.put(type, typeObjs);
}
/**
* Indicates whether some other Objective is "equal to this
* one.
* @param o the reference object with which to compare.
* @return true
if this object is the same as the
* o argument; false
otherwise.
* @see #hashCode()
*/
public boolean equals(Object o)
{
if (o == this)
return (true);
if (!(o instanceof Objective))
return (false);
Objective other = (Objective) o;
return (getExpression().equals(other.getExpression()));
}
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hashtables such as those provided by
* java.util.Hashtable
.
*
* @return a hash code value for this object.
* @see #equals(java.lang.Object)
* @see java.util.Hashtable
*/
public int hashCode()
{
return (getExpression().hashCode());
}
}
/**
* The WeightedLoadObjective
class implements a Weighted
* Load Objective for Poold
.
*
* The goal is to allocate more resources to those resource partitions
* which are heavily loaded. The weighting is determined from the
* objective importance and the pool.importance.
*/
final class WeightedLoadObjective extends AbstractObjective
implements WorkloadDependentObjective
{
/**
* The name of the class.
*/
static final String name = "wt-load";
/**
* The map of calculations made during examination.
*/
Map calcMap;
/**
* Determine whether an objective is satisfied. If the
* objective is still satisfied, return false; otherwise
* return true.
*
* This objective examination determines if all resource sets
* are allocated the share of resources that their utilization
* would indicate they should be. This attempts to ensure that
* highly utilized resource sets recieve the greater
* proportion of available resources.
*
* @param conf The configuration to be examined
* @param solver The solving interface used to get utilization
* information
* @param elem The element to which the objective belongs
*
* @throws PoolsException if there is an error examining the
* pool configuration
* @throws StaleMonitorException if there is an error accessing
* the element's ResourceMonitor
*/
public boolean examine(Configuration conf, Solver solver,
Element elem) throws PoolsException, StaleMonitorException
{
Monitor mon = solver.getMonitor();
Value val = new Value("type", "pset");
List valueList = new LinkedList();
calcMap = new HashMap();
valueList.add(val);
List resList = conf.getResources(valueList);
val.close();
Iterator itRes = resList.iterator();
Calculation.totalUtil = 0;
Calculation.resQ = 0;
while (itRes.hasNext()) {
Resource res = (Resource) itRes.next();
List CPUs = res.getComponents(null);
try {
Calculation calc = new Calculation(res, CPUs,
mon.getUtilization(res),
res.getLongProperty("pset.min"),
res.getLongProperty("pset.max"));
calcMap.put(res, calc);
} catch (StaleMonitorException sme) {
Poold.MON_LOG.log(Severity.INFO,
res.toString() +
" not participating in " + toString() +
" calculatation as it has no " +
"available statistics.");
}
}
Iterator itCalc = calcMap.values().iterator();
while (itCalc.hasNext()) {
Calculation calc = (Calculation) itCalc.next();
if (calc.getShare() != calc.comp.size() &&
calc.getShare() >= calc.min) {
Poold.MON_LOG.log(Severity.INFO,
elem.toString() +
" utilization objective not satisfied " +
toString() + " with desired share " +
calc.getShare() + " and actual share " +
calc.comp.size());
return (true);
}
}
return (false);
}
/**
* Holds data about weighted load calculations. This class is
* basically a structure which holds information specific to a
* weighted-load calculation
*/
static class Calculation {
/**
* The resource on which this calculation is based.
*/
Resource res;
/**
* The list of component resources held by this resource.
*/
List comp;
/**
* The utilization of this resource.
*/
double util;
/**
* The minimum value of this resource's size.
*/
long min;
/**
* The maximum value of this resource's size.
*/
long max;
/**
* The total utilization of all instances of this class.
*/
static double totalUtil;
/**
* The total quantity of resource for all instances of
* this class.
*/
static int resQ;
/**
* Constructor. The class is immutable and holds
* information specific to a set of calculations about
* load.
*
* @param res The resource set
* @param comp The resource components
* @param util The resource utilization
* @param min The minimum qty of resource for this set
* @param max The maximum qty of resource for this set
*/
public Calculation(Resource res, List comp, double util,
long min, long max)
{
this.res = res;
this.comp = comp;
this.min = min;
this.max = max;
this.util = (util / 100) * comp.size();
Calculation.totalUtil += this.util;
Calculation.resQ += comp.size();
}
/**
* Return the share of the total resource for this
* resource.
*/
long getShare()
{
if (util == 0)
return (0);
return (Math.round((util / totalUtil) * resQ));
}
public String toString()
{
StringBuffer buf = new StringBuffer();
buf.append("res: " + res.toString());
buf.append(" components: " + comp.toString());
buf.append(" min: " + min);
buf.append(" max: " + max);
buf.append(" util: " + util);
buf.append(" total resource: " + resQ);
buf.append(" total utilization: " + totalUtil);
buf.append(" share: " + getShare());
return (buf.toString());
}
}
/**
* Calculates the value of a configuration in terms of this
* objective.
*
* In the examination step, calculations of each resource's
* current and desired share were made. The moves can thus be
* assessed in terms of their impact upon the desired
* share. The current difference from desired is already
* known, so each move will serve to reduce or increase that
* difference. Moves that increase the difference have a
* negative score, those that reduce it have a positive
* score. All scores are normalized to return a value between
* -1 and 1.
*
* @param conf Configuration to be scored.
* @param move Move to be scored.
* @param elem The element to which the objective applies
* @throws PoolsException If an there is an error in execution.
*/
public double calculate(Configuration conf, Move move, Element elem)
throws PoolsException
{
double ret = 0;
Poold.OPT_LOG.log(Severity.DEBUG,
"Calculating objective type: " + name);
/*
* There shouldn't be any empty moves, but if there
* are they are rated at 0.
*/
if (move.getQty() == 0)
return (0);
/*
* Find the calculations that represent the source and
* target of the move.
*/
Calculation src = (Calculation) calcMap.get(move.getFrom());
Calculation tgt = (Calculation) calcMap.get(move.getTo());
/*
* Use the calculation details to determine the "gap"
* i.e. number of discrete resources (for a processor
* set these are CPUs), between the desired quantity in
* the set which the calculations represent. Do this
* both before and after the proposed move.
*
* The maximum possible improvement is equal to the
* total number of resources for each set participating
* in the calculation. Since there are two sets we
* know the maximum possible improvement is resQ * 2.
*
* Divide the aggregated change in gap across participating
* sets by the maximum possible improvement to obtain
* a value which scores the move and which is normalised
* between -1 <= ret <= 1.
*/
long oldGap = Math.abs(src.getShare() -
src.comp.size());
long newGap = Math.abs(src.getShare() -
(src.comp.size() - move.getQty()));
ret = oldGap - newGap;
oldGap = Math.abs(tgt.getShare() -
tgt.comp.size());
newGap = Math.abs(tgt.getShare() -
(tgt.comp.size() + move.getQty()));
ret += oldGap - newGap;
ret /= ((double) Calculation.resQ * 2);
Poold.MON_LOG.log(Severity.DEBUG, "ret: " + ret);
return (ret);
}
}
/*
* The following LGroupData and Resulttuple and PSETData classes
* are used for the purposes of calculating and storing
* results sets for the LocalityObjective calculate method.
*/
/*
* To store data for a Localitygroup.
*
* The lgroup is the LocalityGroup.
* The numcpu is the number of cpu in the LocalityGroup.
* The factor is a value required in calculating the LocalityGroup quotient.
*
* The value of factor will always be a finite number
* because the LocalityGroup will never be empty.
*/
final class LGroupData
{
private LocalityGroup lgroup;
private int numcpu = 0;
private double factor;
LGroupData(LocalityGroup l) {
lgroup = l;
int numcpuinlgroup = lgroup.getCPUIDs().length;
factor = 2.0 / ((numcpuinlgroup * numcpuinlgroup)
+ numcpuinlgroup);
}
int getNumcpu() {
return numcpu;
}
double getFactor() {
return factor;
}
void incNumcpu() {
numcpu++;
}
}
/*
* Stores the results of caclulated locality quotients for a PSET.
*
* The AsIsResult is the quotient without any move.
* The FromResult is the quotient when a cpu is taken from it.
* The To result is the quotient when a cpu is added to it.
*/
final class ResultTuple
{
private double AsIsResult = 0;
private double FromResult = 0;
private double ToResult = 0;
ResultTuple(double a, double f, double t) {
setAsIsResult(a);
setFromResult(f);
setToResult(t);
}
double getAsIsResult() {
return AsIsResult;
}
double getFromResult() {
return FromResult;
}
double getToResult() {
return ToResult;
}
void setAsIsResult(double asis) {
AsIsResult = asis;
}
void setFromResult(double from) {
FromResult = from;
}
void setToResult(double to) {
ToResult = to;
}
}
/*
* The PSETData class enables storage and population of the data
* required for the LocalityObjective calculate() method.
*
* The lgroupdata HashMap stores LGroupData objects
* for each LGroup in the pset.
* The results HashMap stores resultsTuple objects for each LGroup.
* The countLgroups() method populates the lgroupdata HashMap.
* The calcQ() method calculates the quotient for any given
* value of intersection and lgroup size.
* The calcResults() method populates the results HashMap.
*/
final class PSETData
{
private Resource pset;
private Map* tight - resource locality is sought *
* loose - resource locality is avoided *
* none - resource locality has no impact *