mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-15 12:15:16 +00:00
perf(memory): Introspect 507ms→11ms、ListPersons 130ms→3ms
继续清「非常卡」。上次修完 pulse 端点后,`/kernel` 与 `/runtime` 仍各约
600ms(每 15s 一次)。这次**逐项量**而不是猜(临时探针量各子收集器):
memDB.Introspect 507ms ← 主凶
social.ListPersons 129ms
textMem.FileCount 0.1ms
docStore.Stats 0.0ms
knowledge.List/Stats 0.0ms
## ① Introspect 507ms → 11ms(43×)
hotspots 的度数写成**相关标量子查询**:
COALESCE((SELECT COUNT(*) FROM memory_block_edges e
WHERE (e.source_id = b.id OR e.target_id = b.id) ...), 0)
EXPLAIN 是 `SCAN b`(3364 块)→ `CORRELATED SCALAR SUBQUERY` → `SCAN e`
(2829 边),即 **3364 × 2829 ≈ 950 万次行扫描**,且 ORDER BY 前必须算完
全部块的度数。
改成边表按端点 UNION ALL + GROUP BY 聚合一次,再 LEFT JOIN 回块表。
边表只扫两遍而不是 3364 遍。**新旧查询输出逐字节一致**(已 diff 验证)。
## ② ListPersons 130ms → 3ms(38×)
它走 `Recall(nil, nil, 1, "")`,而那条 full-recall 路径为了填
`result.Entities` 会加载**全部块(blockColumns 含稠密向量)**:
Recall 全程 130ms:实体 3266 个、关系 30 条
即为了 30 条关系反序列化了 3266 个块的向量,而 ListPersons
**只看 result.Relations**。
新增 `GraphDB.RecentRelations(limit)`:与 full-recall 分支里的关系子查询
**逐字对齐**(同一 WHERE/ORDER/LIMIT,含 trait 边——过滤掉它会让特质
主体漏出人物表)。实测该查询单独 4ms。
等价性验证:新旧路径的人物列表 + 关系集**逐字节 diff 完全一致**
(56 人、同样的边)。旧路径输出是先 git stash 采样的,不是凭记忆比对。
## 验证
- `TestIntrospect_HotspotsQueryIsNotCorrelated`(源码形状)+
`TestIntrospect_LargeGraphIsFast`(2000 块/1500 边的耗时门)。
**两者各自独立可杀变异**(实测):
- 源码断言:退回相关子查询 → FAIL「必须用一次聚合」
- 耗时门:同变异 → FAIL「149ms 超过 60ms 门槛」(修复后 11ms)
为何源码断言要锚在返回值行而非 `Index`:注释里也提到 memory_hotspots,
用 Index 会截到注释处、取到的片段根本没有查询——第一版就这么写错了,
自己先红了一次才发现。
- 全仓 `go test ./... -count=1` → 48 包 ok / 0 FAIL
This commit is contained in:
@ -317,13 +317,22 @@ func (s *SocialStore) ListPersons() ([]string, error) {
|
||||
//
|
||||
// ★ 保留一个回退:若图里一条边都没有(纯空库),
|
||||
// 返回空列表而不是全部 —— 那才是「没有人物」。
|
||||
result, err := s.db.Recall(nil, nil, 1, "")
|
||||
// ★ 改走 RecentRelations(2026-10-08 修 130ms)
|
||||
//
|
||||
// 原实现用 Recall(nil, nil, 1, ""),而那条 full-recall 路径为了填
|
||||
// result.Entities 会加载**全部块(含稠密向量)**。生产实测:
|
||||
//
|
||||
// Recall 全程 130ms:实体 3266 个、关系 30 条
|
||||
//
|
||||
// 即为了 30 条关系白付了 3266 个块的向量反序列化。而本函数只看
|
||||
// result.Relations。改成只取关系(同一 WHERE/ORDER/LIMIT,4ms)。
|
||||
rels, err := s.db.RecentRelations(30)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seen := make(map[string]bool)
|
||||
for _, r := range result.Relations {
|
||||
for _, r := range rels {
|
||||
if strings.HasPrefix(r.RelationType, traitPrefix) {
|
||||
// 特质:源是主体(人物),目标是特质值
|
||||
seen[r.SourceName] = true
|
||||
|
||||
Reference in New Issue
Block a user