Skip to content

Commit d1248c0

Browse files
authored
Perception caching updates (#2649)
* Small updates to perception caching. * wip * debug
1 parent 04efcce commit d1248c0

File tree

2 files changed

+40
-70
lines changed

2 files changed

+40
-70
lines changed
Lines changed: 36 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,45 @@
1-
import Foundation
2-
import OrderedCollections
1+
#if DEBUG
2+
import Foundation
3+
import OrderedCollections
34

4-
final class MemoizedCache<Key: Hashable, Value>: @unchecked Sendable {
5-
private var dictionary = OrderedDictionary<Key, Value>()
6-
private var lock = NSLock()
7-
private let maxCapacity: Int
8-
9-
// TODO possibly remove these stats later
10-
private var totalEvictions = 0
11-
private var totalCalls = 0
12-
private var totalHits = 0
13-
14-
init(maxCapacity: Int = 500) {
15-
self.maxCapacity = maxCapacity
16-
}
17-
18-
func printStats() {
19-
lock.sync {
20-
let hitRate = (Float(totalHits) / Float(totalCalls)) * 100
21-
let evictionRate = (Float(totalEvictions) / Float(totalCalls)) * 100
22-
print("size = \(dictionary.count), totalCalls = \(totalCalls), hitRate = \(hitRate)%, evictionRate = \(evictionRate)%")
5+
func memoize<Result>(
6+
maxCapacity: Int = 500,
7+
_ apply: @escaping () -> Result
8+
) -> () -> Result {
9+
let cache = Cache<[NSNumber], Result>(maxCapacity: maxCapacity)
10+
return {
11+
let callStack = Thread.callStackReturnAddresses
12+
guard let memoizedResult = cache[callStack]
13+
else {
14+
let result = apply()
15+
defer { cache[callStack] = result }
16+
return result
17+
}
18+
return memoizedResult
2319
}
2420
}
25-
26-
subscript(key: Key) -> Value? {
27-
get {
28-
lock.sync {
29-
totalCalls += 1
30-
if let value = dictionary[key] {
31-
totalHits += 1
32-
return value
21+
22+
private final class Cache<Key: Hashable, Value>: @unchecked Sendable {
23+
var dictionary = OrderedDictionary<Key, Value>()
24+
var lock = NSLock()
25+
let maxCapacity: Int
26+
init(maxCapacity: Int = 500) {
27+
self.maxCapacity = maxCapacity
28+
}
29+
subscript(key: Key) -> Value? {
30+
get {
31+
self.lock.sync {
32+
self.dictionary[key]
3333
}
34-
return nil
3534
}
36-
}
37-
set {
38-
lock.sync {
39-
dictionary[key] = newValue
40-
if dictionary.count > maxCapacity {
41-
// evict first (oldest) element
42-
dictionary.removeFirst()
43-
totalEvictions += 1
35+
set {
36+
self.lock.sync {
37+
self.dictionary[key] = newValue
38+
if self.dictionary.count > self.maxCapacity {
39+
self.dictionary.removeFirst()
40+
}
4441
}
4542
}
4643
}
4744
}
48-
49-
}
50-
51-
func memoize<Input: Hashable, Result>(
52-
maxCapacity: Int = 500,
53-
_ apply: @escaping (Input) -> Result
54-
) -> (Input) -> Result {
55-
let cache = MemoizedCache<Input, Result>(maxCapacity: maxCapacity)
56-
57-
return { input in
58-
// defer { cache.printStats() }
59-
if let memoizedResult = cache[input] {
60-
return memoizedResult
61-
}
62-
63-
let result = apply(input)
64-
cache[input] = result
65-
return result
66-
}
67-
68-
}
45+
#endif

Sources/Perception/PerceptionRegistrar.swift

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,11 @@ extension PerceptionRegistrar: Hashable {
191191

192192
#if DEBUG
193193
private func perceptionCheck() {
194-
if #unavailable(iOS 17, macOS 14, tvOS 17, watchOS 10),
194+
if
195+
#unavailable(iOS 17, macOS 14, tvOS 17, watchOS 10),
195196
!PerceptionLocals.isInPerceptionTracking,
196197
!PerceptionLocals.isInWithoutPerceptionChecking,
197-
isInSwiftUIBody
198+
isInSwiftUIBody()
198199
{
199200
runtimeWarn(
200201
"""
@@ -205,15 +206,7 @@ extension PerceptionRegistrar: Hashable {
205206
}
206207
}
207208

208-
var isInSwiftUIBody: Bool {
209-
determineIfInSwiftUIBodyMemoized(Thread.callStackReturnAddresses)
210-
}
211-
212-
let determineIfInSwiftUIBodyMemoized = memoize({ (_ : [NSNumber]) in
213-
determineIfInSwiftUIBody
214-
})
215-
216-
var determineIfInSwiftUIBody: Bool {
209+
private let isInSwiftUIBody: () -> Bool = memoize {
217210
for callStackSymbol in Thread.callStackSymbols {
218211
let mangledSymbol = callStackSymbol.utf8
219212
.drop(while: { $0 != .init(ascii: "$") })

0 commit comments

Comments
 (0)