.NET 性能工程
在调查、解释、修复、基准测试或验证 .NET 应用程序(包括 ASP.NET Core、服务、控制台应用程序、Avalonia、WPF、WinUI、MAUI、Skia、WebGPU、游戏/图形引擎、NativeAOT 以及原生互操作工作负载)的性能时使用此技能。
此仓库定义了一个技能。SKILL.md 是策略和路由入口点;详细的操作过程存在于一个分层参考树中。仅加载当前调查所需的索引和章节,然后在证据跨越所有权边界时展开。
不可协商的规则
- 对优化构建(通常为
Release)进行分析和基准测试,且采用真实部署形式。 - 记录提交状态和脏状态、SDK/运行时、操作系统、体系结构、硬件、电源模式、工作负载、工具版本、环境覆盖以及确切的命令。
- 在修改代码之前建立确定性复现。
- 分离冷启动、首次使用、热稳态、过载、排空和关闭阶段。
- 预热分层编译、着色器、管线、缓存和池,除非冷行为是调查对象。
- 在每个相关的所有权边界收集证据:托管运行时、原生库、内核、调度器、文件系统/网络、依赖项、驱动程序、GPU、合成器和显示器。
- 不要仅凭 RSS 推断托管内存保留。
- 不要仅凭高帧时间、GPU API 使用率或利用率推断 GPU 饱和。
- 不要仅凭一个平均值、一次跟踪、一次帧捕获、一个微基准测试或最快的基准测试运行就声称修复了问题。
- 保留原始工件,并报告缺失符号、丢弃的事件、分析器开销、环境差异和不确定性。
- 保护转储、跟踪、日志、SQL 范围、数据包捕获和基准测试输入语料库中的密钥和个人数据。
- 将命令示例视为版本敏感。首先查询已安装的工具帮助;当语法不同时,帮助信息覆盖示例。使用
references/command-reference.md获取经过验证的选择规则和规范形式。1
必填的调查输出
报告:
- 症状 —— 可观察的影响和违反的目标。
- 复现 —— 确定性的工作负载和环境。
- 收集 —— 确切的工具、命令、持续时间和工件路径。
- 证据 —— 计数器、跟踪、捕获、转储、基准测试分布和测量值。
- 所有权 —— 托管 CPU、运行时、原生 CPU、内核、调度器、I/O、依赖项、内存、驱动程序、GPU、合成器或显示器。
- 主导原因 —— 最大的支持成本、等待时间或增长最快的资源。
- 修复 —— 最小的高杠杆代码或配置变更。
- 验证 —— 等效之前/之后的运行,包含离散度和尾部指标。
- 剩余风险 —— 未解决的瓶颈和测量限制。
在没有来自等效工作负载的之前/之后证据的情况下,切勿声称有改进。
参考路由器
始终从 references/index.md、references/core/index.md 以及经过验证的 references/command-reference.md 开始。
根据问题加载额外的领域索引:
- 托管 CPU、分配、GC、JIT、异常、ThreadPool、锁、异步堆栈、转储:
references/runtime/index.md - 托管/原生/VM/映射/GPU 内存所有权和泄漏分析:
references/memory/index.md - 争用、异步延迟、队列、背压、文件/网络/数据库 I/O、UI 调度器延迟:
references/latency/index.md - 冷启动、首帧/请求、加载器、JIT、ReadyToRun、裁剪、单文件、NativeAOT:
references/startup/index.md - BenchmarkDotNet、微/组件/宏/负载/压力基准测试、异步/并发基准测试、JIT/AOT 比较、反汇编、硬件计数器、统计、CI 门控:
references/benchmarking/index.md - 生产环境、容器、诊断端口、
dotnet-monitor、触发式收集、Kubernetes、工件安全:references/production/index.md - UI/渲染/GPU/WebGPU/Metal/D3D/Vulkan/OpenGL/着色器/同步/呈现:
references/gpu/index.md
通过 references/platforms/index.md 加载每个相关的平台参考。单主机调查通常需要一个平台文档;跨平台比较和远程客户端/服务器场景可能需要多个文档。
不要默认加载所有文档。选择足以回答当前假设的最小集,但不要因为需要另一份指南就在所有权边界处停止。
快速分类序列
- 构建并运行生产等效配置。
- 定义工作负载阶段、成功指标和停止规则。
- 记录
System.Runtime计数器以及进程 CPU 和总内存。 - 当托管所有权可能时,使用
dotnet-trace捕获托管 CPU/运行时证据。 - 当怀疑有进展、饥饿或阻塞时,捕获重复堆栈。
- 如果总成本无法解释,收集平台原生 CPU/调度器/I/O/内存跟踪。
- 对于内存,比较托管活动字节、GC 承诺、私有/RSS/PSS、原生分配、映射、图形分配和 GPU 驻留。
- 对于渲染,关联应用程序标记、CPU 提交、GPU 时间戳、原生 GPU 捕获、呈现、合成器和显示时序。
- 对于基准测试请求,在选择 BenchmarkDotNet 或其他测试框架之前,先选择微、组件、宏、负载或压力方法。
- 对于仅限生产环境的事件,使用有限或触发式收集,并记录 cgroup/主机约束。
- 仅在证据支持主导原因后实施修复。
- 重复相同的工作负载,并发布中位数、离散度、尾部值和原始工件。
所有权测试
- 进程 CPU 高且托管堆栈热:应用程序/JIT/运行时工作。
- 进程 CPU 高但低于 P/Invoke:原生库、分配器、图形 API 或驱动程序 CPU。
- CPU 低且反复出现阻塞堆栈:同步、I/O、调度器、排队或外部依赖项。
- ThreadPool 队列上升且 CPU 低并出现阻塞堆栈:饥饿或同步过异步。
- 托管活动字节稳定但私有/RSS 增长:原生堆、映射、代码堆、碎片、图形资源或操作系统缓存。
- 在等效的完整收集点之后,托管活动图增长:托管内存保留或故意的无限缓存。
- CPU 帧时间长但 GPU 时间戳短:应用程序/渲染线程/驱动程序 CPU 绑定。
- CPU 提交短但 GPU 时间戳长:GPU 执行绑定。
- CPU 和 GPU 都提前完成但呈现延迟:交换链、合成器、垂直同步、可绘制资源饥饿或显示节奏。
- 依赖提交之间存在 GPU 空闲间隙:CPU 饥饿、过度同步、序列化或缓冲不足。
- 延迟高但在 CPU 上的时间低:离 CPU 等待、排队、依赖项、节流或调度器延迟。
- 启动改进但首次操作恶化:工作被推迟而非移除。
- 基准测试改进但产品指标未改进:实验忽略了关键路径、使用了错误的输入分布或优化了非主导操作。
智能体操作过程
- 检查仓库的构建、部署、诊断、基准测试项目、输入语料库、CI 和工件约定。
- 创建
artifacts/performance/<issue-or-timestamp>/或仓库等效目录。 - 当缺少时,添加确定性的工作负载驱动程序和稳定标记。
- 将命令、环境元数据、分析器/工具版本和官方参考链接存储在工件旁边。
- 首先分析包含成本,然后分析独占成本、被调用者、等待、排队、同步和所有权。
- 仅在主导原因得到支持后修改代码。
- 添加可防止问题再次发生的基准测试、回归预算、遥测标记、跟踪配方或生命周期不变量。
- 将大型跟踪、捕获、转储和基准测试工件保留在 Git 之外,除非仓库策略明确存储它们。
- 验证失败、取消、过载、清理、设备丢失和关闭路径——不仅仅是快乐路径。
需要拒绝的无效结论
- “CPU 很高,因此方法 X 很慢”但没有堆栈。
- “RSS 增长了,因此托管堆泄漏。”
- “GC 堆稳定,因此没有泄漏。”
- “平均延迟/帧时间低于预算,因此尾部延迟健康。”
- “GPU 利用率高,因此着色器 X 是瓶颈。”
- “渲染线程繁忙,因此 GPU 繁忙。”
- “一次帧捕获代表稳态行为。”
- “绘制调用减少总是意味着渲染更快。”
- “更多 ThreadPool 线程修复饥饿。”
- “异步代码不能阻塞。”
- “NativeAOT 自动更快。”
- “微基准测试改进证明应用程序改进。”
- “统计上可检测的差异自动具有产品意义。”
- “一次基准测试运行证明结果。”
文档脚注
Footnotes
-
references/command-reference.md,其中链接到 .NET 诊断工具、BenchmarkDotNet、Xcode Instruments、Windows Performance Toolkit、Linuxperf/BCC、PresentMon、RenderDoc 和供应商 GPU 分析器的当前官方文档。 ↩