@Override public int compare(Schedulable s1, Schedulable s2) { double minShareRatio1, minShareRatio2; double tasksToWeightRatio1, tasksToWeightRatio2; int minShare1 = Math.min(s1.getMinShare(), s1.getDemand()); int minShare2 = Math.min(s2.getMinShare(), s2.getDemand()); boolean s1Needy = s1.getRunningTasks() < minShare1; boolean s2Needy = s2.getRunningTasks() < minShare2; minShareRatio1 = s1.getRunningTasks() / Math.max(minShare1, 1.0); minShareRatio2 = s2.getRunningTasks() / Math.max(minShare2, 1.0); tasksToWeightRatio1 = s1.getRunningTasks() / s1.getWeight(); tasksToWeightRatio2 = s2.getRunningTasks() / s2.getWeight(); int res = 0; if (s1Needy && !s2Needy) res = -1; else if (s2Needy && !s1Needy) res = 1; else if (s1Needy && s2Needy) res = (int) Math.signum(minShareRatio1 - minShareRatio2); else // Neither schedulable is needy res = (int) Math.signum(tasksToWeightRatio1 - tasksToWeightRatio2); if (res == 0) { // Jobs are tied in fairness ratio. Break the tie by submit time and job // name to get a deterministic ordering, which is useful for unit tests. res = (int) Math.signum(s1.getStartTime() - s2.getStartTime()); if (res == 0) res = s1.getName().compareTo(s2.getName()); } return res; }
/** * Given a set of Schedulables and a number of slots, compute their weighted fair shares. The min * shares and demands of the Schedulables are assumed to be set beforehand. We compute the fairest * possible allocation of shares to the Schedulables that respects their min shares and demands. * * <p>To understand what this method does, we must first define what weighted fair sharing means * in the presence of minimum shares and demands. If there were no minimum shares and every * Schedulable had an infinite demand (i.e. could launch infinitely many tasks), then weighted * fair sharing would be achieved if the ratio of slotsAssigned / weight was equal for each * Schedulable and all slots were assigned. Minimum shares and demands add two further twists: - * Some Schedulables may not have enough tasks to fill all their share. - Some Schedulables may * have a min share higher than their assigned share. * * <p>To deal with these possibilities, we define an assignment of slots as being fair if there * exists a ratio R such that: - Schedulables S where S.demand < R * S.weight are assigned share * S.demand - Schedulables S where S.minShare > R * S.weight are given share S.minShare - All * other Schedulables S are assigned share R * S.weight - The sum of all the shares is totalSlots. * * <p>We call R the weight-to-slots ratio because it converts a Schedulable's weight to the number * of slots it is assigned. * * <p>We compute a fair allocation by finding a suitable weight-to-slot ratio R. To do this, we * use binary search. Given a ratio R, we compute the number of slots that would be used in total * with this ratio (the sum of the shares computed using the conditions above). If this number of * slots is less than totalSlots, then R is too small and more slots could be assigned. If the * number of slots is more than totalSlots, then R is too large. * * <p>We begin the binary search with a lower bound on R of 0 (which means that all Schedulables * are only given their minShare) and an upper bound computed to be large enough that too many * slots are given (by doubling R until we either use more than totalSlots slots or we fulfill all * jobs' demands). The helper method slotsUsedWithWeightToSlotRatio computes the total number of * slots used with a given value of R. * * <p>The running time of this algorithm is linear in the number of Schedulables, because * slotsUsedWithWeightToSlotRatio is linear-time and the number of iterations of binary search is * a constant (dependent on desired precision). */ public static void computeFairShares( Collection<? extends Schedulable> schedulables, double totalSlots) { // Find an upper bound on R that we can use in our binary search. We start // at R = 1 and double it until we have either used totalSlots slots or we // have met all Schedulables' demands (if total demand < totalSlots). double totalDemand = 0; for (Schedulable sched : schedulables) { totalDemand += sched.getDemand(); } double cap = Math.min(totalDemand, totalSlots); double rMax = 1.0; while (slotsUsedWithWeightToSlotRatio(rMax, schedulables) < cap) { rMax *= 2.0; } // Perform the binary search for up to COMPUTE_FAIR_SHARES_ITERATIONS steps double left = 0; double right = rMax; for (int i = 0; i < COMPUTE_FAIR_SHARES_ITERATIONS; i++) { double mid = (left + right) / 2.0; if (slotsUsedWithWeightToSlotRatio(mid, schedulables) < cap) { left = mid; } else { right = mid; } } // Set the fair shares based on the value of R we've converged to for (Schedulable sched : schedulables) { sched.setFairShare(computeShare(sched, right)); } }
/** * Compute the number of slots assigned to a Schedulable given a particular weight-to-slot ratio * w2sRatio, for use in computeFairShares as described in #{@link * SchedulingAlgorithms#computeFairShares(Collection, double)}. */ private static double computeShare(Schedulable sched, double w2sRatio) { double share = sched.getWeight() * w2sRatio; share = Math.max(share, sched.getMinShare()); share = Math.min(share, sched.getDemand()); return share; }