Skip to content

Commit 53961f3

Browse files
committed
Improve re-allocation of hash tables
When going from dense to hashed, we should expand by a multiple greater than two, or we will have to reallocate immediately afterwards.
1 parent bb30c7c commit 53961f3

File tree

1 file changed

+18
-15
lines changed

1 file changed

+18
-15
lines changed

compiler/src/dotty/tools/dotc/util/HashTable.scala

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package dotty.tools.dotc.util
22

33
object HashTable:
4-
inline val MaxDense = 8
4+
/** The number of elements up to which dense packing is used.
5+
* If the number of elements reaches `DenseLimit` a hash table is used instead
6+
*/
7+
inline val DenseLimit = 8
58

69
/** A hash table using open hashing with linear scan which is also very space efficient
710
* at small sizes.
@@ -10,31 +13,28 @@ object HashTable:
1013
* initial size of the table will be the smallest power of two
1114
* that is equal or greater than the given `initialCapacity`.
1215
* Minimum value is 4.
13-
* @param loadFactor The maximum fraction of used elements relative to capacity.
14-
* The hash table will be re-sized once the number of elements exceeds
15-
* the current size of the hash table multiplied by loadFactor.
16-
* However, a table of size up to MaxDense will be re-sized to only
16+
* @param capacityMultiple The minimum multiple of capacity relative to used elements.
17+
* The hash table will be re-sized once the number of elements
18+
* multiplied by capacityMultiple exceeds the current size of the hash table.
19+
* However, a table of size up to DenseLimit will be re-sized only
1720
* once the number of elements reaches the table's size.
1821
*/
1922
class HashTable[Key >: Null <: AnyRef, Value >: Null <: AnyRef]
20-
(initialCapacity: Int = 8: Int, loadFactor: Float = 0.33f):
21-
import HashTable.MaxDense
23+
(initialCapacity: Int = 8, capacityMultiple: Int = 3):
24+
import HashTable.DenseLimit
25+
2226
private var used: Int = _
2327
private var limit: Int = _
2428
private var table: Array[AnyRef] = _
2529
clear()
2630

2731
private def allocate(capacity: Int) =
2832
table = new Array[AnyRef](capacity * 2)
29-
limit = if capacity <= MaxDense then capacity - 1 else (capacity * loadFactor).toInt
33+
limit = if capacity <= DenseLimit then capacity - 1 else capacity / capacityMultiple
3034

3135
private def roundToPower(n: Int) =
3236
if Integer.bitCount(n) == 1 then n
33-
else
34-
def recur(n: Int): Int =
35-
if n == 1 then 2
36-
else recur(n >>> 1) << 1
37-
recur(n)
37+
else 1 << (32 - Integer.numberOfLeadingZeros(n))
3838

3939
/** Remove all elements from this table and set back to initial configuration */
4040
def clear(): Unit =
@@ -44,7 +44,7 @@ class HashTable[Key >: Null <: AnyRef, Value >: Null <: AnyRef]
4444
/** The number of elements in the set */
4545
def size: Int = used
4646

47-
private def isDense = limit < MaxDense
47+
private def isDense = limit < DenseLimit
4848

4949
/** Hashcode, by default `System.identityHashCode`, but can be overriden */
5050
protected def hash(x: Key): Int = System.identityHashCode(x)
@@ -119,7 +119,10 @@ class HashTable[Key >: Null <: AnyRef, Value >: Null <: AnyRef]
119119

120120
private def growTable(): Unit =
121121
val oldTable = table
122-
allocate(table.length)
122+
val newLength =
123+
if oldTable.length == DenseLimit then DenseLimit * 2 * roundToPower(capacityMultiple)
124+
else table.length
125+
allocate(newLength)
123126
if isDense then
124127
Array.copy(oldTable, 0, table, 0, oldTable.length)
125128
else

0 commit comments

Comments
 (0)