Files
HomeAgent/internal/agent/api/codec_chunkfast_bench_test.go
JianFeeeee b850feaef2 feat: 段页式记忆流转、coldcontext 重命名与安全治理
记忆层:
- MemoryBlock 承载 FrameTS/PageSeq/PageKind/BlockRole/CallID
- coldcontext 归档与回填按完整页传递,工具调用参数和配对键不丢失
- 召回先估算总 token,超预算时按全局页相似度标记 inactive
- inactive 仅是 context 渲染期状态,不落盘、不进入 coldcontext,Prune 后重置
- Peek/确认/Remove 两阶段召回,失败保留冷上下文副本
- stablemem 记录 last_recalled_at,删块连带清理边

命名:
- internal/memory/document → internal/memory/coldcontext
- Doc/DocEvent/DocHit → ColdDoc/ColdEvent/ColdHit
- ContextToColdContext/RecentColdContexts/FindColdContexts
- 保留既有 JSON 标签、doc_ 文件前缀与 doc_count 状态键,兼容存量数据

安全治理:
- 工具策略门禁、安全配置、事件总线发布者身份与入站脱敏
- 声明式 SecurityAPI 与 QQ owner/guest profile
- 日志保留策略与相关 e2e/回归判据

验证:go build ./...、go vet ./...、go test ./...(62 包);
生产副本 e2e:7013 块迁移后 ID/向量/指纹 0 变化、0 缺失。
2026-10-29 23:53:48 +08:00

84 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build cgo
package api
// codec_chunkfast_bench_test.go —— 快速路径 vs 原实现的真实开销对比。
//
// 判据不是「C 比 Go 快」,而是「在真实输入分布下是否真的省下分配与时间」。
// 分配数是重点:C 化的原始动机就是消除每 chunk 12~21 次堆分配带来的 GC 抖动。
import (
"testing"
)
var benchChunks = map[string]string{
"content_zh": `{"id":"chatcmpl-abc","object":"chat.completion.chunk","created":1727000000,"model":"deepseek-v4.1-flash","choices":[{"index":0,"delta":{"content":"这是一段来自真实流式响应的中文内容,用于测量解析开销。"},"finish_reason":null}]}`,
"content_ascii": `{"id":"chatcmpl-abc","choices":[{"index":0,"delta":{"content":"hello world this is a longer ascii content chunk"},"finish_reason":null}]}`,
"toolcall": `{"id":"chatcmpl-abc","object":"chat.completion.chunk","created":1727000000,"model":"deepseek-v4.1-flash","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_9a","type":"function","function":{"name":"stablemem_recall","arguments":"{\"query\":\"用户偏好\",\"limit\":20}"}}]},"finish_reason":null}]}`,
"usage": `{"id":"chatcmpl-abc","object":"chat.completion.chunk","created":1727000000,"model":"deepseek-v4.1-flash","choices":[{"index":0,"delta":{"content":""},"finish_reason":null}],"usage":{"prompt_tokens":3821,"completion_tokens":117,"total_tokens":3938,"prompt_cache_hit_tokens":3584,"prompt_cache_miss_tokens":237}}`,
"finish": `{"id":"c","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}`,
}
// BenchmarkChunkFast_Entry 走真实入口(含 C 路径 + 必要的回退)。
func BenchmarkChunkFast_Entry(b *testing.B) {
for name, in := range benchChunks {
b.Run(name, func(b *testing.B) {
b.SetBytes(int64(len(in)))
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = parseOpenAICompatibleStreamChunkFull(in)
}
})
}
}
// BenchmarkChunkFast_GoOnly 直接调原实现(整块 json.Unmarshal),作对照。
func BenchmarkChunkFast_GoOnly(b *testing.B) {
for name, in := range benchChunks {
b.Run(name, func(b *testing.B) {
b.SetBytes(int64(len(in)))
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = parseOpenAICompatibleStreamChunkFullGo(in)
}
})
}
}
// ---------------------------------------------------------------------
// 基准门禁:防止「优化」悄悄退步,或在没实测过收益时被打开
// ---------------------------------------------------------------------
// TestChunkFast_BenchGate 钉死当前事实:chunkFastEnabled 必须为 false。
//
// ★ 为什么把「一个优化是关的」也做成断言:
//
// 「还没验证有效就先关着」是**容易丢失的状态** —— 后来者看到
// 「快速路径写得挺全 + 6 万条差分测试全过」,很自然会以为它已生效,
// 进而打开它、甚至删掉开关。而实测它**更慢**。
// 断言把这个事实钉在测试里,开关一旦被改就立刻判红。
func TestChunkFast_BenchGate(t *testing.T) {
if chunkFastEnabled {
t.Fatalf("chunkFastEnabled 被打开了,但实测本架构比原实现慢:\n" +
" content_ascii Entry 2016ns/20allocs vs GoOnly 1325ns/13allocs\n" +
" toolcall Entry 5854ns/33allocs vs GoOnly 3270ns/21allocs\n" +
" 根因:5+ 次 cgo 边界 × 每次约 200ns(out-param 逃逸到堆)。\n" +
" 改造方向(已由天花板实验确认可行):一次 C 调用返回全部字段 span、\n" +
" 结果写入调用方栈上的 C 结构体。先改架构,再打开此开关。\n" +
" 改之前请先跑 BenchmarkChunkFast_* 拿到自己的数据。")
}
}
// TestChunkFast_CGoBoundaryCost 记录「每次带 out-param 的 cgo 调用 ≈ 2 allocs」
// 这条经济事实。它是判断任何后续改造是否值得的标尺。
func TestChunkFast_CGoBoundaryCost(t *testing.T) {
// 断言存在(防止有人「顺手优化」掉这两个 helper 里的关键细节)
doc := `{"a":1,"b":{"c":"x"}}`
if _, found, _, bad := findKeyCI(rootSpan(doc), "b"); !found || bad {
t.Fatalf("findKeyCI 失效: found=%v bad=%v", found, bad)
}
if _, found, _, bad := findKeyCS(rootSpan(doc), "a"); bad || !found {
t.Fatalf("findKeyCS 失效: found=%v bad=%v", found, bad)
}
}