Skip to content

Commit a454a99

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 2e58a66 commit a454a99

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
@@ -1564,14 +1564,14 @@ object SymDenotations {
15641564
initPrivateWithin: Symbol)
15651565
extends SymDenotation(symbol, maybeOwner, name, initFlags, initInfo, initPrivateWithin) {
15661566

1567-
import util.LRUCache
1567+
import util.LinearTable
15681568

15691569
// ----- caches -------------------------------------------------------
15701570

15711571
private var myTypeParams: List[TypeSymbol] = null
15721572
private var fullNameCache: SimpleIdentityMap[QualifiedNameKind, Name] = SimpleIdentityMap.Empty
15731573

1574-
private var myMemberCache: LRUCache[Name, PreDenotation] = null
1574+
private var myMemberCache: LinearTable[Name, PreDenotation] = null
15751575
private var myMemberCachePeriod: Period = Nowhere
15761576

15771577
/** A cache from types T to baseType(T, C) */
@@ -1582,9 +1582,9 @@ object SymDenotations {
15821582
private var baseDataCache: BaseData = BaseData.None
15831583
private var memberNamesCache: MemberNames = MemberNames.None
15841584

1585-
private def memberCache(using Context): LRUCache[Name, PreDenotation] = {
1585+
private def memberCache(using Context): LinearTable[Name, PreDenotation] = {
15861586
if (myMemberCachePeriod != ctx.period) {
1587-
myMemberCache = new LRUCache
1587+
myMemberCache = LinearTable.empty
15881588
myMemberCachePeriod = ctx.period
15891589
}
15901590
myMemberCache
@@ -1868,10 +1868,10 @@ object SymDenotations {
18681868
final def nonPrivateMembersNamed(name: Name)(using Context): PreDenotation = {
18691869
Stats.record("nonPrivateMembersNamed")
18701870
if (Config.cacheMembersNamed) {
1871-
var denots: PreDenotation = memberCache lookup name
1871+
var denots: PreDenotation = memberCache.lookup(name)
18721872
if (denots == null) {
18731873
denots = computeNPMembersNamed(name)
1874-
memberCache.enter(name, denots)
1874+
myMemberCache = memberCache.enter(name, denots)
18751875
}
18761876
else if (Config.checkCacheMembersNamed) {
18771877
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)