Skip to content

Commit 94dba23

Browse files
committed
Fix Swift 6.4 build errors from state handler refactor
Three issues introduced by #132 on the current toolchain: 1. StateHandler+MTLBuffer.swift: Remove insertAll(into: inout AsyncMutableViews). The CoreAI framework's @Lifetime(self: &mutableValue) on AsyncMutableViews.insert makes it impossible to abstract state insertion into a helper — all AsyncMutableValue declarations must live in the same flat scope as the consume site. Call sites now simply omit additional state insertion (the pipelined engine's hybrid model support needs a different approach — tracked separately). 2. CoreAISequentialEngine.swift: Delete dead ensureKVCapacity(), copyCache(), and zeroFill() methods that reference properties removed by the state handler refactor (kvCache: SyncStateHandler supersedes them). 3. StateHandler+NDArray.swift: Change zeroFillNDArray from private to internal so StateHandlerTests can access it via @testable import.
1 parent 6329412 commit 94dba23

4 files changed

Lines changed: 1 addition & 91 deletions

File tree

swift/Sources/CoreAILanguageModels/Handlers/StateHandler+MTLBuffer.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,6 @@ public struct FixedMTLBufferState {
4848
get { bindings[index] }
4949
}
5050

51-
/// Insert all states into AsyncMutableViews for pipelined encoding.
52-
public func insertAll(into views: inout InferenceFunction.AsyncMutableViews) {
53-
for (name, buffer, scalarType, shape, strides) in bindings {
54-
var value = unsafe InferenceFunction.AsyncMutableValue(
55-
unsafeBuffer: buffer, byteOffset: 0,
56-
scalarType: scalarType, shape: shape, strides: strides)
57-
views.insert(&value, for: name)
58-
}
59-
}
60-
6151
/// Zero all state buffers. Caller must ensure no in-flight GPU work references these.
6252
public mutating func reset() {
6353
for (_, buffer, _, _, _) in bindings {

swift/Sources/CoreAILanguageModels/Handlers/StateHandler+NDArray.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public struct GrowingNDArrayState: SyncStateHandler {
176176
// MARK: - Shared Utilities
177177

178178
/// Zero-initialize an NDArray, dispatching on scalar type.
179-
private func zeroFillNDArray(_ array: inout NDArray) {
179+
func zeroFillNDArray(_ array: inout NDArray) {
180180
let count = array.shape.reduce(1, *)
181181
switch array.scalarType {
182182
case .float16, .bfloat16:

swift/Sources/CoreAILanguageModels/InferenceEngines/CoreAIPipelinedEngine.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,6 @@ private struct EngineImpl: ~Copyable {
829829
var asyncStates = InferenceFunction.AsyncMutableViews()
830830
asyncStates.insert(&keyState, for: keyCacheName)
831831
asyncStates.insert(&valState, for: valueCacheName)
832-
additionalStates?.insertAll(into: &asyncStates)
833832

834833
// Build Output as AsyncMutableValue (logits)
835834
// Decode uses per-step rotating buffer; prefill uses the shared growing buffer.
@@ -1141,7 +1140,6 @@ private struct EngineImpl: ~Copyable {
11411140
var asyncStates = InferenceFunction.AsyncMutableViews()
11421141
asyncStates.insert(&keyState, for: keyCacheName)
11431142
asyncStates.insert(&valState, for: valueCacheName)
1144-
additionalStates?.insertAll(into: &asyncStates)
11451143

11461144
let logitsShape = [1, queryLength, vocabSize]
11471145
let logitsStrides = try resolvedStrides(descriptor: logitsBaseDesc, shape: logitsShape)
@@ -1248,7 +1246,6 @@ private struct EngineImpl: ~Copyable {
12481246
var asyncStates = InferenceFunction.AsyncMutableViews()
12491247
asyncStates.insert(&keyState, for: keyCacheName)
12501248
asyncStates.insert(&valState, for: valueCacheName)
1251-
additionalStates?.insertAll(into: &asyncStates)
12521249

12531250
let lShape = [1, shape, vocabSize]
12541251
let lStrides = try resolvedStrides(descriptor: logitsBaseDesc, shape: lShape)

swift/Sources/CoreAILanguageModels/InferenceEngines/CoreAISequentialEngine.swift

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -475,84 +475,7 @@ public final class CoreAISequentialEngine: InferenceEngine, @unchecked Sendable
475475
cleanupSpan.end()
476476
}
477477

478-
// MARK: - KV Cache (dynamic growth)
479-
480-
private func ensureKVCapacity(forContextLength needed: Int) throws {
481-
guard needed > currentKVCapacity else { return }
482-
guard needed <= config.maxContextLength else {
483-
throw InferenceRuntimeError.invalidState(
484-
"Context length \(needed) exceeds maximum \(config.maxContextLength)")
485-
}
486-
487-
var newCapacity = currentKVCapacity
488-
while newCapacity < needed { newCapacity *= 2 }
489-
newCapacity = min(newCapacity, config.maxContextLength)
490-
491-
let resolvedKeyDesc = keyCacheDescriptor.resolvingDynamicDimensions(
492-
keyCacheDescriptor.shape.map { $0 < 0 ? newCapacity : $0 })
493-
let resolvedValueDesc = valueCacheDescriptor.resolvingDynamicDimensions(
494-
valueCacheDescriptor.shape.map { $0 < 0 ? newCapacity : $0 })
495-
496-
var newKeyCache = NDArray(descriptor: resolvedKeyDesc)
497-
var newValueCache = NDArray(descriptor: resolvedValueDesc)
498-
_ = newKeyCache.mutableRawView()
499-
_ = newValueCache.mutableRawView()
500-
501-
try Self.copyCache(from: keyCache, to: &newKeyCache)
502-
try Self.copyCache(from: valueCache, to: &newValueCache)
503-
504-
CLILogger.log("KV cache grew: \(currentKVCapacity)\(newCapacity)")
505-
keyCache = newKeyCache
506-
valueCache = newValueCache
507-
currentKVCapacity = newCapacity
508-
}
509-
510-
private static func copyCache(from source: NDArray, to destination: inout NDArray) throws {
511-
let srcShape = source.shape
512-
let dstShape = destination.shape
513-
guard let headDim = srcShape.last else {
514-
throw InferenceRuntimeError.invalidState("KV cache has empty shape — cannot copy")
515-
}
516-
let seqDim = KVCacheFactory.detectSequenceDim(shape: srcShape)
517-
518-
// Number of independent blocks before the sequence dimension (L * B * H or B * H)
519-
let numBlocks = srcShape[..<seqDim].reduce(1, *)
520-
let oldSeqLen = srcShape[seqDim]
521-
let copySize = oldSeqLen * headDim
522-
523-
// Strides in elements for the sequence block
524-
let srcBlockStride = srcShape[seqDim...].reduce(1, *) // S_old * D
525-
let dstBlockStride = dstShape[seqDim...].reduce(1, *) // S_new * D
526-
527-
source.view(as: LogitsScalarType.self).withUnsafePointer { srcPtr, _, _ in
528-
let dstView = destination.mutableView(as: LogitsScalarType.self)
529-
dstView.withUnsafeMutablePointer { dstPtr, _, _ in
530-
for block in 0..<numBlocks {
531-
let srcOff = block * srcBlockStride
532-
let dstOff = block * dstBlockStride
533-
dstPtr.advanced(by: dstOff).update(
534-
from: srcPtr.advanced(by: srcOff), count: copySize)
535-
}
536-
}
537-
}
538-
}
539-
540478
// MARK: - Helpers
541-
542-
private func zeroFill(_ array: inout NDArray) {
543-
let count = array.shape.reduce(1, *)
544-
let view = array.mutableView(as: LogitsScalarType.self)
545-
// Inlined constant write — under -Onone, fillNDArray's
546-
// `(Int) -> LogitsScalarType` closure is invoked per element (no inlining),
547-
// which made zeroing the KV cache (~14.7M elements for a 32K-context
548-
// Qwen3) take ~6 seconds per `reset()`. Direct loop keeps this in
549-
// the few-ms range even unoptimized; under -O it lowers to memset.
550-
view.withUnsafeMutablePointer { ptr, _, _ in
551-
for i in 0..<count {
552-
ptr[i] = 0
553-
}
554-
}
555-
}
556479
}
557480

558481
extension CoreAISequentialEngine {

0 commit comments

Comments
 (0)