package org.radargun.stages.helpers; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public final class Range { private long start; private long end; private Range() {} public Range(long start, long end) { this.start = start; this.end = end; } public long getStart() { return start; } public long getEnd() { return end; } public long getSize() { return end - start; } public Range shift(long shift) { return new Range(start + shift, end + shift); } @Override public int hashCode() { return (int) (start + 31 * (end - 1)); } @Override public boolean equals(Object other) { if (other == null || !(other instanceof Range)) return false; Range otherRange = (Range) other; return start == otherRange.getStart() && end == otherRange.getEnd(); } @Override public String toString() { return "[" + start + ", " + end + "]"; } /** * * Returns pair [startKey, endKey] that specifies a subrange { startKey, ..., endKey-1 } of key * range { 0, 1, ..., numKeys-1 } divideRange divides the keyset evenly to numParts parts with * difference of part lengths being max 1. * * @param numKeys * Total number of keys * @param numParts * Number of parts we're dividing to * @param partIdx * Index of part we want to get * @return The pair [startKey, endKey] */ public static Range divideRange(long numKeys, int numParts, int partIdx) { long base = (numKeys / numParts) + 1; long mod = numKeys % numParts; if (partIdx < mod) { long startKey = partIdx * base; return new Range(startKey, startKey + base); } else { long startKey = base * mod + (partIdx - mod) * (base - 1); return new Range(startKey, startKey + base - 1); } } public static List<List<Range>> balance(List<Range> ranges, int numParts) { int totalSize = 0; for (Range range : ranges) { totalSize += range.getSize(); } Iterator<Range> iterator = ranges.iterator(); Range currentRange; if (iterator.hasNext()) { currentRange = iterator.next(); } else { currentRange = new Range(0, 0); } List<List<Range>> balanced = new ArrayList<List<Range>>(numParts); for (int i = 0; i < numParts; ++i) { long myRangeSize = divideRange(totalSize, numParts, i).getSize(); List<Range> myRanges = new ArrayList<Range>(); while (currentRange.getSize() <= myRangeSize) { myRanges.add(currentRange); myRangeSize -= currentRange.getSize(); if (iterator.hasNext()) { currentRange = iterator.next(); } else { break; // this should happen only for the last range } } if (myRangeSize != 0) { myRanges.add(new Range(currentRange.getStart(), currentRange.getStart() + myRangeSize)); currentRange = new Range(currentRange.getStart() + myRangeSize, currentRange.getEnd()); } balanced.add(myRanges); } return balanced; } }