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