Skip to content

Commit 1c1a1cf

Browse files
committed
Cached transformed results in Cache
The transform to get the transformed result might be expensive, so we should cache its result.
1 parent 29619a6 commit 1c1a1cf

File tree

2 files changed

+27
-12
lines changed

2 files changed

+27
-12
lines changed

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,19 +1009,21 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
10091009
return []
10101010
}
10111011

1012+
let request = BuildTargetSourcesRequest(targets: targets.sorted { $0.uri.stringValue < $1.uri.stringValue })
1013+
10121014
// If we have a cached request for a superset of the targets, serve the result from that cache entry.
10131015
let fromSuperset = await orLog("Getting source files from superset request") {
1014-
try await cachedTargetSources.get(isolation: self) { request in
1015-
targets.isSubset(of: request.targets)
1016-
} transform: { response in
1017-
return BuildTargetSourcesResponse(items: response.items.filter { targets.contains($0.target) })
1018-
}
1016+
try await cachedTargetSources.getDerived(
1017+
isolation: self,
1018+
request,
1019+
canReuseKey: { targets.isSubset(of: $0.targets) },
1020+
transform: { BuildTargetSourcesResponse(items: $0.items.filter { targets.contains($0.target) }) }
1021+
)
10191022
}
10201023
if let fromSuperset {
10211024
return fromSuperset.items
10221025
}
10231026

1024-
let request = BuildTargetSourcesRequest(targets: targets.sorted { $0.uri.stringValue < $1.uri.stringValue })
10251027
let response = try await cachedTargetSources.get(request, isolation: self) { request in
10261028
try await buildSystemAdapter.send(request)
10271029
}

Sources/SwiftExtensions/Cache.swift

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,28 @@ package class Cache<Key: Sendable & Hashable, Result: Sendable> {
3333
return try await task.value
3434
}
3535

36-
package func get(
36+
/// Get the value cached for `key`. If no value exists for `key`, try deriving the result from an existing cache entry
37+
/// that satisfies `canReuseKey` by applying `transform` to that result.
38+
package func getDerived(
3739
isolation: isolated any Actor,
38-
whereKey keyPredicate: (Key) -> Bool,
39-
transform: @Sendable @escaping (Result) -> Result
40+
_ key: Key,
41+
canReuseKey: @Sendable @escaping (Key) -> Bool,
42+
transform: @Sendable @escaping (_ cachedResult: Result) -> Result
4043
) async throws -> Result? {
41-
for (key, value) in storage {
42-
if keyPredicate(key) {
43-
return try await transform(value.value)
44+
if let cached = storage[key] {
45+
// If we have a value for the requested key, prefer that
46+
return try await cached.value
47+
}
48+
49+
// See if don't have an entry for this key, see if we can derive the value from a cached entry.
50+
for (cachedKey, cachedValue) in storage {
51+
guard canReuseKey(cachedKey) else {
52+
continue
4453
}
54+
let transformed = Task { try await transform(cachedValue.value) }
55+
// Cache the transformed result.
56+
storage[key] = transformed
57+
return try await transformed.value
4558
}
4659
return nil
4760
}

0 commit comments

Comments
 (0)