xref: /illumos-gate/usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Objective.java (revision 9525b14bcdeb5b5f6f95ab27c2f48f18bd2ec829)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * ident	"%Z%%M%	%I%	%E% SMI"
27  */
28 
29 package com.sun.solaris.domain.pools;
30 
31 import java.io.FileInputStream;
32 import java.io.IOException;
33 import java.lang.reflect.Field;
34 import java.text.DecimalFormat;
35 import java.util.*;
36 import java.util.logging.*;
37 
38 import com.sun.solaris.service.logging.Severity;
39 import com.sun.solaris.service.locality.*;
40 import com.sun.solaris.service.pools.*;
41 
42 
43 /**
44  * An objective interface. All classes which wish to contribute to the
45  * Objective Function (OF hence) calculation must implement this
46  * interface. This interface defines a strategy which can be used to
47  * make a contribution to the Objective Function calculation.
48  *
49  * The OF calculation (which is implemented by <code>Poold</code>)
50  * consists of determining all possible resource moves across all
51  * resources. Once all moves are known, all registered objectives
52  * (i.e. instances of this interface) are called and asked to value
53  * the move.
54  *
55  * Note, the output of this method is constrained to be between -1 and
56  * 1, representing minimum and maximum desirability of this move in
57  * terms of this objective. This is enforced by <code>Poold</code> and
58  * an <code>IllegalOFValueException</code> will be thrown if this
59  * constraint is broken.
60  */
61 interface Objective
62 {
63 	/**
64 	 * Return the contribution of this objective. The contribution
65 	 * is constrainted to be a value between -1 and +1 to ensure
66 	 * that no objective can make a disproportionate contribution
67 	 * to the total result.
68 	 *
69 	 * The more desirable this move appears in terms of this
70 	 * objective, the closer to +1 will be the value. A value of 0
71 	 * indicates that the move is neutral in terms of the
72 	 * objective. A negative value indicates that the move is
73 	 * undesirable.
74 	 *
75 	 * @param conf The configuration which is being examined
76 	 * @param move The move under consideration
77 	 * @param elem The element to which the objective applies
78 	 *
79 	 * @throws PoolsException if there is an error manipulating
80 	 * the configuration
81 	 */
82 	public double calculate(Configuration conf, Move move, Element elem)
83 	    throws PoolsException;
84 
85 	/**
86 	 * Set the objective's expression to the supplied parameter.
87 	 *
88 	 * @param exp An expression for this objective.
89 	 */
90 	public void setExpression(Expression exp);
91 
92 	/**
93 	 * Get the objective's expression.
94 	 */
95 	public Expression getExpression();
96 }
97 
98 /**
99  * This interface must be implemented by all Objectives which are
100  * workload dependent. The examine method is used by a Solver to
101  * determine if the objective is still being satisfied.
102  */
103 interface WorkloadDependentObjective extends Objective
104 {
105 	/**
106 	 * This method returns true if the Objective is no longer
107 	 * satisfied. If the objective is still satisfied, then return
108 	 * false.
109 	 *
110 	 * @param conf The configuration to be examined
111 	 * @param solver The solving interface used to get utilization
112 	 * information
113 	 * @param elem The element to which the objective belongs
114 	 *
115 	 * @throws PoolsException if there is an error examining the
116 	 * pool configuration
117 	 * @throws StaleMonitorException if there is an error accessing
118 	 * the element's ResourceMonitor
119 	 */
120 	public boolean examine(Configuration conf, Solver solver,
121 	    Element elem) throws PoolsException, StaleMonitorException;
122 }
123 
124 /**
125  * This class provides a skeletal implementation of the
126  * <code>Objective</code> interface to minimize the effort required
127  * to implement this interface.
128  *
129  * To implement an objective, the programmer need only to extend this
130  * class and add the name of the class into the appropriate element
131  * objectives property in the <code>poold.properties</code> file.
132  */
133 abstract class AbstractObjective implements Objective
134 {
135 	abstract public double calculate(Configuration conf, Move move,
136 	    Element elem) throws PoolsException;
137 
138 	/**
139 	 * The objectives which are recognized by this class
140 	 */
141 	private static Map objectives;
142 
143 	/**
144 	 * The expression associated with this objective
145 	 */
146 	private Expression exp;
147 
148 	/**
149 	 * Set the objective's expression to the supplied parameter.
150 	 *
151 	 * @param exp An expression for this objective.
152 	 */
153 	public void setExpression(Expression exp)
154 	{
155 		this.exp = exp;
156 	}
157 
158 	/**
159 	 * Get the objective's expression.
160 	 */
161 	public Expression getExpression()
162 	{
163 		return (exp);
164 	}
165 
166 	/**
167 	 * A factory method which returns a created objective which is
168 	 * associated with the supplied expression. The type and the
169 	 * expression are used to identify valid types of objectives
170 	 * to which this expression may be applied. If an acceptable
171 	 * objective cannot be found for the supplied type, then an
172 	 * <code>IllegalArgumentException</code> will be thrown.
173 	 *
174 	 * @param type The element type for which an objective must be
175 	 * found
176 	 * @param exp The expression which will be associated with the
177 	 * objective
178 	 *
179 	 * @throws IllegalArgumentExcetion if the supplied expression
180 	 * cannot be associated with an objective of the supplied type
181 	 */
182 	public static Objective getInstance(String type, Expression exp)
183 	    throws IllegalArgumentException
184 	{
185 		Objective ret = null;
186 		Map typeObjs = null;
187 
188 		initMapIfNecessary();
189 		typeObjs = (Map)objectives.get(type);
190 		if (typeObjs != null) {
191 			Class objClass = (Class)typeObjs.get(exp.getName());
192 			if (objClass != null) {
193 				try {
194 					ret = (Objective) objClass.
195 					    newInstance();
196 				} catch (Exception e) {
197 					Poold.utility.die(Poold.OPT_LOG, e,
198 					    true);
199 				}
200 				ret.setExpression(exp);
201 			}
202 		}
203 		if (ret == null)
204 			throw new IllegalArgumentException(
205 			    "unrecognized objective name for " + type + ": " +
206 			    exp.toString());
207 		return (ret);
208 	}
209 
210 	/**
211 	 * Return a string representation of this objective.
212 	 */
213 	public String toString()
214 	{
215 		return (exp.toString());
216 	}
217 
218 	/**
219 	 * Initialize the implementation map the first time it's
220 	 * called.
221 	 */
222 	private static void initMapIfNecessary()
223 	{
224 		/*
225 		 * Setup the objectives map for the known classes
226 		 */
227 		if (objectives == null) {
228 			objectives = new HashMap();
229 			Properties props = new Properties();
230 			try {
231 				props.load(
232 				    new FileInputStream(
233 				    Poold.POOLD_PROPERTIES_PATH));
234 			} catch (IOException ioe) {
235 				Poold.utility.die(Poold.CONF_LOG, ioe);
236 			}
237 			registerObjectives(props, objectives, "system");
238 			registerObjectives(props, objectives, "pset");
239 		}
240 	}
241 
242 	/**
243 	 * Add the objectives contained in the supplied properties to
244 	 * the set of valid objectives. The objectives are updated
245 	 * with objectives of the supplied type contained in the
246 	 * properties.
247 	 *
248 	 * @param props The properties containing the objectives
249 	 * @param objectives The objectives to be updated
250 	 * @param type The type of objectives to be added
251 	 */
252 	private static void registerObjectives(Properties props,
253 	    Map objectives, String type)
254 	{
255 		Map typeObjs = new HashMap();
256 		String objs = props.getProperty(type + ".objectives");
257 		String objNames[] = objs.split(",");
258 		for (int i = 0; i < objNames.length; i++) {
259 			String objName = objNames[i].trim();
260 			try {
261 				Class clazz = Class.forName(objName);
262 				Field field = clazz.getDeclaredField("name");
263 				String key = (String) field.get(null);
264 				typeObjs.put(key, clazz);
265 			} catch (ClassNotFoundException cnfe) {
266 				Poold.utility.die(Poold.CONF_LOG, cnfe);
267 			} catch (NoSuchFieldException nsfe) {
268 				Poold.utility.die(Poold.CONF_LOG, nsfe);
269 			} catch (IllegalAccessException iae) {
270 				Poold.utility.die(Poold.CONF_LOG, iae);
271 			}
272 		}
273 		objectives.put(type, typeObjs);
274 	}
275 
276 	/**
277 	 * Indicates whether some other Objective is "equal to this
278 	 * one.
279 	 * @param o the reference object with which to compare.
280 	 * @return <code>true</code> if this object is the same as the
281 	 * o argument; <code>false</code> otherwise.
282 	 * @see	#hashCode()
283 	 */
284 	public boolean equals(Object o)
285 	{
286 		if (o == this)
287 			return (true);
288 		if (!(o instanceof Objective))
289 			return (false);
290 		Objective other = (Objective) o;
291 
292 		return (getExpression().equals(other.getExpression()));
293 	}
294 
295 	/**
296 	 * Returns a hash code value for the object. This method is
297 	 * supported for the benefit of hashtables such as those provided by
298 	 * <code>java.util.Hashtable</code>.
299 	 *
300 	 * @return a hash code value for this object.
301 	 * @see	#equals(java.lang.Object)
302 	 * @see	java.util.Hashtable
303 	 */
304 	public int hashCode()
305 	{
306 		return (getExpression().hashCode());
307 	}
308 }
309 
310 
311 /**
312  * The <code>WeightedLoadObjective</code> class implements a Weighted
313  * Load Objective for <code>Poold</code>.
314  *
315  * The goal is to allocate more resources to those resource partitions
316  * which are heavily loaded. The weighting is determined from the
317  * objective importance and the pool.importance.
318  */
319 final class WeightedLoadObjective extends AbstractObjective
320     implements WorkloadDependentObjective
321 {
322 	/**
323 	 * The name of the class.
324 	 */
325 	static final String name = "wt-load";
326 
327 	/**
328 	 * The map of calculations made during examination.
329 	 */
330 	Map calcMap;
331 
332 	/**
333 	 * Determine whether an objective is satisfied. If the
334 	 * objective is still satisfied, return false; otherwise
335 	 * return true.
336 	 *
337 	 * This objective examination determines if all resource sets
338 	 * are allocated the share of resources that their utilization
339 	 * would indicate they should be. This attempts to ensure that
340 	 * highly utilized resource sets recieve the greater
341 	 * proportion of available resources.
342 	 *
343 	 * @param conf The configuration to be examined
344 	 * @param solver The solving interface used to get utilization
345 	 * information
346 	 * @param elem The element to which the objective belongs
347 	 *
348 	 * @throws PoolsException if there is an error examining the
349 	 * pool configuration
350 	 * @throws StaleMonitorException if there is an error accessing
351 	 * the element's ResourceMonitor
352 	 */
353 	public boolean examine(Configuration conf, Solver solver,
354 	    Element elem) throws PoolsException, StaleMonitorException
355 	{
356 		Monitor mon = solver.getMonitor();
357 		Value val = new Value("type", "pset");
358 		List valueList = new LinkedList();
359 		calcMap = new HashMap();
360 		valueList.add(val);
361 
362 		List resList = conf.getResources(valueList);
363 		val.close();
364 		Iterator itRes = resList.iterator();
365 
366 		Calculation.totalUtil = 0;
367 		Calculation.resQ = 0;
368 
369 		while (itRes.hasNext()) {
370 			Resource res = (Resource) itRes.next();
371 			List CPUs = res.getComponents(null);
372 
373 			try {
374 				Calculation calc = new Calculation(res, CPUs,
375 				    mon.getUtilization(res),
376 				    res.getLongProperty("pset.min"),
377 				    res.getLongProperty("pset.max"));
378 				calcMap.put(res, calc);
379 			} catch (StaleMonitorException sme) {
380 				Poold.MON_LOG.log(Severity.INFO,
381 				    res.toString() +
382 				    " not participating in " + toString() +
383 				    " calculatation as it has no " +
384 				    "available statistics.");
385 			}
386 		}
387 		Iterator itCalc = calcMap.values().iterator();
388 		while (itCalc.hasNext()) {
389 			Calculation calc = (Calculation) itCalc.next();
390 			if (calc.getShare() != calc.comp.size() &&
391 			    calc.getShare() >= calc.min) {
392 				Poold.MON_LOG.log(Severity.INFO,
393 				    elem.toString() +
394 				    " utilization objective not satisfied " +
395 				    toString() + " with desired share " +
396 				    calc.getShare() + " and actual share " +
397 				    calc.comp.size());
398 				return (true);
399 			}
400 		}
401 		return (false);
402 	}
403 
404 	/**
405 	 * Holds data about weighted load calculations. This class is
406 	 * basically a structure which holds information specific to a
407 	 * weighted-load calculation
408 	 */
409 	static class Calculation {
410 		/**
411 		 * The resource on which this calculation is based.
412 		 */
413 		Resource res;
414 
415 		/**
416 		 * The list of component resources held by this resource.
417 		 */
418 		List comp;
419 
420 		/**
421 		 * The utilization of this resource.
422 		 */
423 		double util;
424 
425 		/**
426 		 * The minimum value of this resource's size.
427 		 */
428 		long min;
429 
430 		/**
431 		 * The maximum value of this resource's size.
432 		 */
433 		long max;
434 
435 		/**
436 		 * The total utilization of all instances of this class.
437 		 */
438 		static double totalUtil;
439 
440 		/**
441 		 * The total quantity of resource for all instances of
442 		 * this class.
443 		 */
444 		static int resQ;
445 
446 		/**
447 		 * Constructor. The class is immutable and holds
448 		 * information specific to a set of calculations about
449 		 * load.
450 		 *
451 		 * @param res The resource set
452 		 * @param comp The resource components
453 		 * @param util The resource utilization
454 		 * @param min The minimum qty of resource for this set
455 		 * @param max The maximum qty of resource for this set
456 		 */
457 		public Calculation(Resource res, List comp, double util,
458 		    long min, long max)
459 		{
460 			this.res = res;
461 			this.comp = comp;
462 			this.min = min;
463 			this.max = max;
464 			this.util = (util / 100) * comp.size();
465 			Calculation.totalUtil += this.util;
466 			Calculation.resQ += comp.size();
467 		}
468 
469 		/**
470 		 * Return the share of the total resource for this
471 		 * resource.
472 		 */
473 		long getShare()
474 		{
475 			if (util == 0)
476 				return (0);
477 			return (Math.round((util / totalUtil) * resQ));
478 		}
479 
480 		public String toString()
481 		{
482 			StringBuffer buf = new StringBuffer();
483 			buf.append("res: " + res.toString());
484 			buf.append(" components: " + comp.toString());
485 			buf.append(" min: " + min);
486 			buf.append(" max: " + max);
487 			buf.append(" util: " + util);
488 			buf.append(" total resource: " + resQ);
489 			buf.append(" total utilization: " + totalUtil);
490 			buf.append(" share: " + getShare());
491 			return (buf.toString());
492 		}
493 	}
494 
495 	/**
496 	 * Calculates the value of a configuration in terms of this
497 	 * objective.
498 	 *
499 	 * In the examination step, calculations of each resource's
500 	 * current and desired share were made. The moves can thus be
501 	 * assessed in terms of their impact upon the desired
502 	 * share. The current difference from desired is already
503 	 * known, so each move will serve to reduce or increase that
504 	 * difference. Moves that increase the difference have a
505 	 * negative score, those that reduce it have a positive
506 	 * score. All scores are normalized to return a value between
507 	 * -1 and 1.
508 	 *
509 	 * @param conf Configuration to be scored.
510 	 * @param move Move to be scored.
511 	 * @param elem The element to which the objective applies
512 	 * @throws PoolsException If an there is an error in execution.
513 	 */
514 	public double calculate(Configuration conf, Move move, Element elem)
515 	    throws PoolsException
516 	{
517 		double ret = 0;
518 
519 		Poold.OPT_LOG.log(Severity.DEBUG,
520 		    "Calculating objective type: " + name);
521 		/*
522 		 * There shouldn't be any empty moves, but if there
523 		 * are they are rated at 0.
524 		 */
525 		if (move.getQty() == 0)
526 			return (0);
527 
528 		/*
529 		 * Find the calculations that represent the source and
530 		 * target of the move.
531 		 */
532 		Calculation src = (Calculation) calcMap.get(move.getFrom());
533 		Calculation tgt = (Calculation) calcMap.get(move.getTo());
534 
535 		/*
536 		 * Use the calculation details to determine the "gap"
537 		 * i.e. number of discrete resources (for a processor
538 		 * set these are CPUs), between the desired quantity in
539 		 * the set which the calculations represent. Do this
540 		 * both before and after the proposed move.
541 		 *
542 		 * The maximum possible improvement is equal to the
543 		 * total number of resources for each set participating
544 		 * in the calculation. Since there are two sets we
545 		 * know the maximum possible improvement is resQ * 2.
546 		 *
547 		 * Divide the aggregated change in gap across participating
548 		 * sets by the maximum possible improvement to obtain
549 		 * a value which scores the move and which is normalised
550 		 * between -1 <= ret <= 1.
551 		 */
552 		long oldGap = Math.abs(src.getShare() -
553 		    src.comp.size());
554 		long newGap = Math.abs(src.getShare() -
555 		    (src.comp.size() - move.getQty()));
556 		ret = oldGap - newGap;
557 		oldGap = Math.abs(tgt.getShare() -
558 		    tgt.comp.size());
559 		newGap = Math.abs(tgt.getShare() -
560 		    (tgt.comp.size() + move.getQty()));
561 		ret += oldGap - newGap;
562 		ret /= ((double) Calculation.resQ * 2);
563 
564 		Poold.MON_LOG.log(Severity.DEBUG, "ret: " + ret);
565 		return (ret);
566 	}
567 }
568 
569 /**
570  * A locality based objective which will assess moves in terms of
571  * their impact on the locality of the sets of resources which are
572  * impacted.
573  *
574  * The objective will assess moves with respect to the type of
575  * locality specified in the objective:
576  *
577  * <ul>
578  * <li><p>
579  * tight - resource locality is sought
580  * <li><p>
581  * loose - resource locality is avoided
582  * <li><p>
583  * none - resource locality has no impact
584  * </ul>
585  */
586 final class LocalityObjective extends AbstractObjective
587 {
588 	/**
589 	 * The name of the class.
590 	 */
591 	static final String name = "locality";
592 
593 	/**
594 	 * The locality domain used to describe locality for this
595 	 * objective.
596 	 */
597 	private LocalityDomain ldom;
598 
599 	/**
600 	 * Prepare the calculation for this objective for the resource
601 	 * to which it applies.
602 	 *
603 	 * @param ldom LocalityDomain containing these resources.
604 	 * @param res Resource to which this objective is applied.
605 	 *
606 	 * @throws PoolsException if accessing the supplied resource
607 	 * fails.
608 	 */
609 	public void prepare(LocalityDomain ldom, Resource res)
610 	    throws PoolsException
611 	{
612 		this.ldom = ldom;
613 	}
614 
615 	/**
616 	 * Calculates the value of a configuration in terms of this
617 	 * objective.
618 	 *
619 	 * Firstly check to see if it is possible to short-cut the
620 	 * calculation. If not, then start to examine the disposition
621 	 * of CPUs and locality groups in relation to the processor
622 	 * set being evaluated. The objective scores moves in terms of
623 	 * their impact upon the quotient of cpus contained in each
624 	 * locality group.
625 	 *
626 	 * @param conf Configuration to be scored.
627 	 * @param move Move to be scored.
628 	 * @param elem The element to which the objective applies
629 	 * @throws Exception If an there is an error in execution.
630 	 */
631 	public double calculate(Configuration conf, Move move, Element elem)
632 	    throws PoolsException
633 	{
634 		KVExpression kve = (KVExpression) getExpression();
635 		double ret = 0;
636 		double qA = 0;
637 		double qB = 0;
638 		Resource res = (Resource) elem;
639 		ComponentMove cm = (ComponentMove) move;
640 		Poold.MON_LOG.log(Severity.DEBUG,
641 		    "Calculating objective type: " + name + " for: " + elem);
642 
643 		/*
644 		 * If we are set to "none" then we don't care which
645 		 * configuration so just return 0
646 		 */
647 		if (kve.getValue().compareTo("none") == 0)
648 			return (ret);
649 		/*
650 		 * If the maximum latency is 0, we don't care about
651 		 * latency
652 		 */
653 		if (ldom.getMaxLatency() == 0)
654 			return (ret);
655 		/*
656 		 * If this element doesn't participate in the move, we
657 		 * should return 0.
658 		 */
659 		if (elem.equals(move.getFrom()) == false &&
660 		    elem.equals(move.getTo()) == false)
661 			return (ret);
662 
663 		/*
664 		 * Assess the effect on lgrp quotient for all
665 		 * moves. Try to maximise lgrp quotient for "tight"
666 		 * objectives and minimize if for "loose" objectives.
667 		 */
668 		/*
669 		 * All contained cpus
670 		 */
671 		List contains = res.getComponents(null);
672 		/*
673 		 * All lgrps
674 		 */
675 		Set groups = ldom.foreignGroups(new HashSet(), contains);
676 
677 		qB = calcQ(contains, groups);
678 
679 		if (elem.equals(move.getFrom()))
680 			contains.removeAll(cm.getComponents());
681 		else
682 			contains.addAll(cm.getComponents());
683 		/*
684 		 * Recalculate lgrps to take account of new components
685 		 */
686 		groups = ldom.foreignGroups(new HashSet(), contains);
687 		qA = calcQ(contains, groups);
688 
689 		ret = qA - qB;
690 
691 		if (kve.getValue().compareTo("loose") == 0)
692 			ret = 0 - ret;
693 
694 		Poold.MON_LOG.log(Severity.DEBUG, "ret: " + ret);
695 		return (ret);
696 	}
697 
698 	/**
699 	 * Calculate a quotient using the supplied list of components
700 	 * and set of locality groups. The quotient represents the
701 	 * average (as in mean) number of CPUs (from the population in
702 	 * contains) present in each of the LocalityGroups contained
703 	 * in groups.
704 	 *
705 	 * @param contains The population of Components.
706 	 * @param groups The population of LocalityGroups.
707 	 *
708 	 * @throws PoolsException if there is an error accessing the
709 	 * CPUs.
710 	 */
711 	private double calcQ(List contains, Set groups) throws PoolsException
712 	{
713 		Iterator groupIt = groups.iterator();
714 		double q = 0.0;
715 		while (groupIt.hasNext()) {
716 			LocalityGroup grp = (LocalityGroup)groupIt.next();
717 			int cpu_ids[] = grp.getCPUIDs();
718 			Iterator containsIt = contains.iterator();
719 			int intersection = 0;
720 			double total = 0;
721 			while (containsIt.hasNext()) {
722 				Component comp = (Component) containsIt.next();
723 				for (int i = 0; i < cpu_ids.length; i++) {
724 					if (cpu_ids[i] == (int) comp.
725 					    getLongProperty("cpu.sys_id")) {
726 						intersection++;
727 						break;
728 					}
729 				}
730 			}
731 			double factor = 0;
732 			for (int i = 1; i <= cpu_ids.length; i++)
733 				factor += i;
734 			factor = 1 / factor;
735 			for (int i = 1; i <= intersection; i++)
736 				q += i * factor;
737 		}
738 		q /= groups.size();
739 		return (q);
740 	}
741 }
742 
743 /**
744  * A resource set utilization based objective which will assess moves
745  * in terms of their (likely) impact on the future performance of a
746  * resource set with respect to it's specified utilization objective.
747  *
748  * The utilization objective must be specified in terms of a
749  * KVOpExpression, see the class definition for information about the
750  * form of these expressions. The objective can be examined in terms
751  * of it's compliance with the aid of a monitoring object. The actual
752  * assessment of compliance is indicated by the associated monitoring
753  * object, with this class simply acting as a co-ordinator of the
754  * relevant information.
755  */
756 final class UtilizationObjective extends AbstractObjective
757     implements WorkloadDependentObjective
758 {
759 	/**
760 	 * The name of the class.
761 	 */
762 	static final String name = "utilization";
763 
764 	/**
765 	 * Short run detection.
766 	 */
767 	private List zoneList = new LinkedList();
768 
769 	/**
770 	 * Format for printing utilization.
771 	 */
772 	private static final DecimalFormat uf = new DecimalFormat("0.00");
773 
774 	/**
775 	 * Solver used to calculate delta, i.e. gap, between target and
776 	 * actual utilization values.
777 	 */
778 	private Solver gapSolver;
779 
780 	/**
781 	 * Determine whether an objective is satisfied. If the
782 	 * objective is still satisfied, return false; otherwise
783 	 * return true.
784 	 *
785 	 * The assessment of control is made by the monitoring class
786 	 * using the supplied Expression and resource.
787 	 *
788 	 * @param conf The configuration to be examined
789 	 * @param solver The solving interface used to get utilization
790 	 * information
791 	 * @param elem The element to which the objective belongs
792 	 *
793 	 * @throws PoolsException if there is an error examining the
794 	 * pool configuration
795 	 * @throws StaleMonitorException if there is an error accessing
796 	 * the element's ResourceMonitor
797 	 */
798 	public boolean examine(Configuration conf, Solver solver,
799 	    Element elem) throws PoolsException, StaleMonitorException
800 	{
801 		KVOpExpression kve = (KVOpExpression) getExpression();
802 		ResourceMonitor mon;
803 
804 		/*
805 		 * If there is no resource monitor, then we cannot
806 		 * make an assessment of the objective's achievability.
807 		 * Log a message to make clear that this objective is
808 		 * not being assessed and then indicate that
809 		 * the objective has been achieved.
810 		 */
811 		try {
812 			mon = solver.getMonitor().get((Resource)elem);
813 		} catch (StaleMonitorException sme) {
814 			Poold.MON_LOG.log(Severity.INFO,
815 			    elem.toString() +
816 			    " utilization objective not measured " +
817 			    toString() + " as there are no available " +
818 			    "statistics.");
819 			return (false);
820 		}
821 		gapSolver = solver;
822 
823 		double val = solver.getMonitor().getUtilization((Resource)elem);
824 
825 		StatisticList sl = (StatisticList) mon.get("utilization");
826 		int zone = sl.getZone(kve, val);
827 
828 		if (zoneList.size() == 9) {
829 			zoneList.remove(0);
830 		}
831 		zoneList.add(new Integer(sl.getZoneMean(val)));
832 
833 		/*
834 		 * Evaluate whether or not this objective is under
835 		 * control.
836 		 */
837 		if ((zone & StatisticOperations.ZONEZ) ==
838 		    StatisticOperations.ZONEZ) {
839 			/*
840 			 * If the objective is GT or LT, then don't
841 			 * return true as long as the objective is
842 			 * satisfied.
843 			 */
844 			if (kve.getOp() == KVOpExpression.LT &&
845 			    (zone & StatisticOperations.ZONET) ==
846 			    StatisticOperations.ZONELT)
847 				return (false);
848 
849 			if (kve.getOp() == KVOpExpression.GT &&
850 			    (zone & StatisticOperations.ZONET) ==
851 			    StatisticOperations.ZONEGT)
852 				return (false);
853 			Poold.MON_LOG.log(Severity.INFO,
854 			    elem.toString() +
855 			    " utilization objective not satisfied " +
856 			    toString() + " with utilization " + uf.format(val) +
857 			    " (control zone bounds exceeded)");
858 			return (true);
859 		}
860 		/*
861 		 * Check if our statistics need to be recalculated.
862 		 */
863 		checkShort(mon, elem, val);
864 		return (false);
865 	}
866 
867 	/**
868 	 * Calculates the value of a configuration in terms of this
869 	 * objective.
870 	 *
871 	 * Every set must be classified with a control zone when this
872 	 * function is called. The move can be assessed in terms of
873 	 * the control violation type. zone violations which are minor
874 	 * are offered a lower contribution than more significant
875 	 * violations.
876 	 *
877 	 * @param conf Configuration to be scored.
878 	 * @param move Move to be scored.
879 	 * @param elem The element to which the objective applies
880 	 * @throws Exception If an there is an error in execution.
881 	 */
882 	public double calculate(Configuration conf, Move move, Element elem)
883 	    throws PoolsException
884 	{
885 		KVOpExpression kve = (KVOpExpression) getExpression();
886 		double ret;
887 
888 		/*
889 		 * If the move is from the examined element, then
890 		 * check to see if the recipient has any
891 		 * objectives. If not, score the move poorly since we
892 		 * should never want to transfer resources to a
893 		 * recipient with no objectives. If there are
894 		 * objectives, then return the delta between target
895 		 * performance and actual performance for this
896 		 * element.
897 		 *
898 		 * If the move is to the examined element, then check
899 		 * to see if the donor has any objectives. If not,
900 		 * score the move highly, since we want to favour
901 		 * those resources with objectives. If there are
902 		 * objectives, return the delta between actual and
903 		 * target performance.
904 		 *
905 		 * If the element is neither the recipient or the
906 		 * donor of this proposed move, then score the move
907 		 * neutrally as 0.
908 		 */
909 		try {
910 			double val, gap;
911 			StatisticList sl;
912 
913 			if (elem.equals(move.getFrom())) {
914 				val = gapSolver.getMonitor().
915 				    getUtilization(move.getFrom());
916 				sl = (StatisticList) gapSolver.getMonitor().
917 				    get(move.getFrom()).get("utilization");
918 				gap = sl.getGap(kve, val) / 100;
919 
920 				if (gapSolver.getObjectives(move.getTo()) ==
921 				    null) {
922 					/*
923 					 * Moving to a resource with
924 					 * no objectives should always
925 					 * be viewed unfavourably. The
926 					 * degree of favourability is
927 					 * thus bound between 0 and
928 					 * -1. If the source gap is
929 					 * negative, then subtract it
930 					 * from -1 to get the
931 					 * score. If positive,
932 					 * just return -1.
933 					 */
934 					    if (gap < 0) {
935 						    ret = -1 - gap;
936 					    } else {
937 						    ret = -1;
938 					    }
939 				} else {
940 					ret = 0 - gap;
941 				}
942 			} else if (elem.equals(move.getTo())) {
943 				val = gapSolver.getMonitor().
944 				    getUtilization(move.getTo());
945 				sl = (StatisticList) gapSolver.getMonitor().
946 				    get(move.getTo()).get("utilization");
947 				gap = sl.getGap(kve, val) / 100;
948 
949 				if (gapSolver.getObjectives(move.getFrom()) ==
950 				    null) {
951 					/*
952 					 * Moving from a resource with
953 					 * no objectives should always
954 					 * be viewed favourably. The
955 					 * degree of favourability is
956 					 * thus bound between 0 and
957 					 * 1. If the destination gap
958 					 * is negative, then add to 1
959 					 * to get the score. If
960 					 * positive, just return 1.
961 					 */
962 					if (gap < 0) {
963 						ret = 0 - gap;
964 					} else {
965 						ret = 1;
966 					}
967 				} else {
968 					ret = 0 + gap;
969 				}
970 			} else {
971 				ret = 0;
972 			}
973 		} catch (StaleMonitorException sme) {
974 			/*
975 			 * We should always find a monitor,
976 			 * but if we can't then just assume
977 			 * this is a neutral move and return
978 			 * 0.
979 			 */
980 			ret = 0;
981 		}
982 		Poold.MON_LOG.log(Severity.DEBUG, "ret: " + ret);
983 		return (ret);
984 	}
985 
986 	/**
987 	 * Check whether or not a set's statistics are still useful
988 	 * for making decision..
989 	 *
990 	 * Each set is controlled in terms of the zones of control
991 	 * based in terms of standard deviations from a mean. If the
992 	 * utilization of the set is not fluctuating normally around a
993 	 * mean, these checks will cause the accumulated statistics to
994 	 * be discarded and control suspended until a new sufficient
995 	 * set of data is accumulated.
996 	 *
997 	 * @param mon Resource monitor to examine.
998 	 * @param elem Element to which the resource monitor belongs.
999 	 * @param val Latest monitored value.
1000 	 */
1001 	private void checkShort(ResourceMonitor mon, Element elem, double val)
1002 	{
1003 		boolean checkOne = true;
1004 		int checkOnePos = 0;
1005 		boolean doCheckOne = false;
1006 
1007 		Iterator itZones = zoneList.iterator();
1008 		while (itZones.hasNext()) {
1009 			int zone = ((Integer) itZones.next()).intValue();
1010 			if (doCheckOne) {
1011 				if (checkOne) {
1012 					if ((zone & StatisticOperations.ZONET)
1013 					    != checkOnePos) {
1014 						checkOne = false;
1015 					}
1016 				}
1017 			} else {
1018 				if (zoneList.size() >= 9) {
1019 					checkOnePos = zone &
1020 					    StatisticOperations.ZONET;
1021 					doCheckOne = true;
1022 				}
1023 			}
1024 		}
1025 		if (zoneList.size() >= 9 && checkOne) {
1026 			Poold.MON_LOG.log(Severity.INFO,
1027 			    elem.toString() +
1028 			    " utilization objective statistics reinitialized " +
1029 			    toString() + " with utilization " + uf.format(val) +
1030 			    " (nine points on same side of mean)");
1031 			mon.resetData("utilization");
1032 			zoneList.clear();
1033 		}
1034 	}
1035 }
1036