Files
Workspace/Docs/Performance/2026-09-28.md
T
cneicy 08246ddb01
Validate ShrinkSDK Workspace / catalog (push) Successful in 5s
Validate ShrinkSDK Workspace / unity (push) Successful in 3m20s
feat: integrate performance upgrade and agent inspection tooling
2026-09-29 10:16:02 +08:00

6.4 KiB
Raw Blame History

性能与跨宿主验收 — 2026-09-28 UTC

本次为本地工作区验收,不代表远程发布、生产容量承诺或所有后端的性能保证。原始测量时间保留在 JSON/XML 中;Windows 本地显示日期可能为 9 月 29 日。

方法与证据边界

  • Windows x64;Unity 2022.3.62f3 的实际 Standalone Player,而非用 EditMode 冒充 Player;另有 .NET 8.0.31、.NET 10 和 Godot 4.6.3 Mono 的实际运行。
  • 网络矩阵:64 B/1 KB/32 KB 负载参数,1/32/256 会话;RPC、广播、慢消费者、突发队列。Loopback 会话不是 OS socket;TCP 文件是实际本机 socket,且 RPC 采用串行往返采样,不等于高并发容量压测。
  • Context:10/100/1000 组件依赖链,10,000 次创建退役;旧实现来自 Context 子模块提交 b93f3e61223e7572588b67c0d3935073817f0da6,不是重新编写的假想基线。
  • 存档:100 KB/1 MB/10 MB,前台/后台编码;.NET 每组合 10 次并覆盖加密,Player 记录真实阶段耗时与采集线程。输入主要为字符串快照,不能推断复杂游戏对象的采集成本。
  • 单机开发环境、有限样本,非隔离实验机;p99 易受 JIT、GC、系统调度影响。没有可靠的多轮置信区间,不将小幅波动称为普遍提升。
  • Unity 的 GC.GetAllocatedBytesForCurrentThread 对已知分配返回零,已弃用该结果。Player 改用经已知数组分配校准的 GC.Alloc 事件计数;事件次数不是 B/op。独立 .NET 的字节计数与 Player 分开报告。

原始数据位于 Data/2026-09-28;Player 完整输出同时保存在 XML 与 player-output.txt。工具、测试源代码保留在 Tools/AgentSupport 与 Assets/AgentSupportTests。

已量化的核心变化

Context(.NET 8)

组件数 旧诊断 p95 µs 新诊断 p95 µs 旧 B/op 新 B/op
10 9.4 11.7 10,456 7,312
100 538.2 116.9 108,016 73,456
1000 7,426.4 548.3 1,083,616 735,328

大规模诊断通过按依赖键访问 provider 候选索引获益;10 个组件的 p95 本轮反而略高,不能宣称所有规模都更快。10,000 次退役后旧运行时持有 10,000 个 fiber,新运行时为 0,诊断历史有界 64。弱引用测试验证实例可回收;整进程 GC 差值可能为负,不将其解读为精确节省字节数。

深链生命周期改为迭代队列展开,避免 1000 层同步完成/撤回造成栈溢出;不改变依赖排空与效应撤回顺序。

网络封装(.NET 8)

负载参数 旧 JSON 外封装 B/op 新 v2 外封装 B/op
64 B 3,504 32
1 KB 10,560 32
32 KB 230,328 32

这里只比较已经编码的消息体如何封装,不是端到端零分配。新封装取消外层 JSON/Base64,消息直接写入池化 buffer;仍有 writer 对象、消息反序列化、排队和 transport 的独立成本。JSON 与生成式 MessagePack 的完整消息编码对照见原始 rows,不能套用此表的 32 B。

队列突发测试输入 10,000 个显式可替换状态,32 个 session 分区最终保留 32 项、32 KB,返回 9,968 次 Replaced;一次 4 KB budget 泵送完成 4 项。可靠操作不使用这种替换机制,满队列显式拒绝。

实际 TCP 与后端选择

256 个本机 TCP 连接、1 KB、256 次串行 RPC 往返:

运行时 p50 µs p95 µs p99 µs ops/s
.NET 8 99.3 258.9 355.1 6,911
.NET 10 88.4 250.8 384.3 7,392

其他参数组合有升有降;本轮未证明目标场景稳定达到 15% 收益,因此不引入新的默认 .NET 10 专属服务端依赖。Pipelines 仅有内存 buffer roundtrip 微实验,未实现等价 socket 后端对照,不足以采纳。共享运行时继续 netstandard2.1。

TCP 慢消费者测试证明有界队列和拒绝反馈,不证明业务消费成功:该突发负载使用 RPC 消息的一次性发送路径,记录中存在 DispatchMissCount;不能将该行视为完整 handler 吞吐测试。可靠队列行为、会话隔离、取消和故障另由功能测试验证。KCP 未作独立吞吐结论。

EventBus 与存档

.NET 8 同步 Post、同步完成的异步 Post 为 0 B/op;注册/撤回 224 B/op,按需绑定诊断 120 B/op。同步路径的捕获闭包已移出快路径;Unity 的结构体事件空值检查另外增加值类型短路,避免 Mono 装箱。

校准后的 Player 实测:独立 Bus 10,000 次同步发布为 0 次分配事件;当前默认 Bus 为 10,000 次分配事件。修复值类型空值检查前默认通道是 20,000 次,不能将早先字节 API 的零值作为基线。

默认全局 Bus 可能带网络桥或诊断观察器;接口观察器需要结构体装箱,不能把独立 Bus 的零分配结果扩大到所有配置。Player 同时测量默认与独立通道,并为引用类型空值保留异常检查。

存档采集仍在调用线程,后台开关仅移动脱离宿主对象的快照编码与加密。示例 .NET 8 的 10 MB 未加密平均编码为前台约 7.68 ms、后台约 8.21 ms;主要收益是主线程工作转移,而非宣称总保存时间必然缩短。Player 的冷启动与各阶段实测见 player-output.txt;未隐藏首轮高耗时。格式、原子落盘、取消时备份保留及损坏回退均有测试。

复现

在已还原依赖的仓库运行:

pwsh Tools/AgentSupport/Benchmarks/Prepare-Baseline.ps1
dotnet run --project Tools/AgentSupport/Benchmarks -c Release -f net8.0 -- Artifacts/AgentSupport/benchmark-net8.json
dotnet run --project Tools/AgentSupport/Benchmarks -c Release -f net10.0 -- Artifacts/AgentSupport/benchmark-net10.json
dotnet run --project Tools/AgentSupport/Benchmarks -c Release -f net8.0 -- --tcp Artifacts/AgentSupport/tcp-net8.json
dotnet test Tools/AgentSupport/RuntimeTests
dotnet test Tools/AgentSupport/Shrink.Inspect.Tests

Unity 用 TestRunner 的 StandaloneWindows64 Player 运行 ShrinkSDK.AgentSupport.PlayerTests;Editor 辅助入口 PlayerAcceptance.Run() 将本次结果保存到 Artifacts/AgentSupport/unity-player-results.xml。基线脚本读取子模块 HEAD:以后 HEAD 前进时,应显式使用上述旧提交重建对照,不把新的 HEAD 当成本报告基线。

最终功能验收、消费工程已知失败和导航覆盖范围见 AgentSupportAcceptance;协议和回退见 迁移说明。