Skip to content

Commit 775cb28

Browse files
ahaviliclaude
andcommitted
perf(index): share file index across IndexStoreReader instances via global cache
Add a shared file index cache (_sharedFileIndexCache) in the Swift bridge so that multiple IndexStoreReader instances for the same store path reuse the same pre-built file→unit index. This avoids redundant unit scans that serialize on _buildIndexLock, reducing indexstore FFI CPU from 64s to 12s (5.4x) and improving overall CPU utilization from 191% to 207%. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent dc62874 commit 775cb28

1 file changed

Lines changed: 19 additions & 5 deletions

File tree

grapha-swift/swift-bridge/Sources/GraphaSwiftBridge/IndexStoreReader.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,18 @@ import Synchronization
9696

9797
// MARK: - Callback State
9898

99-
// buildFileIndex globals — only called once, no concurrency concern.
99+
// buildFileIndex globals — serialized by _buildIndexLock.
100100
private let _buildIndexLock = Mutex<Void>(())
101101
nonisolated(unsafe) private var _cbStore: indexstore_t? = nil
102102
nonisolated(unsafe) private var _cbFileIndex: [String: UnitInfo] = [:]
103103
nonisolated(unsafe) private var _cbRecordName: String? = nil
104104
nonisolated(unsafe) private var _cbImports: [ExtractedImport] = []
105105

106+
/// Shared file index cache keyed by store path. Multiple IndexStoreReader
107+
/// instances for the same store share a single file index, avoiding
108+
/// redundant unit scans when threads each open their own handle.
109+
private let _sharedFileIndexCache = Mutex<[String: [String: UnitInfo]]>([:])
110+
106111
/// Per-extraction context passed through the `ctx` pointer of _apply_f callbacks.
107112
/// This eliminates global mutable state, allowing concurrent extractions.
108113
private final class ExtractionContext {
@@ -147,6 +152,7 @@ private func _collectRecordName(_ ctx: UnsafeMutableRawPointer?, _ dep: indexsto
147152

148153
final class IndexStoreReader: @unchecked Sendable {
149154
private let store: indexstore_t
155+
private let storePath: String
150156
/// Lazy file→unit index, built on first access
151157
private var fileIndex: [String: UnitInfo]?
152158

@@ -156,6 +162,7 @@ final class IndexStoreReader: @unchecked Sendable {
156162
return nil
157163
}
158164
self.store = store
165+
self.storePath = storePath
159166
}
160167

161168
deinit {
@@ -165,11 +172,18 @@ final class IndexStoreReader: @unchecked Sendable {
165172
// MARK: - Public
166173

167174
func extractFile(_ filePath: String) -> (UnsafeMutableRawPointer, UInt32)? {
168-
// Build the file index once (thread-safe via lock)
169175
if fileIndex == nil {
170-
_buildIndexLock.withLock { _ in
171-
if fileIndex == nil {
172-
fileIndex = buildFileIndex()
176+
if let cached = _sharedFileIndexCache.withLock({ $0[storePath] }) {
177+
fileIndex = cached
178+
} else {
179+
_buildIndexLock.withLock { _ in
180+
if let cached = _sharedFileIndexCache.withLock({ $0[storePath] }) {
181+
fileIndex = cached
182+
} else {
183+
fileIndex = buildFileIndex()
184+
let idx = fileIndex!
185+
_sharedFileIndexCache.withLock { $0[storePath] = idx }
186+
}
173187
}
174188
}
175189
}

0 commit comments

Comments
 (0)