Skip to content

Commit 2226716

Browse files
committed
Cache all memberNamed results
Use a full cache instead of an LRU cache. For `typer/*.scala`, this reduced computed member searches on normal ClassDenotations from 520K to 170K. Cache hit rate improves to 97.5%, from 92.5%. (Without member caching there are almost 7Mio computed members for the same code base).
1 parent 239254a commit 2226716

File tree

2 files changed

+95
-6
lines changed

2 files changed

+95
-6
lines changed

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,14 +1552,14 @@ object SymDenotations {
15521552
initPrivateWithin: Symbol)
15531553
extends SymDenotation(symbol, maybeOwner, name, initFlags, initInfo, initPrivateWithin) {
15541554

1555-
import util.LRUCache
1555+
import util.LinearTable
15561556

15571557
// ----- caches -------------------------------------------------------
15581558

15591559
private var myTypeParams: List[TypeSymbol] = null
15601560
private var fullNameCache: SimpleIdentityMap[QualifiedNameKind, Name] = SimpleIdentityMap.Empty
15611561

1562-
private var myMemberCache: LRUCache[Name, PreDenotation] = null
1562+
private var myMemberCache: LinearTable[Name, PreDenotation] = null
15631563
private var myMemberCachePeriod: Period = Nowhere
15641564

15651565
/** A cache from types T to baseType(T, C) */
@@ -1570,9 +1570,9 @@ object SymDenotations {
15701570
private var baseDataCache: BaseData = BaseData.None
15711571
private var memberNamesCache: MemberNames = MemberNames.None
15721572

1573-
private def memberCache(using Context): LRUCache[Name, PreDenotation] = {
1573+
private def memberCache(using Context): LinearTable[Name, PreDenotation] = {
15741574
if (myMemberCachePeriod != ctx.period) {
1575-
myMemberCache = new LRUCache
1575+
myMemberCache = LinearTable.empty
15761576
myMemberCachePeriod = ctx.period
15771577
}
15781578
myMemberCache
@@ -1856,10 +1856,10 @@ object SymDenotations {
18561856
final def nonPrivateMembersNamed(name: Name)(using Context): PreDenotation = {
18571857
Stats.record("nonPrivateMembersNamed")
18581858
if (Config.cacheMembersNamed) {
1859-
var denots: PreDenotation = memberCache lookup name
1859+
var denots: PreDenotation = memberCache.lookup(name)
18601860
if (denots == null) {
18611861
denots = computeNPMembersNamed(name)
1862-
memberCache.enter(name, denots)
1862+
myMemberCache = memberCache.enter(name, denots)
18631863
}
18641864
else if (Config.checkCacheMembersNamed) {
18651865
val denots1 = computeNPMembersNamed(name)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package dotty.tools.dotc.util
2+
3+
/** A table with an immutable API that must be used linearly since it uses
4+
* mutation internally. A packed array representation is used for sizes
5+
* up to 8, and a hash table is used for larger sizes.
6+
*/
7+
abstract class LinearTable[Key >: Null <: AnyRef, Value >: Null <: AnyRef]:
8+
def lookup(key: Key): Value
9+
def enter(key: Key, value: Value): LinearTable[Key, Value]
10+
def invalidate(key: Key): Unit
11+
def size: Int
12+
13+
object LinearTable:
14+
def empty[Key >: Null <: AnyRef, Value >: Null <: AnyRef]: LinearTable[Key, Value] =
15+
ArrayTable[Key, Value](8)
16+
17+
class ArrayTable[Key >: Null <: AnyRef, Value >: Null <: AnyRef](capacity: Int) extends LinearTable[Key, Value]:
18+
19+
val elems = new Array[AnyRef](capacity * 2)
20+
var size = 0
21+
22+
def lookup(key: Key): Value =
23+
var i = 0
24+
while i < elems.length do
25+
if elems(i) eq key then
26+
return elems(i + 1).asInstanceOf[Value]
27+
if elems(i) == null then
28+
return null
29+
i += 2
30+
null
31+
32+
def enter(key: Key, value: Value): LinearTable[Key, Value] =
33+
var i = 0
34+
while i < elems.length do
35+
if elems(i) eq key then
36+
elems(i + 1) = value
37+
return this
38+
if elems(i) == null then
39+
elems(i) = key
40+
elems(i + 1) = value
41+
size += 1
42+
return this
43+
i += 2
44+
val ht = HashTable[Key, Value](initialCapacity = 16)
45+
i = 0
46+
while i < elems.length do
47+
ht.enter(elems(i).asInstanceOf[Key], elems(i + 1).asInstanceOf[Value])
48+
i += 2
49+
ht.enter(key, value)
50+
ht
51+
52+
def invalidate(key: Key): Unit =
53+
var i = 0
54+
while i < elems.length do
55+
if elems(i) eq key then
56+
size -= 1
57+
elems(i) = null
58+
return
59+
i += 2
60+
61+
override def toString: String =
62+
val buf = new StringBuilder
63+
var i = 0
64+
while i < elems.length do
65+
buf.append(if i == 0 then "ArrayTable(" else ", ")
66+
if elems(i) != null then
67+
buf.append(elems(i))
68+
buf.append(" -> ")
69+
buf.append(elems(i + 1))
70+
i += 2
71+
buf.append(")")
72+
buf.toString
73+
end ArrayTable
74+
75+
class HashTable[Key >: Null <: AnyRef, Value >: Null <: AnyRef](initialCapacity: Int) extends LinearTable[Key, Value]:
76+
private val table = java.util.HashMap[Key, Value](initialCapacity)
77+
78+
def lookup(key: Key): Value =
79+
table.get(key)
80+
def enter(key: Key, value: Value): LinearTable[Key, Value] =
81+
table.put(key, value)
82+
this
83+
def invalidate(key: Key): Unit =
84+
table.remove(key)
85+
def size: Int =
86+
table.size
87+
88+
override def toString: String = table.toString
89+
end HashTable

0 commit comments

Comments
 (0)