mcpField
A
analyzing-dotnet-performance

analyzing-dotnet-performance

github.com/dotnet测试与质量5.8k5.1k 次安装1 天前更新
代码仓库

关于 analyzing-dotnet-performance

用于扫描 C#/.NET 代码中的性能反模式,并按优先级给出发现和具体修复建议,适合代码审查与热点路径审计。可按扫描深度和代码信号选择检查范围。

主要功能

  1. 01检查 C#/.NET 性能模式
  2. 02按代码信号选择检查项
  3. 03支持三种扫描深度
  4. 04按优先级提供修复建议

适用场景

  1. 01审查代码以寻找优化机会
  2. 02审计性能关键路径
  3. 03发布前扫描代码库
  4. 04为人工审查提供第二意见

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dotnet/skills --skill analyzing-dotnet-performance

安装后在 Claude Code 中输入/analyzing-dotnet-performance确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
async-patterns.md
collections-and-linq.md
critical-patterns.md
io-and-serialization.md
memory-and-strings.md
regex-patterns.md
structural-patterns.md

详细介绍

.NET 性能模式

扫描 C#/.NET 代码中的性能反模式,并生成按优先级排序的发现结果及具体修复建议。模式取自官方 .NET 性能博客系列,并提炼为可供用户采取行动的指导。

适用场景

  • 审查 C#/.NET 代码,寻找性能优化机会
  • 检查热路径中分配开销较大的模式或低效模式
  • 发布前系统性扫描代码库中的已知反模式
  • 手动性能审查后的第二意见分析

不适用场景

  • 算法复杂度分析——此技能针对 API 使用模式,而非算法设计
  • 不在热路径中且没有性能要求的代码——避免过早优化

输入

输入必填说明
源代码是要扫描的 C# 文件、代码块或仓库路径
热路径上下文建议提供哪些代码路径对性能至关重要
目标框架建议提供.NET 版本(某些模式要求 .NET 8+)
扫描深度可选critical-only、standard(默认)或 comprehensive

工作流

步骤 1:加载关键参考资料

从包含此 SKILL.md 的目录解析捆绑文件的路径,而不是从用户的工作区解析。首先加载此参考文件:

  • references/critical-patterns.md

如果直接读取失败,只列出一次此技能的 references/ 目录;只有在列表中显示存在预期文件时才重试。不要使用工作区文件或文本搜索来定位技能安装位置。

第 2 步:检测代码信号并选择主题配方

扫描代码,查找指示需要检查哪些模式类别的信号。条件允许时,使用关键参考资料中的 ## Detection 部分,以及第 3 步中的内联配方进行初步信号检测。

检测到信号后,根据扫描深度加载所选主题的参考资料:

  • critical-only:不加载其他参考资料(仅使用 critical-patterns.md)
  • standard(默认):从以下列表中加载与检测到的信号相匹配的参考资料:
    • references/async-patterns.md — async/Task/ValueTask 信号
    • references/memory-and-strings.md — Span/Memory/字符串分配信号
    • references/regex-patterns.md — Regex 信号
    • references/collections-and-linq.md — Dictionary/List/LINQ 信号
    • references/io-and-serialization.md — JsonSerializer/HttpClient/Stream 信号
    • references/structural-patterns.md — 始终加载(无论是否封闭类都要检查)
  • comprehensive:加载上述全部六份主题参考资料

报告覆盖范围时,所选参考资料包括 references/critical-patterns.md,以及仅限上述已选主题参考资料。如果重试后仍无法获取某份已选参考资料,请使用第 3 步中的内联配方补充缺失部分。在最终报告中加入 Reference coverage: reduced; unavailable: <paths>; used inline recipes for missing references.,并将 <paths> 替换为仅包含所选参考资料中缺失的相对路径。

使用已加载参考文件中的 ## Detection 部分,以及参考文件不可用时第 3 步中的内联配方,检查相应类别。

代码中的信号主题
async、await、Task、ValueTask异步模式
Span<、Memory<、stackalloc、ArrayPool、string.Substring、.Replace(、.ToLower()、循环中的 +=、params内存与字符串
Regex、[GeneratedRegex]、Regex.Match、RegexOptions.CompiledRegex 模式
Dictionary<、List<、.ToList()、.Where(、.Select(、LINQ 方法、static readonly Dictionary<集合与 LINQ
JsonSerializer、HttpClient、Stream、FileStreamI/O 与序列化

无论是否检测到信号,都要检查结构模式(非封闭类)。

扫描深度决定范围:

  • critical-only:仅检查关键模式(死锁、性能退化超过 10 倍)
  • standard(默认):关键模式 + 检测到的主题模式
  • comprehensive:所有模式类别
第 3 步:扫描并报告

对于少于 500 行的文件,请先通读整个文件——比单独运行 grep 配方更快发现大多数模式。使用 grep 确认计数,并捕捉可能在目视检查中遗漏的模式。

针对每个相关模式类别,运行以下检测配方。报告准确数量,不要估算。

核心扫描配方(参考文件不可用时运行):

# Strings & memory
grep -n '\.IndexOf(\"' FILE                    # Missing StringComparison
grep -n '\.Substring(' FILE                    # Substring allocations
grep -En '\.(StartsWith|EndsWith|Contains)\s*\(' FILE  # Missing StringComparison
grep -n '\.ToLower()\|\.ToUpper()' FILE        # Culture-sensitive + allocation
grep -n '\.Replace(' FILE                      # Chained Replace allocations
grep -n 'params ' FILE                         # params array allocation

# Collections & LINQ
grep -n '\.Select\|\.Where\|\.OrderBy\|\.GroupBy' FILE  # LINQ on hot path
grep -n '\.All\|\.Any' FILE                    # LINQ on string/char
grep -n 'new Dictionary<\|new List<' FILE      # Per-call allocation
grep -n 'static readonly Dictionary<' FILE     # FrozenDictionary candidate

# Regex
grep -n 'RegexOptions.Compiled' FILE           # Compiled regex budget
grep -n 'new Regex(' FILE                      # Per-call regex
grep -n 'GeneratedRegex' FILE                  # Positive: source-gen regex

# Structural
grep -n 'public class \|internal class ' FILE  # Unsealed classes
grep -n 'sealed class' FILE                    # Already sealed
grep -n ': IEquatable' FILE                    # Positive: struct equality

规则:

  • 对检测到的模式类别运行所有相关配方
  • 在分类发现项之前,输出扫描执行清单——列出每个配方及命中数量
  • 0 次命中也是有效且有价值的结果(可确认良好实践)
  • 如果已加载参考文件,也要运行其中的 ## Detection 配方

反向验证规则: 对于检查缺失情况的模式,始终统计正反两种情况并报告比例(例如:“M 个类中有 N 个已封闭”)。比例决定严重程度——0/185 表示系统性问题,12/15 表示一致性修复。

第 3b 步:跨文件一致性检查

如果在某个文件中发现优化模式,请检查同目录、实现相同接口或继承相同基类的文件是否使用了未优化的等效写法。将其标记为 🟡 中等,并以已优化的文件作为证据。

步骤 3c:复合分配检查

运行扫描配方后,检查以下单行配方容易遗漏的多重分配模式:

  1. 分支式 .Replace() 链: 跨多个 if/else 分支调用 .Replace() 的方法——报告所有分支的分配总数,而不仅是每行的数量。
  2. 跨方法链式调用: 公共方法委托给另一个会分配中间对象的方法时(例如,A 调用 B,B 执行 3 次正则替换,然后 A 调用 C),将整条调用链的成本作为一个发现报告。
  3. 包含分配调用的复合 +=: 类似 result += $"...{Foo().ToLower()}" 的代码行包含 2 次以上分配(插值 + ToLower + 拼接)——标出复合成本,而不仅是 .ToLower()。
  4. string.Format 的具体情况: 区分从资源加载的格式字符串(无法修复)和编译时字面量格式字符串(可改用插值修复)。逐一列出可采取措施的位置。
步骤 4:对发现进行分类和优先级排序

为每项发现指定严重程度:

严重程度标准操作
🔴 严重死锁、崩溃、安全漏洞、性能回退 >10 倍必须修复
🟡 中等有 2–10 倍的改进空间,属于热路径最佳实践热路径上应该修复
ℹ️ 信息该模式适用,但代码可能不在热路径上如果性能分析显示有影响,可考虑修复

优先级排序规则:

  1. 如果用户指出了热路径代码,则将该代码中的所有发现提升至其最高严重程度
  2. 如果热路径上下文未知,则无条件报告 🔴 严重发现;报告 🟡 中等发现时附注:“如果此代码位于热路径上,则会产生显著影响”
  3. 切勿对明显对性能不敏感的代码提出微优化建议

基于规模提升严重程度: 当同一模式出现在多个实例中时,提升其严重程度:

  • 同一反模式有 1–10 个实例 → 按该模式的基础严重程度报告
  • 同一反模式有 11–50 个实例 → 将 ℹ️ 信息模式提升至 🟡 中等
  • 同一反模式有 50 个以上实例 → 提升至 🟡 中等并提高优先级;标记为代码库范围内的系统性问题

始终报告扫描配方得出的准确数量,不要使用估算值或 Agent 汇总值。

步骤 5:生成发现

保持发现内容简洁。 每项发现用一个简短区块呈现,不要写成长篇说明。按严重程度分组(🔴 → 🟡 → ℹ️),不要按文件分组。

每项发现的格式:

#### ID. Title (N instances)
**Impact:** one-line impact statement
**Files:** file1.cs:L1, file2.cs:L2, ... (list locations, don't build tables)
**Fix:** one-line description of the change (e.g., "Add `StringComparison.Ordinal` parameter")
**Caveat:** only if non-obvious (version requirement, correctness risk)

简洁输出规则:

  • 对于简单修复(添加关键字、参数或更改类型),不要提供 ❌/✅ 代码块。 用一行描述修复方法即可。
  • 仅在转换并不明显时提供代码块(例如,将 LINQ 链替换为 foreach 循环,或将闭包提取到外部)。
  • 文件位置使用逗号分隔的行内列表,不要用表格。使用 File.cs:L42 格式。
  • 除了 Impact 行外,不要添加解释性文字——严重程度图标已能传达紧迫性。
  • 合并采用相同修复方法的相关发现(例如,所有 .ToLower() 调用合并为一项发现,不要按文件拆分)。
  • 正向发现使用项目符号列表,不要用表格。每种模式占一行:✅ 模式 — 证据。

最后附上汇总表和免责声明:

| Severity | Count | Top Issue |
|----------|-------|-----------|
| 🔴 Critical | N | ... |
| 🟡 Moderate | N | ... |
| ℹ️ Info | N | ... |

> ⚠️ **Disclaimer:** These results are generated by an AI assistant and are non-deterministic. Findings may include false positives, miss real issues, or suggest changes that are incorrect for your specific context. Always verify recommendations with benchmarks and human review before applying changes to production code.

验证

提交结果前,请确认:

  • 已检查所有关键模式(来自参考文件或行内配方)
  • 仅在检测到匹配信号时运行特定主题的配方
  • 每项发现都包含具体的代码修复方法
  • 扫描执行清单已完成(所有配方均已运行)
  • 末尾已包含汇总表

常见误区

误区正确做法
将每个 Dictionary 都标记为需要改用 FrozenDictionary仅当字典构造后不再发生修改时才标记
在异步方法中建议使用 Span<T>异步代码中使用 Memory<T>;Span<T> 仅用于同步热路径
报告热路径之外的 LINQ仅标记已识别热路径或紧密循环中的 LINQ;在低频运行的代码中使用 LINQ 是可以接受的。从 .NET 7 开始,LINQ 的 Min/Max/Sum/Average 已实现向量化;一概禁止使用 LINQ 的做法并不合理
在应用代码中建议使用 ConfigureAwait(false)仅适用于库代码;这并非主要的性能问题
到处推荐使用 ValueTask仅用于频繁同步完成的热路径
将 DI 服务中的 new HttpClient() 标记为问题检查是否已在使用 IHttpClientFactory
对动态模式建议使用 [GeneratedRegex]仅当模式字符串是编译时字面量时才标记
广泛建议使用 CollectionsMarshal.AsSpan仅用于有基准测试证据支持的极热路径;它会增加复杂性和脆弱性
为微优化建议使用 unsafe 代码除非绝对必要,否则避免使用 unsafe——不要为了无关紧要的微优化而推荐它。Span<T>、安全上下文中的 stackalloc 和 ArrayPool 等安全替代方案已能满足绝大多数性能需求

评论

支持 Markdown