/usr/bin/git diff --no-prefix HEAD diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/DominantResourceFairnessPolicy.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/DominantResourceFairnessPolicy.java index ec995b7c9ee..d32037cc6e8 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/DominantResourceFairnessPolicy.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/DominantResourceFairnessPolicy.java @@ -111,8 +111,8 @@ public void initialize(FSContext fsContext) { /** * This class compares two {@link Schedulable} instances according to the - * DRF policy. If neither instance is below min share, weighted fair shares - * are compared. + * DRF policy. If neither instance is below min share, approximate fair share + * ratios are compared. */ public static class DominantResourceFairnessComparator implements Comparator { @@ -131,16 +131,18 @@ public int compare(Schedulable s1, Schedulable s2) { Resource minShare2 = s2.getMinShare(); Resource clusterCapacity = fsContext.getClusterResource(); - // These arrays hold the fair and min shares for each resource type - // shares[0][x] are the fair shares, and shares[1][x] are the min shares. - float[][] shares1 = new float[info.length][2]; - float[][] shares2 = new float[info.length][2]; + // These arrays hold the usage, fair, and min share ratios for each + // resource type. ratios[0][x] are the usage ratios, ratios[1][x] are + // the fair share ratios, and ratios[2][x] are the min share ratios. + float[][] ratios1 = new float[info.length][3]; + float[][] ratios2 = new float[info.length][3]; - // Calculate shares of the cluster for each resource both schedulables. - int dominant1 = calculateShares(usage1, clusterCapacity, shares1, - s1.getWeights()); - int dominant2 = calculateShares(usage2, clusterCapacity, shares2, - s2.getWeights()); + // Calculate cluster shares and approximate fair shares for each + // resource type of both schedulables. + int dominant1 = calculateClusterAndFairRatios(usage1, clusterCapacity, + ratios1, s1.getWeights()); + int dominant2 = calculateClusterAndFairRatios(usage2, clusterCapacity, + ratios2, s2.getWeights()); // A queue is needy for its min share if its dominant resource // (with respect to the cluster capacity) is below its configured min @@ -155,20 +157,20 @@ public int compare(Schedulable s1, Schedulable s2) { int res = 0; if (!s2Needy && !s1Needy) { - // Sort shares by cluster share and compare them by weight - sortShares(shares1, shares2); - res = compareShares(shares1, shares2, 0); + // Sort shares by usage ratio and compare them by weight + sortRatios(ratios1, ratios2); + res = compareRatios(ratios1, ratios2, 1); } else if (s1Needy && !s2Needy) { res = -1; } else if (s2Needy && !s1Needy) { res = 1; } else { // both are needy below min share - // Calculate the min shares, then sort by cluster share, and compare - // by min share - calculateMinShares(usage1, minShare1, shares1); - calculateMinShares(usage2, minShare2, shares2); - sortShares(shares1, shares2); - res = compareShares(shares1, shares2, 1); + // Calculate the min share ratios, then sort by usage ratio, and compare + // by min share ratio + calculateMinShareRatios(usage1, minShare1, ratios1); + calculateMinShareRatios(usage2, minShare2, ratios2); + sortRatios(ratios1, ratios2); + res = compareRatios(ratios1, ratios2, 2); } if (res == 0) { @@ -185,105 +187,125 @@ public int compare(Schedulable s1, Schedulable s2) { } /** - * Sort both shares arrays according to the cluster shares (the first index - * of the inner arrays, e.g. {@code shares1[0][x]}). + * Sort both ratios arrays according to the usage ratios (the + * first index of the inner arrays, e.g. {@code ratios1[x][0]}). * - * @param shares1 the first shares array - * @param shares2 the second shares array + * @param ratios1 the first ratios array + * @param ratios2 the second ratios array */ @VisibleForTesting - void sortShares(float[][] shares1, float[][]shares2) { + void sortRatios(float[][] ratios1, float[][]ratios2) { // sort order descending by resource share - Arrays.sort(shares1, (float[] o1, float[] o2) -> + Arrays.sort(ratios1, (float[] o1, float[] o2) -> (int) Math.signum(o2[0] - o1[0])); - Arrays.sort(shares2, (float[] o1, float[] o2) -> + Arrays.sort(ratios2, (float[] o1, float[] o2) -> (int) Math.signum(o2[0] - o1[0])); } /** - * Calculate a resource's share of the cluster. The shares array will be - * populated with the fraction of the cluster that {@code resource} - * represents, for each resource type. If the cluster's resources are 100MB - * and 10 vcores, and the usage ({@code resource}) is 10 MB and 5 CPU, the - * shares will be 0.1 and 0.5. + * Calculate a resource's usage ratio and approximate fair share ratio. + * The {@code shares} array will be populated with both the usage ratio + * and the approximate fair share ratio for each resource type. The usage + * ratio is calculated as {@code resource} divided by {@code cluster}. + * The approximate fair share ratio is calculated as the usage ratio + * divided by {@code weight}. If the cluster's resources are 100MB and + * 10 vcores, and the usage ({@code resource}) is 10 MB and 5 CPU, the + * usage ratios will be 0.1 and 0.5. If the weights are 2, the fair + * share ratios will be 0.05 and 0.25. + * + * The approximate fair share ratio is the usage divided by the + * approximate fair share, i.e. the cluster resources times the weight. + * The approximate fair share is an acceptable proxy for the fair share + * because when comparing resources, the resource with the higher weight + * will be assigned by the scheduler a proportionally higher fair share. * - * The shares array must be n x 2, where n is the number of - * resource types. Only the first index of the inner arrays in the shares - * array will be used, e.g. {@code shares[x][0]}. + * The {@code shares} array must be at least n x 2, where n + * is the number of resource types. Only the first and second indices of + * the inner arrays in the {@code shares} array will be used, e.g. + * {@code shares[x][0]} and {@code shares[x][1]}. * - * @param resource the resource for which to calculate min shares - * @param cluster the cluster resources - * @param shares the shares array + * The return value will be the index of the dominant resource type in the + * {@code shares} array. The dominant resource is the resource type for + * which {@code resource} has the largest usage ratio. + * + * @param resource the resource for which to calculate ratios + * @param cluster the total cluster resources + * @param ratios the shares array to populate * @param weights the resource weights - * @return the index of the largest share + * @return the index of the resource type with the largest cluster share */ @VisibleForTesting - int calculateShares(Resource resource, Resource cluster, - float[][] shares, ResourceWeights weights) { + int calculateClusterAndFairRatios(Resource resource, Resource cluster, + float[][] ratios, ResourceWeights weights) { ResourceInformation[] resourceInfo = resource.getResources(); ResourceInformation[] clusterInfo = cluster.getResources(); int max = 0; for (int i = 0; i < clusterInfo.length; i++) { - shares[i][0] = resourceInfo[i].getValue() / - (clusterInfo[i].getValue() * weights.getWeight(i)); + // First calculate the cluster share + ratios[i][0] = resourceInfo[i].getValue() / clusterInfo[i].getValue(); - if (shares[i][0] > shares[max][0]) { + // Use the cluster share to find the dominant resource + if (ratios[i][0] > ratios[max][0]) { max = i; } + + // Now divide by the weight to get the approximate fair share. + // It's OK if the weight is zero, because the floating point division + // will yield Infinity, i.e. this Schedulable will lose out to any + // other Schedulable with non-zero weight. + ratios[i][1] = ratios[i][0] / weights.getWeight(i); } return max; } /** - * Calculate a resource's min shares. The shares array will be populated - * with the ratio of usage compared to min share for each resource. - * If the min shares are 5 MB and 10 vcores, and the usage + * Calculate a resource's min share ratios. The {@code ratios} array will be + * populated with the {@code resource} divided by {@code minShare} for each + * resource type. If the min shares are 5 MB and 10 vcores, and the usage * ({@code resource}) is 10 MB and 5 CPU, the ratios will be 2 and 0.5. * - * The shares array must be n x 2, where n is the number of - * resource types. Only the second index of the inner arrays in the shares - * array will be used, e.g. {@code shares[x][1]}. + * The {@code ratios} array must be n x 3, where n is the + * number of resource types. Only the third index of the inner arrays in + * the {@code ratios} array will be used, e.g. {@code ratios[x][2]}. * * @param resource the resource for which to calculate min shares * @param minShare the min share - * @param shares the shares array - * @param weights the resource weights + * @param ratios the shares array to populate */ @VisibleForTesting - void calculateMinShares(Resource resource, Resource minShare, - float[][] shares) { + void calculateMinShareRatios(Resource resource, Resource minShare, + float[][] ratios) { ResourceInformation[] resourceInfo = resource.getResources(); ResourceInformation[] minShareInfo = minShare.getResources(); for (int i = 0; i < minShareInfo.length; i++) { - shares[i][1] = resourceInfo[i].getValue() / minShareInfo[i].getValue(); + ratios[i][2] = resourceInfo[i].getValue() / minShareInfo[i].getValue(); } } /** - * Compare the two shares arrays and return -1, 0, or 1 if the first array + * Compare the two ratios arrays and return -1, 0, or 1 if the first array * is less than, equal to, or greater than the second array, respectively. - * If {@code cluster} is true, the comparison is done based on the fair - * shares (stored in index 0 of the inner arrays in the shares array). If - * false, the comparison is done based on the min shares (stored in index 1 - * of the inner arrays in the shares arrays). The shares arrays are assumed - * to be sorted in descending order. + * The {@code index} parameter determines which index of the inner arrays + * will be used for the comparisons. 0 is for usage ratios, 1 is for + * fair share ratios, and 2 is for the min share ratios. The ratios arrays + * are assumed to be sorted in descending order by usage ratio. * - * @param shares1 the first shares array - * @param shares2 the second shares array + * @param ratios1 the first shares array + * @param ratios2 the second shares array * @param index the outer index of the shares arrays to compare. 0 is for * fair shares, and 1 is for min shares * @return -1, 0, or 1 if the first array is less than, equal to, or * greater than the second array, respectively */ @VisibleForTesting - int compareShares(float[][] shares1, float[][] shares2, int index) { + int compareRatios(float[][] ratios1, float[][] ratios2, int index) { int ret = 0; - for (int i = 0; i < shares1.length; i++) { - ret = (int) Math.signum(shares1[i][index] - shares2[i][index]); + for (int i = 0; i < ratios1.length; i++) { + ret = (int) Math.signum(ratios1[i][index] - ratios2[i][index]); if (ret != 0) { break; diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/TestDominantResourceFairnessPolicy.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/TestDominantResourceFairnessPolicy.java index 152d02bbbc2..d4d3bfb7853 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/TestDominantResourceFairnessPolicy.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/TestDominantResourceFairnessPolicy.java @@ -200,22 +200,22 @@ public void testUnevenWeightsDifferentDominantResource() { @Test public void testSortShares() { - float[][] shares1 = {{0.3f, 2.0f}, {0.2f, 1.0f}, {0.4f, 0.1f}}; - float[][] shares2 = {{0.2f, 9.0f}, {0.3f, 2.0f}, {0.25f, 0.1f}}; + float[][] ratios1 = {{0.3f, 2.0f}, {0.2f, 1.0f}, {0.4f, 0.1f}}; + float[][] ratios2 = {{0.2f, 9.0f}, {0.3f, 2.0f}, {0.25f, 0.1f}}; float[][] expected1 = {{0.4f, 0.1f}, {0.3f, 2.0f}, {0.2f, 1.0f}}; float[][] expected2 = {{0.3f, 2.0f}, {0.25f, 0.1f}, {0.2f, 9.0f}}; DominantResourceFairnessComparator comparator = new DominantResourceFairnessComparator(); - comparator.sortShares(shares1, shares2); + comparator.sortRatios(ratios1, ratios2); - for (int i = 0; i < shares1.length; i++) { + for (int i = 0; i < ratios1.length; i++) { Assert.assertArrayEquals("The shares array was not sorted into the " + "expected order: incorrect inner array encountered", - expected1[i], shares1[i], 0.00001f); + expected1[i], ratios1[i], 0.00001f); Assert.assertArrayEquals("The shares array was not sorted into the " + "expected order: incorrect inner array encountered", - expected2[i], shares2[i], 0.00001f); + expected2[i], ratios2[i], 0.00001f); } } @@ -231,16 +231,16 @@ public void testCalculateShares() { used.setResourceValue("test", 2L); capacity.setResourceValue("test", 5L); - int dominant = comparator.calculateShares(used, capacity, shares, - ResourceWeights.NEUTRAL); + int dominant = comparator.calculateClusterAndFairRatios(used, capacity, + shares, ResourceWeights.NEUTRAL); - assertEquals("Calculated cluster share for memory (10MB out of 100MB) is " + assertEquals("Calculated usage ratio for memory (10MB out of 100MB) is " + "incorrect", 0.1, shares[index.get(ResourceInformation.MEMORY_MB.getName())][0], .00001); - assertEquals("Calculated cluster share for vcores (5 out of 10) is " + assertEquals("Calculated usage ratio for vcores (5 out of 10) is " + "incorrect", 0.5, shares[index.get(ResourceInformation.VCORES.getName())][0], .00001); - assertEquals("Calculated cluster share for test resource (2 out of 5) is " + assertEquals("Calculated usage ratio for test resource (2 out of 5) is " + "incorrect", 0.4, shares[index.get("test")][0], .00001); assertEquals("The wrong dominant resource index was returned", index.get(ResourceInformation.VCORES.getName()).intValue(), @@ -252,63 +252,74 @@ public void testCalculateMinShares() { Map index = ResourceUtils.getResourceTypeIndex(); Resource used = Resources.createResource(10, 5); Resource minShares = Resources.createResource(5, 10); - float[][] shares = new float[3][2]; + float[][] ratios = new float[3][2]; DominantResourceFairnessComparator comparator = new DominantResourceFairnessComparator(); used.setResourceValue("test", 2L); minShares.setResourceValue("test", 0L); - comparator.calculateMinShares(used, minShares, shares, - ResourceWeights.NEUTRAL); + comparator.calculateMinShareRatios(used, minShares, ratios); - assertEquals("Calculated min share for memory (10MB out of 5MB) is " + assertEquals("Calculated min share ratio for memory (10MB out of 5MB) is " + "incorrect", 2.0, - shares[index.get(ResourceInformation.MEMORY_MB.getName())][1], .00001); - assertEquals("Calculated min share for vcores (5 out of 10) is " + ratios[index.get(ResourceInformation.MEMORY_MB.getName())][1], .00001); + assertEquals("Calculated min share ratio for vcores (5 out of 10) is " + "incorrect", 0.5, - shares[index.get(ResourceInformation.VCORES.getName())][1], .00001); - assertEquals("Calculated min share for test resource (0 out of 5) is " - + "incorrect", 0.0, shares[index.get("test")][0], .00001); + ratios[index.get(ResourceInformation.VCORES.getName())][1], .00001); + assertEquals("Calculated min share ratio for test resource (0 out of 5) is " + + "incorrect", 0.0, ratios[index.get("test")][0], .00001); } @Test public void testCompareShares() { - float[][] shares1 = {{0.4f, 0.1f}, {0.3f, 2.0f}, {0.2f, 1.0f}}; - float[][] shares2 = {{0.3f, 2.0f}, {0.2f, 0.1f}, {0.2f, 9.0f}}; - float[][] shares3 = {{0.3f, 1.0f}, {0.2f, 0.5f}, {0.1f, 2.0f}}; + float[][] ratios1 = { + {0.4f, 0.1f, 2.0f}, + {0.3f, 2.0f, 0.1f}, + {0.2f, 1.0f, 9.0f} + }; + float[][] ratios2 = { + {0.3f, 2.0f, 1.0f}, + {0.2f, 0.1f, 0.5f}, + {0.2f, 9.0f, 2.0f} + }; + float[][] ratios3 = { + {0.3f, 1.0f, 0.1f}, + {0.2f, 0.5f, 2.0f}, + {0.1f, 2.0f, 1.0f} + }; DominantResourceFairnessComparator comparator = new DominantResourceFairnessComparator(); - int ret = comparator.compareShares(shares1, shares2, 0); + int ret = comparator.compareRatios(ratios1, ratios2, 0); assertEquals("Expected the first array to be larger because the first " - + "cluster share element is larger", 1, ret); + + "usage ratio element is larger", 1, ret); - ret = comparator.compareShares(shares2, shares1, 0); + ret = comparator.compareRatios(ratios2, ratios1, 0); assertEquals("Expected the first array to be smaller because the first " - + "cluster share element is smaller", -1, ret); + + "usage ratio element is smaller", -1, ret); - ret = comparator.compareShares(shares1, shares1, 0); + ret = comparator.compareRatios(ratios1, ratios1, 0); assertEquals("Expected the arrays to be equal, since they're the same " + "array", 0, ret); - ret = comparator.compareShares(shares2, shares2, 0); + ret = comparator.compareRatios(ratios2, ratios2, 0); assertEquals("Expected the arrays to be equal, since they're the same " + "array", 0, ret); - ret = comparator.compareShares(shares3, shares3, 0); + ret = comparator.compareRatios(ratios3, ratios3, 0); assertEquals("Expected the arrays to be equal, since they're the same " + "array", 0, ret); - ret = comparator.compareShares(shares2, shares3, 0); + ret = comparator.compareRatios(ratios2, ratios3, 0); assertEquals("Expected the first array to be larger because the last " - + "cluster share element is larger, and all other elements are equal", + + "usage ratio element is larger, and all other elements are equal", 1, ret); }