diff --git a/spectator-api/src/main/java/com/netflix/spectator/impl/Hash64.java b/spectator-api/src/main/java/com/netflix/spectator/impl/Hash64.java
index cf2efd512..7a368bb37 100644
--- a/spectator-api/src/main/java/com/netflix/spectator/impl/Hash64.java
+++ b/spectator-api/src/main/java/com/netflix/spectator/impl/Hash64.java
@@ -414,4 +414,22 @@ public long computeAndReset() {
reset();
return h;
}
+
+ /**
+ * Helper to efficiently reduce hash from to range of {@code [0, n)}. Based on
+ * fast
+ * alternative to modulo reduction post.
+ */
+ public static int reduce(long hash, int n) {
+ return reduce((int) hash, n);
+ }
+
+ /**
+ * Helper to efficiently reduce hash from to range of {@code [0, n)}. Based on
+ * fast
+ * alternative to modulo reduction post.
+ */
+ public static int reduce(int hash, int n) {
+ return (int) (((hash & 0xFFFFFFFFL) * (n & 0xFFFFFFFFL)) >>> 32);
+ }
}
diff --git a/spectator-api/src/test/java/com/netflix/spectator/impl/Hash64Test.java b/spectator-api/src/test/java/com/netflix/spectator/impl/Hash64Test.java
index 6040d6611..4d1473cd8 100644
--- a/spectator-api/src/test/java/com/netflix/spectator/impl/Hash64Test.java
+++ b/spectator-api/src/test/java/com/netflix/spectator/impl/Hash64Test.java
@@ -272,4 +272,19 @@ public void hashDoubles() {
hashes.add(h);
}
}
+
+ @Test
+ public void reduce() {
+ int n = 10;
+ int[] counts = new int[n];
+ Random r = new Random();
+ for (int i = 0; i < 100_000; ++i) {
+ long hash = r.nextLong();
+ ++counts[Hash64.reduce(hash, n)];
+ }
+ for (int count : counts) {
+ int delta = Math.abs(10_000 - count);
+ Assertions.assertTrue(delta < 1000);
+ }
+ }
}