Skip to content

Commit

Permalink
Improve hash functions for primitive hashtables
Browse files Browse the repository at this point in the history
Existing hashcodes from Agrona were very fast, but overly simple. In some
cases they caused too long probing chains. The new hash functions come from
Koloboke and provide better bit avalanching.
  • Loading branch information
Marko Topolnik committed Nov 24, 2015
1 parent 844f381 commit 654fd31
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 23 deletions.
3 changes: 3 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ com.hazelcast.client.impl.protocol.util.UnsafeBuffer
com.hazelcast.client.impl.protocol.util.BufferBuilder
contain code originating from the Agrona project
(https://github.com/real-logic/Agrona).

The class com.hazelcast.util.HashUtil contains code originating
from the Koloboke project (https://github.com/OpenHFT/Koloboke).
16 changes: 16 additions & 0 deletions hazelcast/src/main/java/com/hazelcast/util/HashUtil.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2008-2015, Hazelcast, Inc. All Rights Reserved.
* Portions Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -410,6 +411,21 @@ public static long MurmurHash3_fmix(long k) {
return k;
}

public static long fastLongMix(long k) {
// phi = 2^64 / goldenRatio
final long phi = 0x9E3779B97F4A7C15L;
long h = k * phi;
h ^= h >>> 32;
return h ^ (h >>> 16);
}

public static int fastIntMix(int k) {
// phi = 2^32 / goldenRatio
final int phi = 0x9E3779B9;
final int h = k * phi;
return h ^ (h >>> 16);
}

/**
* Hash code for multiple objects using {@link Arrays#hashCode(Object[])}.
*/
Expand Down
23 changes: 11 additions & 12 deletions hazelcast/src/main/java/com/hazelcast/util/collection/Hashing.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,25 @@

package com.hazelcast.util.collection;

import static com.hazelcast.util.HashUtil.fastIntMix;
import static com.hazelcast.util.HashUtil.fastLongMix;

/**
* Hashcode calculation.
* Hashcode functions for classes in this package.
*/
public final class Hashing {
private Hashing() { }

public static int intHash(final int value, final int mask) {
final int hash = value ^ (value >>> 16);
return hash & mask;
static int intHash(final int value, final int mask) {
return fastIntMix(value) & mask;
}

public static int longHash(final long value, final int mask) {
int hash = (int) value ^ (int) (value >>> 32);
hash ^= (hash >>> 16);
return hash & mask;
static int longHash(final long value, final int mask) {
return ((int) fastLongMix(value)) & mask;
}

public static int evenLongHash(final long value, final int mask) {
int hash = (int) value ^ (int) (value >>> 32);
hash = (hash << 1) - (hash << 8);
return hash & mask;
static int evenLongHash(final long value, final int mask) {
final int h = (int) fastLongMix(value);
return h & mask & ~1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ public class Int2ObjectHashMapTest {
intToObjectMap.put(testEntry, String.valueOf(testEntry));
}

final String mapAsAString = "{12=12, 11=11, 7=7, 19=19, 3=3, 1=1}";
final String mapAsAString = "{1=1, 3=3, 7=7, 12=12, 19=19, 11=11}";
assertThat(intToObjectMap.toString(), equalTo(mapAsAString));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public class IntHashSetTest {
final IntHashSet other = new IntHashSet(1000, -1);
other.copy(set);

assertThat(other, contains(1, 2));
assertThat(other, contains(2, 1));
}

@Test public void twoEmptySetsAreEqual() {
Expand Down Expand Up @@ -274,10 +274,10 @@ public class IntHashSetTest {
private void assertIteratorHasElements() {
final Iterator<Integer> iter = set.iterator();

assertTrue(iter.hasNext());
assertEquals(Integer.valueOf(1), iter.next());
assertTrue(iter.hasNext());
assertEquals(Integer.valueOf(2), iter.next());
assertTrue(iter.hasNext());
assertEquals(Integer.valueOf(1), iter.next());
assertFalse(iter.hasNext());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ public class Long2LongHashMapTest {

final Iterator<Entry<Long, Long>> it = entrySet.iterator();
assertTrue(it.hasNext());
assertEntryIs(it.next(), 1L, 1L);
assertTrue(it.hasNext());
assertEntryIs(it.next(), 2L, 3L);
assertTrue(it.hasNext());
assertEntryIs(it.next(), 1L, 1L);
assertFalse(it.hasNext());
}

Expand Down Expand Up @@ -236,7 +236,7 @@ public class Long2LongHashMapTest {
@Test public void toStringShouldReportAllEntries() {
map.put(1, 2);
map.put(3, 4);
assertEquals("{1->2 3->4}", map.toString());
assertEquals("{3->4 1->2}", map.toString());
}

private static void assertEntryIs(final Entry<Long, Long> entry, final long expectedKey, final long expectedValue) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ public class Long2ObjectHashMapTest {
longToObjectMap.put(testEntry, String.valueOf(testEntry));
}

final String mapAsAString = "{12=12, 11=11, 7=7, 19=19, 3=3, 1=1}";
final String mapAsAString = "{11=11, 7=7, 3=3, 12=12, 19=19, 1=1}";
assertThat(longToObjectMap.toString(), equalTo(mapAsAString));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public class LongHashSetTest {
final LongHashSet other = new LongHashSet(1000, -1);
other.copy(set);

assertThat(other, contains(1L, 2L));
assertThat(other, contains(2L, 1L));
}

@Test public void twoEmptySetsAreEqual() {
Expand Down Expand Up @@ -274,10 +274,10 @@ public class LongHashSetTest {
private void assertIteratorHasElements() {
final Iterator<Long> iter = set.iterator();

assertTrue(iter.hasNext());
assertEquals(Long.valueOf(1), iter.next());
assertTrue(iter.hasNext());
assertEquals(Long.valueOf(2), iter.next());
assertTrue(iter.hasNext());
assertEquals(Long.valueOf(1), iter.next());
assertFalse(iter.hasNext());
}
}

0 comments on commit 654fd31

Please sign in to comment.