diff --git a/docs/Disaggregated/P5D-INPROC-PERFORMANCE.md b/docs/Disaggregated/P5D-INPROC-PERFORMANCE.md new file mode 100644 index 00000000..f25e0784 --- /dev/null +++ b/docs/Disaggregated/P5D-INPROC-PERFORMANCE.md @@ -0,0 +1,66 @@ +# P5d 报告:inproc 性能专项(2026-09-18) + +> 状态:**已落地两个提交**(`cb06538c` 一轮、`56a77348` 二轮,均在 `feat/disaggregated` 并已推送)。 +> 本报告回答三个问题:要做什么、做到了什么、什么没完成。 + +## 定位与目标 + +**插入点**:P5c 之后、P6 之前。路线图的既有纪律是"性能对着 pull 臂只记录、不作阻塞门,专门的优化阶段排在路线图推完之后"——但 P5c 收官后在真实负载上的实测把这件事提前了:FCL + Minecraft 26.3-rc-3、视距 12、Redmi `2f7cbe2e`,inproc 世界内只有 **7-13 fps**,monolith 同场景 **~116 fps**,约 9 倍差距,inproc 不可用。 + +**目标**:在不改变 wire 语义、不动正确性门(双车道、G1/G2/G5/G14、双生成器门)的前提下,把 `inproc` 与 `monolith` 的逐帧帧率差拉到尽可能接近 1。性能数字的采集纪律与其他阶段一致:reboot-clean、同热窗口配对 A/B、只用 Redmi `2f7cbe2e`,设备侧风扇开自动档;A/B 切换靠 `/sdcard/FCL/mg_transport.txt` 运行时开关,免 APK 重编。 + +## 起点的瓶颈链(全部实测,非推测) + +- persistent-map 推送:每个 verb 把每个映射的**全范围**推上线(实测 ~25 MB/帧,每 64 KB 块一条记录一次 barrier 往返)。 +- 脏检查:每 64 KB 块做内容哈希,**每个 draw 对全部 arena 全范围扫**——XXH64/XXH3 一度占整个进程 73%(绑定过滤后仍 32.8%)。 +- barrier:每条 verb 记录一次 `appliedSeq` 往返。 +- `Doorbell::Wait` 的自旋每次迭代读一次时钟——该设备上 `clock_gettime` 是真系统调用(无 vDSO),占进程 ~20%。 + +## 做了什么 + +### 一轮 `cb06538c`(15 文件,+1100/-25) + +- **persistent-map 推送改为三级臂,按 buffer 在 map 时挑选**: + - **mprotect 脏页追踪**(主臂):映射范围的页对齐内部注册 `PROT_READ`,应用写入触发 SIGSEGV,handler 在槽位位图上标脏页并放行,推送时只发含脏页的 64 KB 块、按连续段一次 mprotect 重新武装。归属判定三条,任一不满足即原样链给前一个 handler:只认内核 `SEGV_ACCERR`(用户态 raise 与未映射页不算)、必须在存活跟踪区间内、位图位鉴别重放(已置位页上的立即重 fault 只应答一次,再犯即非写访问,诚实崩溃)。对齐**向内收**——外向对齐的 edge 页含 malloc 元数据,信号被屏蔽的驱动/运行时 worker 线程 fault 即进程死亡(ctest 下 SmallRing 必崩、gdb 下必过的根因)。首推全量一次(与哈希臂 "fresh pushes everything once" 同规则)。 + - **哈希抑制臂**(`MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS`,默认 1):未追踪 buffer 的逐块 XXH3 对比,只推变化块。 + - **绑定过滤推送**(`PushDrawConsumers`):只推当前 draw/dispatch 实际消费的 buffer(VAO attribs/element、索引绑定点、indirect/param 槽、**TexBuffer 背存**——顺手修了高速飞行云的抽动,用户已确认)。 +- **barrier 批处理**(`MOBILEGL_IPC_BATCH_WAITS`,默认 1;`MOBILEGL_PIPE_VERIFY` 强制 0):无 reply slot 的 kCtxState/kCtxCso/kCtxObject 值类记录发布即返回,barrier 推迟到下一个拉取类 verb——R-1 的围栏柱不动,只减少往返。两个连带问题被钉死而非口头论证:kCtxObject 类记录依赖同步语义的测试加显式围栏(GenerateMipmap、FramebufferDeath 两条);批处理下才可能出现的"apply 线程成为前端对象最后一个引用"新增**延期销毁队列**(入队 `(kind, lifetimeId)` POD,GL 线程在 verb 钩子重放七个 DestroyAndFree helper)——没有它 CopyImageLayered 两条必炸 `Fatal{RoleViolation, "MGPipeSlots"}`。 +- **自旋时钟批处理**:`Doorbell::Wait` 的自旋改为每 64 次 yield 读一次时钟(clock_gettime 家族 ~20% → ~2%)。 + +### 二轮 `56a77348`(2 文件,+34/-11) + +一轮上设备后发现 epoch skip 从未生效:负载里总有子页 persistent map,任何"未追踪"成员都否决跳过,绑定游走照样每 draw 跑(~8%),而直接扫所有未追踪成员的替代方案实测更差(41-43 vs 58-62 fps)。 + +- **零页注册**:子页范围不再落哈希臂,注册为"空内部 + hasEdges"的追踪槽(不保护任何页),全范围由两个有界的边块哈希服务——不再否决任何跳过。 +- **shadow 页对齐分配**(`SHADOW_ALLOCATION_ALIGNMENT = 4096`;`GL_MIN_MAP_BUFFER_ALIGNMENT` 查询仍答 64):shadow 占有的每一页字节全是自己的,追踪器永远不必碰邻居分配的字节;边块与零页形态在真实负载中基本消失。monolith 共用该分配器,A/B 无影响。 + +## 数字(VD12,同场景,in-world 稳定段) + +| 阶段 | split (inproc) | monolith | pmap 线流量/帧 | 关键 profile 行 | +|---|---|---|---|---| +| 起点 | 7-13 fps | ~116 fps | ~25 MB | XXH64/3 73%→32.8%,clock_gettime ~20% | +| 一轮 `cb06538c` | 60-66 fps | 112-115 | ~0.23 MB | WaitForApplied 11.6%,绑定游走 ~8% | +| 二轮 `56a77348` | **63-65 fps** | 110-118 | ~0.23 MB | 绑定游走**消失**,XXH3 2.6%,clock ~2% | + +帧率比从 ~1:9 收敛到 ~1:1.7。云不闪了(TexBuffer 覆盖),画面与 monolith 无可见差异。 + +## 验证 + +- 双车道:unit **2188/2188**、integration-split **111/111** 全绿(期间唯一反复出现的一条红是 `GenerateMipmapDepthPixels` 的环境性 flake:其 DirectGLES/DirectVulkan **monolith** 兄弟变体同样 flaky,而本包全部代码路径在 monolith 下可证明惰性——`PushIsArmed` false、无 ClientSession、deferral 首行 monolith 早退)。 +- `gen_pipe.py --check` / `--self-test` 绿,生成文件与生成器一致。 +- 每处关键修复都有 red-once 形态的证据:SmallRing SEGFAULT(外向对齐)、GenerateMipmapFieldsCross exit 101(kCtxObject 免等)、CopyImageLayered `Fatal{RoleViolation}`(apply 线程末引用)、余数块计数 3 vs 2(边块未合并)、117 MB/帧(边块无条件推)。 + +## 什么没完成 + +1. **后端 sync 锁复合(最大遗留,~18-20% 进程时间)**:apply 线程自身约 **48%** 的时间在 mutex lock/unlock 上——`SyncNeccessaryBuffers` 系路径在每个 draw 对每个 buffer 拿 per-resource `pendingMutex`(外加 `g_poolMutex` / `g_deferredBufferReleasesMutex`),~900 draws/帧放大成毫秒级。monolith profile 里**没有**这个复合(同一份后端代码在单线程下跑)——是锁调用频率 × 流水线序列化的可见性,还是真实竞争,尚未定位到具体站点。这是下一轮的第一目标。 +2. **R-1 序列化(`WaitForApplied` ~11%)**:每个 draw 等 `appliedSeq` 的往返。根本解是 `gPipeInputs` 版本化/双缓冲——属于 **P11** 的既定设计范围,本阶段刻意不动。 +3. **小项**:`__emutls_get_address` ~3%(emutls 路径上的 thread_local 读);`SetHashSuppressor` 的 XXH3 ~2.6%(P5 遗留的 set 记录去重哈希,另一包的地界)。 +4. **验证债**: + - 79-trace 全集本机未跑(无此环境);Redmi 复测窗口未排。 + - x86 32 位的 `MGHostSpan` 32B 断言:本包设计是 arm64-only,未做 32 位验证。 + - FCL 侧测试接线**未提交**(按约定):`settings.gradle.kts` 的 `:MobileGL` 指向、`FCLauncher.java` 的 renderer env 注入(含 `mg_transport.txt` 开关,该文件在 git 里还处于未解决合并状态 UU,内容正常)、`MobileGL-disagg/build.gradle` 属性映射与 arm64 abiFilters、gradle wrapper 回退 8.13。插件 APK 构建路径里的 `MOBILEGL_TRANSPORT=inproc` 行在打插件包前必须拿掉。 +5. **平台可移植性备注**:mprotect+SIGSEGV 臂只覆盖 POSIX(Android/Linux 验证)。Windows 桌面若需要,应加 `GetWriteWatch` 实现臂而非 `#ifdef` 信号路径;哈希臂作为全平台兜底永久保留。handler 已按 JVM 隐式空指针检查的要求链式转发非己 fault。 + +## 下一轮的入口 + +按收益排序:后端锁复合(pendingMutex 调用站点定位,先插一次性计数器确认频率,再决定是去锁、合并锁还是改 dirty 探测粒度)→ `SetHashSuppressor` 哈希 → emutls 热点。R-1 序列化不动,等 P11。 diff --git a/docs/Disaggregated/ROADMAP.md b/docs/Disaggregated/ROADMAP.md index 780810c0..55f4ee68 100644 --- a/docs/Disaggregated/ROADMAP.md +++ b/docs/Disaggregated/ROADMAP.md @@ -1,6 +1,6 @@ # MGPipe 路线图 -> 状态:**P0、P0.5、P1、P2、P3a、P4a、P5、P5b、P5c 已收官**(2026-09-17,代码头 `b88e8487`)。当前头、逐门数字、开放项与下一步见 **[`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)**;设计见 `ARCHITECTURE.md`;逐阶段实测见 `MEASUREMENTS.md`。**下一个是 P6 spawn transport**(此时只是传输替换)。 +> 状态:**P0、P0.5、P1、P2、P3a、P4a、P5、P5b、P5c、P5d 已收官**(P5c:2026-09-17,代码头 `b88e8487`;P5d:2026-09-18,代码头 `56a77348`)。当前头、逐门数字、开放项与下一步见 **[`CURRENT_STAGE_PROGRESS.md`](CURRENT_STAGE_PROGRESS.md)**;设计见 `ARCHITECTURE.md`;逐阶段实测见 `MEASUREMENTS.md`;P5d 报告见 [`P5D-INPROC-PERFORMANCE.md`](P5D-INPROC-PERFORMANCE.md)。**下一个是 P6 spawn transport**(此时只是传输替换)。 ## 通用纪律(每个 commit) @@ -27,6 +27,7 @@ | **P5** 传输 + inproc applier + 发射表 | ✅ `ff2994d9..37fc4fdb` | 同一 codec 上的 `InProcessTransport`;四种 build flavour;发射表三类 / caps mirror / reply mailbox;apply-thread context 终身持有;lockstep verb barrier;tight ReadPixels;persistent-map 块推送;G8 字段归属生成器;split 测试 / CI / APK 车道;八包(c0 契约、w1 codec、s1 session、p1 归属、b1 persistent map、t1 车道、c1 路由、v1 server)+ j0 / x2 / v1-r3 收尾 | joint:OpenRA inproc 2/2 SSIM 1.0;`integration-split` 21/21;E1 14/14 红;G1 0/0/0/0。收尾头全门在 E3(a) 停止(设计性 skip 被判红),Part 2/4 未到达;收官审查 1 blocker / 10 major / 2 minor → P5b r1/r2。§6 | | **P5b** `inproc` verb migration | ✅ `37fc4fdb..82683d4a` | class-C 普查决定顺序;d1 19 / i1 7 / t2 6 / f1 11 槽迁移;sync 五槽;具名 blit;GLES mip descriptor;r1 / r2;收官审查三项修复。发射表 A=2 / B=54 / C=15 | 主机全门 `348d22a4` complete;`integration-split` 107/107;合并普查零回退、79 trace 72/6/1;**Redmi 正确性 8/8 + 四臂 A/B、barrier tax 首测**。§7 | | **P5c** `inproc` 共享内存读点归零 | ✅ `11ac3de6..b88e8487` | server 端纹理 staged shadow(`StagedTextureStore`,句柄为键、整段覆盖、defined-ness 追踪);`SEG_EVENT` 三个 producer + `kEventGlError`(反向通道零裸指针);sink / twin 按记录句柄解析(blit 具名臂经 verb 句柄工作区,Magma 走 G6 消费臂);`applier_reset` / `object_death` 控制记录(opcode 77/78,framebuffer 首个 wire delete);`set_context_values`(79)退役全部值类 BARRIER-PULLED 行,三 shutter 自答;双层 `Fatal{RoleViolation}` 守卫 + `InBarrierWait` 接线;两个具名豁免 scope(通告家族 P4b/P7、G6 registry 家族 P3b/P4b)承接未到期的债 | 角色守卫开启下 unit 2187/2187(strict 下同绿)、`integration-split` 111/111、每层守卫 red-once 按名变红;纹理 `0xDD` audit 在 bsl / complementary 两条 in-world trace 零 Fatal;rsp 按帧实测(bsl 948.5/帧,残留全为钉住的对象类 15 行);G1 三个认定 resize(SwapchainObject::Create / CopyTexSubImage2D / ScopedRestartIndexSubstitution,均已具名)+ `.text` −16 B、pull 构建零 MG_Remote 符号;G5 保护区字节一致;双生成器 --check/--self-test 绿 | P5b | +| **P5d** `inproc` 性能专项 | ✅ `cb06538c`、`56a77348` | persistent-map 推送三级臂(mprotect 脏页追踪:SIGSEGV 三重归属判定 + 内收页对齐 + 边块哈希 + 首推全量;哈希抑制 `MOBILEGL_IPC_PERSISTENT_HASH_SUPPRESS`;绑定过滤 `PushDrawConsumers` 含 TexBuffer——云闪修复);barrier 批处理 `MOBILEGL_IPC_BATCH_WAITS` + 延期销毁队列 + 两条测试围栏;`Doorbell::Wait` 自旋时钟 64 合 1;零页注册 + shadow 页对齐分配(`SHADOW_ALLOCATION_ALIGNMENT=4096`,GL 查询仍 64);epoch skip 稳态消掉绑定游走 | VD12 Minecraft(Redmi,FCL)split 7-13 → **63-65 fps**,monolith 110-118 同场景(~1:9 → ~1:1.7);pmap 25 MB/帧 → 0.23 MB/帧;unit 2188/2188、`integration-split` 111/111;双生成器绿。未完成:后端 sync 锁复合(apply 线程 ~48% 在锁上)、R-1 序列化(P11)、emutls / set 去重哈希小项。报告:[`P5D-INPROC-PERFORMANCE.md`](P5D-INPROC-PERFORMANCE.md) | P5b | | **P6** spawn transport | P5c 之后 | `SocketTransport`(socketpair + fork/execve,envp 剔除 + 强制 monolith 双保险);`ServerMain`;`MOBILEGL_IPC_SERVER_PATH` + `dladdr` 兜底;有界重试握手;EOF 即时退出;device-lost latch;EGL forwarder 的控制面帧;P5c 留下的 `s_synced` / `g_syncedRenderStateParameters` 按 context 世代重置 | P5b 的完整渲染路径在 `spawn` 下绿;进程树只多一个子进程;`HeadlessGL` fork 预检无孤儿;OpenRA 在 Adreno 830 上 split SSIM ≥ 0.99 | | **P3b / P4b** 深化(Espryt) | 待排 | 按存储属主键控的发射游标与 view 索引重映射;`g_fboTextureSyncList`;`ResolvedTextureBindingMemo` / `SamplerPassMemo` / image sweep / program registry 重键;XFB scatter 搬到 client;删 fragColor 重推导 workaround 与 `g_broadcastMemo*`;raw-depth-fetch sampler 原生化;回读 / pack state;P4a 记下的 R-3 / R-4 / R-5 / R-7 / R-10 / R-11;`ProgramArtifacts.h` 的 NDK 尺寸钉 | 纹理 / program 场景;CTS `texture_*` / `shader_image_*` / `packed_pixels` 在 0.5 pp 内;每一个 Iris trace;`TextureUploadShapeScenario` 升级成门 | | **P7** DirectVulkan(Magma)全量迁移 | 待排 | 其余 10 个子系统:`SetupDrawSnapshot` 探测字段塌成 dirty mask;占位纹理原生化;具名 UBO host payload(D-B8);内部 shader 烘焙;`VertexInputStateFactory` 内容寻址 CSO;D18 容器纪律保留 | 集成 + trace 在 Magma 的 push 与 split 下全绿;verify 零分歧;`nm -D libMobileGLServer.so \| grep glslang` 为空;CTS 0.5 pp 内。**再基线检查点:中点完成子系统 < 40% 立即重定基线** |