Skip to content

Commit

Permalink
reduce alloc for hashCode (#1383)
Browse files Browse the repository at this point in the history
Copy pattern used in SmallHashMap to reduce allocations
while computing the cached hash code value.
  • Loading branch information
brharrington authored Nov 9, 2021
1 parent cab7445 commit 924f4fe
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,35 @@ final class SortedTagMap private (private val data: Array[String], private val l
*/
override def hashCode: Int = {
if (cachedHashCode == 0) {
cachedHashCode = super.hashCode()
cachedHashCode = computeHashCode
}
cachedHashCode
}

/**
* Compute the hash code for the map. This method is based on the
* [[scala.util.hashing.MurmurHash3.unorderedHash()]] method. It is more efficient
* for our purposes because it avoids creating tons of [[scala.runtime.IntRef]]
* objects as well as tuples during iteration.
*/
private[util] def computeHashCode: Int = {
var a, b = 0
var c = 1
var i = 0
while (i < length) {
val h = data(i).hashCode
a += h
b ^= h
if (h != 0) c *= h
i += 1
}
var h = 0x3c074a61
h = scala.util.hashing.MurmurHash3.mix(h, a)
h = scala.util.hashing.MurmurHash3.mix(h, b)
h = scala.util.hashing.MurmurHash3.mixLast(h, c)
scala.util.hashing.MurmurHash3.finalizeHash(h, length)
}

/**
* Overridden to get better performance.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,44 @@ class SortedTagMapSuite extends FunSuite {
assert(a.compareTo(b) < 0)
assert(b.compareTo(a) > 0)
}

test("equals contract") {
val a = SortedTagMap(Array("a", "1", "b", "2"))
val b = SortedTagMap(Array("a", "1", "b", "2", "c", "3"))
val c = SortedTagMap.createUnsafe(Array("a", "1", "b", "2", null, null), 4)
val d = SortedTagMap.createUnsafe(Array("a", "1", "b", "2", null, null, null, null), 4)

// reflexive
assertEquals(a, a)
assertEquals(b, b)
assertEquals(c, c)
assertEquals(d, d)

// symmetric
assert(a != b && b != a)
assert(a == c && c == a)
assert(a == d && c == d)

// transitive
assert(a == c && c == d && a == d)

// nulls
assert(a != null)
}

test("hashCode") {
val a = SortedTagMap(Array("a", "1", "b", "2"))
val b = SortedTagMap(Array("a", "1", "b", "2", "c", "3"))
val c = SortedTagMap.createUnsafe(Array("a", "1", "b", "2", null, null), 4)
val d = SortedTagMap.createUnsafe(Array("a", "1", "b", "2", null, null, null, null), 4)

assertEquals(a.hashCode, a.hashCode)
assertEquals(b.hashCode, b.hashCode)
assertEquals(c.hashCode, c.hashCode)
assertEquals(d.hashCode, d.hashCode)

assertNotEquals(a.hashCode, b.hashCode)
assertEquals(a.hashCode, c.hashCode)
assertEquals(a.hashCode, d.hashCode)
}
}

0 comments on commit 924f4fe

Please sign in to comment.