@@ -329,15 +329,20 @@ public boolean isEquivalentTo(RoundRobinPicker picker) {
329329 }
330330
331331 /*
332- * Implementation of Static Stride Scheduler, replaces EDFScheduler.
332+ * The Static Stride Scheduler is an implementation of an earliest deadline first (EDF) scheduler
333+ * in which each object is chosen periodically with frequency proportional to its weight.
333334 * <p>
335+ * Specifically, each backend is given a deadline equal to the multiplicative inverse of
336+ * its weight. The place of each backend in its deadline is tracked, and each call to
337+ * pick a backend returns the backend index with the least remaining time in its deadline.
338+ * <p>
339+ * The way in which this is implemented is through a static stride scheduler.
334340 * The Static Stride Scheduler works by iterating through the list of subchannel weights
335341 * and using modular arithmetic to proportionally distribute picks, favoring entries
336342 * with higher weights. It is based on the observation that the intended sequence generated
337- * from the EDF scheduler is a periodic one that can be achieved through modular arithmetic.
338- * This scheduler generates a practically equivalent sequence of picks as the EDFScheduler.
339- * The Static Stride Scheduler is more performant than the EDFScheduler, as it removes
340- * the need for a priority queue (and thus mutex locks).
343+ * from an EDF scheduler is a periodic one that can be achieved through modular arithmetic.
344+ * The Static Stride Scheduler is more performant than other implementations of the EDF
345+ * Scheduler, as it removes the need for a priority queue (and thus mutex locks).
341346 * <p>
342347 * go/static-stride-scheduler
343348 * <p>
@@ -348,7 +353,7 @@ public boolean isEquivalentTo(RoundRobinPicker picker) {
348353 */
349354 @ VisibleForTesting
350355 static final class StaticStrideScheduler {
351- private final int [] scaledWeights ;
356+ private final short [] scaledWeights ;
352357 private final int sizeDivisor ;
353358 private final AtomicInteger sequence ;
354359 private static final int K_MAX_WEIGHT = 0xFFFF ;
@@ -359,7 +364,7 @@ static final class StaticStrideScheduler {
359364 int numWeightedChannels = 0 ;
360365 double sumWeight = 0 ;
361366 float maxWeight = 0 ;
362- int meanWeight = 0 ;
367+ short meanWeight = 0 ;
363368 for (float weight : weights ) {
364369 if (weight > 0 ) {
365370 sumWeight += weight ;
@@ -370,18 +375,19 @@ static final class StaticStrideScheduler {
370375
371376 double scalingFactor = K_MAX_WEIGHT / maxWeight ;
372377 if (numWeightedChannels > 0 ) {
373- meanWeight = (int ) Math .round (scalingFactor * sumWeight / numWeightedChannels );
378+ int value = (int ) Math .round (scalingFactor * sumWeight / numWeightedChannels );
379+ meanWeight = (short ) (value > 0x7FFF ? value - 0x10000 : value );
374380 } else {
375381 meanWeight = 1 ;
376382 }
377383
378384 // scales weights s.t. max(weights) == K_MAX_WEIGHT, meanWeight is scaled accordingly
379- int [] scaledWeights = new int [numChannels ];
385+ short [] scaledWeights = new short [numChannels ];
380386 for (int i = 0 ; i < numChannels ; i ++) {
381387 if (weights [i ] <= 0 ) {
382388 scaledWeights [i ] = meanWeight ;
383389 } else {
384- scaledWeights [i ] = (int ) Math .round (weights [i ] * scalingFactor );
390+ scaledWeights [i ] = (short ) Math .round (weights [i ] * scalingFactor );
385391 }
386392 }
387393
@@ -414,7 +420,7 @@ int pick() {
414420 long sequence = this .nextSequence ();
415421 int backendIndex = (int ) (sequence % this .sizeDivisor );
416422 long generation = sequence / this .sizeDivisor ;
417- long weight = this .scaledWeights [backendIndex ];
423+ int weight = Short . toUnsignedInt ( this .scaledWeights [backendIndex ]) ;
418424 long offset = (long ) K_MAX_WEIGHT / 2 * backendIndex ;
419425 if ((weight * generation + offset ) % K_MAX_WEIGHT < K_MAX_WEIGHT - weight ) {
420426 continue ;
0 commit comments