```
feat(AI): 添加MLX内存管理和AI模型互斥卸载机制 为防止应用因内存溢出被系统终止,在项目中添加了MLX框架依赖, 并在应用启动时配置GPU缓存限制,设置256MB缓存上限以避免内存过度使用。 同时实现了LLM和VL模型的互斥卸载机制,确保大模型不会同时常驻内存, 通过在加载一个模型前先卸载另一个模型来控制内存使用,防止jetsam OOM。 chore(project): 配置代码签名授权文件 refactor(localization): 调整本地化字符串并清理冗余条目 修正了提醒任务和建议相关的本地化文本,调整了多个UI字符串, 清理了过时和重复的本地化条目,更新了AI识别相关的新字符串资源。 ```
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import Foundation
|
||||
import MLX
|
||||
|
||||
enum AIRuntimeError: Error, LocalizedError {
|
||||
case notReady
|
||||
@@ -33,6 +34,16 @@ actor AIRuntime {
|
||||
|
||||
private init() {}
|
||||
|
||||
/// App 启动时调用一次:给 MLX 的 GPU 缓冲池设上限,避免 reuse cache 在大模型常驻之上
|
||||
/// 继续膨胀、把峰值推过单 App 内存上限。仅真机生效(模拟器走 CPU,且部分 Metal 路径会 abort)。
|
||||
/// 与 increased-memory-limit entitlement + LLM/VL 互斥卸载配合,三管齐下防 jetsam OOM。
|
||||
nonisolated static func configureMLXMemory() {
|
||||
#if !targetEnvironment(simulator)
|
||||
// 256MB cache 上限:够复用、不至于在 3GB 模型之上再囤几百 MB 空闲缓冲。
|
||||
MLX.GPU.set(cacheLimit: 256 * 1024 * 1024)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 加载模型。首次调用会真正加载,后续幂等。
|
||||
func prepare() async throws {
|
||||
switch status {
|
||||
@@ -52,6 +63,10 @@ actor AIRuntime {
|
||||
throw AIRuntimeError.notReady
|
||||
}
|
||||
|
||||
// OOM 闸门(§3.1):LLM(~1GB)与 VL(~3GB)不可同时常驻,叠加会冲过单 App 内存上限被 jetsam 杀。
|
||||
// 加载 LLM 前先卸 VL,释放其 ModelContainer + MLX 显存缓存。
|
||||
unloadVL()
|
||||
|
||||
status = .loading
|
||||
do {
|
||||
let session = try await LLMSession.load(
|
||||
@@ -120,6 +135,10 @@ actor AIRuntime {
|
||||
throw AIRuntimeError.notReady
|
||||
}
|
||||
|
||||
// OOM 闸门(§3.1):加载 VL(~3GB)前先卸 LLM(~1GB),否则两者常驻叠加冲过内存上限被 jetsam 杀
|
||||
// —— 这正是「异常项快拍识别时 App 自动退出」的主因。
|
||||
unloadLLM()
|
||||
|
||||
vlStatus = .loading
|
||||
do {
|
||||
let session = try await VLSession.load(
|
||||
@@ -133,6 +152,26 @@ actor AIRuntime {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 卸载(OOM 闸门)
|
||||
|
||||
/// 卸载 LLM,释放 ModelContainer 引用并清 MLX 显存缓存。幂等。
|
||||
/// 注:若此刻有 generate() 的流仍在跑,它持有 session 快照,真正释放要等流结束;
|
||||
/// 但快拍/归档场景下没有并发文本流,卸载即时生效。
|
||||
private func unloadLLM() {
|
||||
guard llmSession != nil else { return }
|
||||
llmSession = nil
|
||||
status = .notReady
|
||||
MLX.GPU.clearCache()
|
||||
}
|
||||
|
||||
/// 卸载 VL,释放 ModelContainer 引用并清 MLX 显存缓存。幂等。
|
||||
private func unloadVL() {
|
||||
guard vlSession != nil else { return }
|
||||
vlSession = nil
|
||||
vlStatus = .notReady
|
||||
MLX.GPU.clearCache()
|
||||
}
|
||||
|
||||
/// 图像 → JSON 字符串(由 VLPrompts.reportExtraction 引导)。
|
||||
/// 调用方负责解析 + 失败回退(§3.2)。
|
||||
/// AIRuntime 是 actor,本调用与 LLM.generate() 自然串行,不会 OOM。
|
||||
|
||||
Reference in New Issue
Block a user