mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 09:28:14 +00:00
- docs/zh/架构迁移评估.md: C ABI→子进程+共享内存完整迁移论证(1621行) - docs/zh/experiments/: 18项可复跑可行性实验(架构评估的所有数字来源) - plan.md §11: 11.1~11.9 插件架构缺陷修复清单(唯一权威编号) - main 保持干净,本批次为 update 特性分支的整改起点
112 lines
5.0 KiB
Markdown
112 lines
5.0 KiB
Markdown
# 插件架构评估实验
|
||
|
||
[`../../架构迁移评估.md`](../../架构迁移评估.md) 中所有数字的来源。
|
||
**18 项实验,一键复跑**,用于复核结论或在改动后验证回归。
|
||
|
||
```bash
|
||
./run.sh # 跑全部(约 3-5 分钟)
|
||
./run.sh 12 13 # 只跑指定实验
|
||
./run.sh 1 1c # dlclose/NODELETE 组
|
||
```
|
||
|
||
依赖:`go >= 1.21`、`gcc`、Linux(用到 `eventfd`/`memfd_create`/`dlopen`)。
|
||
脚本在 `mktemp -d` 里构建,**不污染主仓 `go.mod`**;实验源码均带 `//go:build ignore`。
|
||
|
||
拉取 `golang.org/x/sys` 需要网络(实验 1/2/4/8/10)。本机走 clash:
|
||
```bash
|
||
export HTTPS_PROXY=http://127.0.0.1:7890 HTTP_PROXY=http://127.0.0.1:7890
|
||
```
|
||
|
||
## 目录
|
||
|
||
| 目录 | 主题 | 对应章节 |
|
||
|---|---|---|
|
||
| `01-dlclose-nodelete/` | `dlclose` 对 `DF_1_NODELETE` 是 no-op | 1.1 / 1.2 |
|
||
| `02-feasibility/` | 新架构可行性 11 项 | 第七章 |
|
||
| `03-lost-update/` | 副本模型的 lost update | 8.4 / 8.6 |
|
||
| `04-cgo-uninterruptible/` | cgo 调用不可中断 | 9.3 |
|
||
|
||
## 实验清单与最近一次实测结果
|
||
|
||
复跑于 2026-08-31,go1.25.12 linux/amd64,192.168.2.60(12 核)。
|
||
|
||
### 01 组:dlclose / NODELETE
|
||
|
||
| # | 实验 | 结论 |
|
||
|---|---|---|
|
||
| 1a | Go 宿主经纯 C shim 加载/卸载第三层 `.so` | 纯 C 目标可卸载;Go c-shared 目标仍不可 |
|
||
| 1b | `/proc/self/maps` 段数验证 | 纯 C: 5→**0**(真卸载);Go c-shared: 5→**5** |
|
||
| 1c | 版本化路径 dlopen | handle 不同,`ver=v2` 生效(方案可行但泄漏,已否决) |
|
||
|
||
**关键**:`DF_1_NODELETE` 属于**被卸载对象自身**的 ELF 属性,
|
||
与谁调用 `dlopen` 无关——套任何层数的 C 中间件都绕不过去。
|
||
|
||
### 02 组:新架构可行性
|
||
|
||
| # | 实验 | 最近结果 |
|
||
|---|---|---|
|
||
| 1 | eventfd 是否走 Go netpoller | 200 goroutine 阻塞 → 线程 **+0~1** ✅ |
|
||
| 2 | 跨进程 eventfd + 偏移解引用 | 父子 mmap 基址不同,偏移仍正确;post **10.9 µs** |
|
||
| 3 | 锁仲裁 RPC 往返成本 | **19.4 µs/次**(20000 次) |
|
||
| 4 | post-and-forget vs 同步 Publish | 5.07s → 2.29ms(**2218x**) |
|
||
| 5 | 17 子进程常驻开销 | **29.1MB RSS / 12.9MB PSS**,84 线程 |
|
||
| 6 | 子进程崩溃隔离 | 退出码 **2**,EOF **2.5ms** 感知,宿主存活 |
|
||
| 7 | 子进程热重载 | 同路径替换二进制 → v1→v2 立即生效 |
|
||
| 8 | **跨进程并发改写 StageContext** | 5 进程 × 300 轮,**零丢失零撕裂** |
|
||
| 9 | 持锁进程崩溃自愈 | 无死锁,**无需 robust mutex** |
|
||
| 10 | 二进制零拷贝 | 100KB/1MB/5MB → **14-22x**,体积 −100% |
|
||
| 11 | 工具调用 RPC 延迟 | p50 **19.6 µs**,占 LLM 往返 0.00065% |
|
||
|
||
### 03 组:副本模型缺陷
|
||
|
||
| # | 实验 | 最近结果 |
|
||
|---|---|---|
|
||
| 12 | 副本模型 lost update 率 | 内置 **0%** vs 外部 **35.8~36.8%** |
|
||
| 13 | 现网 sanitizer+weather 冲突 | **1.6~4.3%** 清洗结果被覆盖 |
|
||
|
||
**实验 12 的对照设计是重点**:两组用**完全相同的并发扇出**
|
||
(`stages.go:124` 的 `go func` + `wg.Wait()`),唯一差异是
|
||
「共享同一 `*StageContext`」vs「快照-副本-写回」。
|
||
|
||
内置组 0% 证明**并发扇出这个原始设计是正确的**;
|
||
副本组 36% 证明**跨 C ABI 边界后锁语义失效**才是缺陷所在。
|
||
不要据此得出"应该取消并发"的结论。
|
||
|
||
⚠️ **13 的比率随机器负载波动**(观测区间 1.6%~4.3%)——它取决于两个插件
|
||
handler 的实际执行耗时比。文档正文引用 1.6% 是首次测量值,
|
||
**应理解为「量级在百分之几」而非精确常数**。
|
||
|
||
### 04 组:cgo 不可中断
|
||
|
||
| # | 实验 | 最近结果 |
|
||
|---|---|---|
|
||
| 14a | cgo 死循环 vs 子进程 Kill | cgo 泄漏;子进程 **零泄漏** |
|
||
| 14b | 泄漏增长曲线(20 次) | 泄漏 **20 goroutine / 18 OS 线程**,线性 |
|
||
|
||
## 复跑时的注意事项
|
||
|
||
**结果会有波动,以下属正常**:
|
||
|
||
- 实验 12/13 的丢失率随调度波动(12 稳定在 35~37%,13 在 1.6~4.3%)
|
||
- 实验 1 的线程增长为 0 或 1(取决于 netpoller 线程是否已存在)
|
||
- 实验 10 的加速比 14~22x(受 CPU 缓存状态影响)
|
||
- 实验 5 的 PSS 受同机其他 Go 进程影响(共享页计算)
|
||
|
||
**结果不应变的**(若变了说明环境或结论有问题):
|
||
|
||
- 实验 1b 中纯 C `.so` 的段数必须归 **0**,Go c-shared 必须**不归零**
|
||
- 实验 8 的「总字符数 == 最终长度」必须成立(零丢失)
|
||
- 实验 9 必须无死锁
|
||
- 实验 12 的内置模型必须 **0%**
|
||
- 实验 14b 的泄漏必须**线性增长**
|
||
|
||
## 已知限制
|
||
|
||
- 实验 8 的 arena 未实现压实,64KB 用尽即停止写入(写入次数 < 5×300 属预期,
|
||
见评估文档 3.3)
|
||
- 实验 12/13 是**链路复刻**而非直接调用生产代码,
|
||
证明的是「副本模型这一机制」存在缺陷,不能替代对 `sanitizer`/`weather`
|
||
的真实行为回归测试
|
||
- 实验 5 的插件是最小 stdio loop(2.68MB),真实插件(如 qq 7.5MB)开销更高
|
||
- 无 Windows 环境,9.2 的 Windows DLL 缺陷**未经实测**,仅代码阅读
|