3253b221 by 柴进

:bug: 干掉 HistoryManager 内部两条 O(N) 全扫 hot path (Mac 仍闪退根因)

第二阶段发布后用户日志显示: 启动 8s 后 5s 内连续 5 次
load_history_index 全扫 513 条 + 513 次 stat, 主线程累计阻塞,
随后日志戛然而止 (典型 jetsam SIGKILL 指纹).

定位到两条阶段 1+2 没修的 hot path:

1. get_history_item(timestamp): 点击历史项查看详情时触发,
   原走 load_history_index() + 线性查找. 改为直接调
   load_history_item_fast() (读单个 metadata.json), O(1).

2. _update_history_index(history_item): 每次生成完图都调,
   原走 load_history_index() 全扫 + from_dict + 路径修正 + 整表写回.
   改为直接对 raw json list dict 操作, 无 stat / 无 from_dict.

改完之后 hot path 完全不再触发 _fix_history_path 循环.
1 parent 57047eb9
......@@ -1006,19 +1006,14 @@ class HistoryManager:
return []
def get_history_item(self, timestamp: str) -> Optional[HistoryItem]:
"""获取指定时间戳的历史记录项
"""获取指定时间戳的历史记录项
Args:
timestamp: 时间戳
Returns:
历史记录项,如果不存在则返回None
以前实现走 load_history_index() 全扫 + 路径修正,每次 O(N) + N 次 stat。
在 Mac 用户 513 条历史的环境下,点击历史项查看详情会触发主线程
阻塞 ~60ms; 连续点击会累积内存峰值并触发 jetsam SIGKILL。
现改为直接读 {timestamp}/metadata.json + 同目录扫描,O(1)。
"""
history_items = self.load_history_index()
for item in history_items:
if item.timestamp == timestamp:
return item
return None
return self.load_history_item_fast(timestamp)
def load_history_item_fast(self, timestamp: str) -> Optional[HistoryItem]:
"""轻量读取单条历史记录:直接从 {timestamp}/metadata.json + 文件扫描。
......@@ -1082,18 +1077,35 @@ class HistoryManager:
return False
def _update_history_index(self, history_item: HistoryItem):
"""更新历史记录索引
"""更新历史记录索引(每次生成完图片都会调用,hot path)。
Args:
history_item: 要添加的历史记录项
老实现走 load_history_index() 全扫 + 路径修正 + N 次 stat,再
整个 list 反序列化 + 重新写回。513 条历史时主线程阻塞 ~60ms,
Mac 上累计触发 jetsam SIGKILL。
现改为直接对 raw json 列表 dict 操作:读->过滤->插首位->写。
无 stat、无 from_dict、无路径修正。
"""
history_items = self.load_history_index()
try:
if self.history_index_file.exists():
with open(self.history_index_file, 'r', encoding='utf-8') as f:
raw = json.load(f)
if not isinstance(raw, list):
raw = []
else:
raw = []
except Exception as e:
self.logger.error(f"_update_history_index 读取索引失败: {e}")
raw = []
# 检查是否已存在相同时间戳的记录,如果存在则替换
history_items = [item for item in history_items if item.timestamp != history_item.timestamp]
history_items.insert(0, history_item) # 插入到开头
new_ts = history_item.timestamp
raw = [d for d in raw if isinstance(d, dict) and d.get('timestamp') != new_ts]
raw.insert(0, history_item.to_dict())
self._save_history_index(history_items)
try:
with open(self.history_index_file, 'w', encoding='utf-8') as f:
json.dump(raw, f, ensure_ascii=False, indent=2)
except Exception as e:
self.logger.error(f"_update_history_index 写入索引失败: {e}")
def _save_history_index(self, history_items: List[HistoryItem]):
"""保存历史记录索引到文件
......