Skip to content

Commit e06d1c6

Browse files
committed
WalkUtils: add two inline elements into the WalkerCache to avoid memory allocations in the common case.
If there are no more than 2 elements in the cache, we can avoid using the `cache` Dictionary, which avoids memory allocations. Fortunately this is the common case by far (about 97% of all walker invocations).
1 parent 36573f3 commit e06d1c6

File tree

1 file changed

+42
-1
lines changed

1 file changed

+42
-1
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/WalkUtils.swift

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,46 @@ extension SmallProjectionWalkingPath {
111111
/// A client must provide this cache in a `walkUpCache` or `walkDownCache` property.
112112
struct WalkerCache<Path : WalkingPath> {
113113
mutating func needWalk(for value: Value, path: Path) -> Path? {
114+
115+
// Handle the first inline entry.
116+
guard let e = inlineEntry0 else {
117+
inlineEntry0 = (value, path)
118+
return path
119+
}
120+
if e.value == value {
121+
let newPath = e.path.merge(with: path)
122+
if newPath != e.path {
123+
inlineEntry0 = (value, newPath)
124+
return newPath
125+
}
126+
return nil
127+
}
128+
129+
// Handle the second inline entry.
130+
guard let e = inlineEntry1 else {
131+
inlineEntry1 = (value, path)
132+
return path
133+
}
134+
if e.value == value {
135+
let newPath = e.path.merge(with: path)
136+
if newPath != e.path {
137+
inlineEntry1 = (value, newPath)
138+
return newPath
139+
}
140+
return nil
141+
}
142+
143+
// If there are more than two elements, it goes into the `cache` Dictionary.
114144
return cache[value.hashable, default: CacheEntry()].needWalk(path: path)
115145
}
116146

117147
var isEmpty: Bool { cache.isEmpty }
118148

119-
mutating func clear() { cache.removeAll(keepingCapacity: true) }
149+
mutating func clear() {
150+
inlineEntry0 = nil
151+
inlineEntry1 = nil
152+
cache.removeAll(keepingCapacity: true)
153+
}
120154

121155
private struct CacheEntry {
122156
var cachedPath: Path?
@@ -135,6 +169,13 @@ struct WalkerCache<Path : WalkingPath> {
135169
}
136170
}
137171

172+
// If there are no more than 2 elements in the cache, we can avoid using the `cache` Dictionary,
173+
// which avoids memory allocations.
174+
// Fortunately this is the common case by far (about 97% of all walker invocations).
175+
private var inlineEntry0: (value: Value, path: Path)?
176+
private var inlineEntry1: (value: Value, path: Path)?
177+
178+
// All elements, which don't fit into the inline entries.
138179
private var cache = Dictionary<HashableValue, CacheEntry>()
139180
}
140181

0 commit comments

Comments
 (0)