analyzing-dotnet-performance by dotnet/skills
npx skills add https://github.com/dotnet/skills --skill analyzing-dotnet-performance扫描 C#/.NET 代码以查找性能反模式,并提供带有具体修复建议的优先级排序结果。模式来源于官方的 .NET 性能博客系列,提炼为可供客户操作的指导。
| 输入 | 必需 | 描述 |
|---|---|---|
| 源代码 | 是 | 要扫描的 C# 文件、代码块或仓库路径 |
| 热路径上下文 | 推荐 | 哪些代码路径是性能关键路径 |
| 目标框架 | 推荐 | .NET 版本(某些模式需要 .NET 8+) |
| 扫描深度 | 可选 | critical-only、(默认)或 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
standardcomprehensive尝试加载 references/critical-patterns.md 以及下面列出的特定主题参考文件。这些文件包含详细的检测方法和 grep 命令。
如果未找到参考文件(例如,在沙盒环境中或技能仅作为指令嵌入时),跳过文件加载,直接进入步骤 3,使用下面内联列出的扫描方法。不要花费时间在文件系统中搜索参考文件——如果它们不在预期的相对路径下,则表示不可用。
扫描代码以检测需要检查哪些模式类别的信号。如果加载了参考文件,则使用其 ## Detection 部分。否则,使用步骤 3 中的内联方法。
| 代码中的信号 | 主题 |
|---|---|
async、await、Task、ValueTask | 异步模式 |
Span<、Memory<、stackalloc、ArrayPool、string.Substring、.Replace(、.ToLower()、循环中的 +=、params | 内存与字符串 |
Regex、[GeneratedRegex]、Regex.Match、RegexOptions.Compiled | 正则表达式模式 |
Dictionary<、List<、.ToList()、.Where(、.Select(、LINQ 方法、static readonly Dictionary< | 集合与 LINQ |
JsonSerializer、HttpClient、Stream、FileStream | I/O 与序列化 |
无论信号如何,始终检查结构模式(未密封的类)。
扫描深度控制范围:
critical-only:仅关键模式(死锁,>10 倍的性能退化)standard(默认):关键模式 + 检测到的主题模式comprehensive:所有模式类别对于少于 500 行的文件,首先读取整个文件——你将比运行单个 grep 方法更快地发现大多数模式。使用 grep 来确认计数并捕捉可能遗漏的模式。
对于每个相关的模式类别,运行下面的检测方法。报告确切的计数,而非估计。
核心扫描方法(当参考文件不可用时运行这些):
# 字符串与内存
grep -n '\.IndexOf(\"' FILE # 缺少 StringComparison
grep -n '\.Substring(' FILE # Substring 分配
grep -En '\.(StartsWith|EndsWith|Contains)\s*\(' FILE # 缺少 StringComparison
grep -n '\.ToLower()\|\.ToUpper()' FILE # 区分区域性的 + 分配
grep -n '\.Replace(' FILE # 链式 Replace 分配
grep -n 'params ' FILE # params 数组分配
# 集合与 LINQ
grep -n '\.Select\|\.Where\|\.OrderBy\|\.GroupBy' FILE # 热路径上的 LINQ
grep -n '\.All\|\.Any' FILE # 字符串/字符上的 LINQ
grep -n 'new Dictionary<\|new List<' FILE # 每次调用的分配
grep -n 'static readonly Dictionary<' FILE # FrozenDictionary 候选
# 正则表达式
grep -n 'RegexOptions.Compiled' FILE # 编译正则表达式开销
grep -n 'new Regex(' FILE # 每次调用的正则表达式
grep -n 'GeneratedRegex' FILE # 正面:源码生成的正则表达式
# 结构
grep -n 'public class \|internal class ' FILE # 未密封的类
grep -n 'sealed class' FILE # 已密封的类
grep -n ': IEquatable' FILE # 正面:结构体相等性
规则:
## Detection 方法验证反面规则: 对于缺失模式,始终统计两面并报告比例(例如,"N 个类中有 M 个是密封的")。比例决定严重性——0/185 是系统性问题,12/15 是修复一致性问题。
如果在一个文件中发现了优化模式,检查同级文件(相同目录、相同接口、相同基类)是否使用了未优化的等效模式。标记为 🟡 中等,并将优化文件作为证据。
运行扫描方法后,查找这些单行方法可能遗漏的多重分配模式:
.Replace() 链: 在多个 if/else 分支中调用 .Replace() 的方法——报告所有分支的总分配次数,而不仅仅是每行。+= 与嵌入的分配调用: 像 result += $"...{Foo().ToLower()}" 这样的行包含 2+ 次分配(插值 + ToLower + 连接)——标记复合成本,而不仅仅是 .ToLower()。string.Format 特异性: 区分资源加载的格式字符串(不可修复)和编译时常量格式字符串(可通过插值修复)。列举可修复的位置。为每个发现分配一个严重性等级:
| 严重性 | 标准 | 操作 |
|---|---|---|
| 🔴 关键 | 死锁、崩溃、安全漏洞、>10 倍的性能退化 | 必须修复 |
| 🟡 中等 | 2-10 倍的改进机会,热路径的最佳实践 | 应在热路径上修复 |
| ℹ️ 信息 | 模式适用但代码可能不在热路径上 | 如果性能分析显示有影响则考虑修复 |
优先级排序规则:
基于规模的严重性升级: 当相同模式出现在许多实例中时,提升严重性:
始终报告确切计数(来自扫描方法),而非估计或代理摘要。
保持发现结果简洁。 每个发现是一个简短的块——不是长篇大论。按严重性分组(🔴 → 🟡 → ℹ️),而非按文件。
每个发现的格式:
#### ID. 标题 (N 个实例)
**影响:** 一行影响说明
**文件:** file1.cs:L1, file2.cs:L2, ... (列出位置,不要构建表格)
**修复:** 一行描述更改内容(例如,"添加 `StringComparison.Ordinal` 参数")
**注意事项:** 仅当非显而易见时(版本要求、正确性风险)
简洁输出规则:
File.cs:L42 格式。.ToLower() 调用归为一个发现,而非按文件拆分)。✅ 模式 — 证据。以摘要表格和免责声明结束:
| 严重性 | 数量 | 主要问题 |
|----------|-------|-----------|
| 🔴 关键 | N | ... |
| 🟡 中等 | N | ... |
| ℹ️ 信息 | N | ... |
> ⚠️ **免责声明:** 这些结果由 AI 助手生成,具有非确定性。发现可能包含误报、遗漏实际问题或建议的更改对你的特定上下文不正确。在将更改应用到生产代码之前,务必通过基准测试和人工审查来验证建议。
在交付结果之前,验证:
| 陷阱 | 正确方法 |
|---|---|
将每个 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 覆盖了绝大多数性能需求 |
每周安装数
96
仓库
GitHub 星标数
703
首次出现
2026年3月10日
安全审计
安装于
opencode89
kimi-cli88
gemini-cli88
amp88
github-copilot88
codex88
Scan C#/.NET code for performance anti-patterns and produce prioritized findings with concrete fixes. Patterns sourced from the official .NET performance blog series, distilled to customer-actionable guidance.
| Input | Required | Description |
|---|---|---|
| Source code | Yes | C# files, code blocks, or repository paths to scan |
| Hot-path context | Recommended | Which code paths are performance-critical |
| Target framework | Recommended | .NET version (some patterns require .NET 8+) |
| Scan depth | Optional | critical-only, standard (default), or comprehensive |
Try to load references/critical-patterns.md and the topic-specific reference files listed below. These contain detailed detection recipes and grep commands.
If reference files are not found (e.g., in a sandboxed environment or when the skill is embedded as instructions only), skip file loading and proceed directly to Step 3 using the scan recipes listed inline below. Do not spend time searching the filesystem for reference files — if they aren't at the expected relative path, they aren't available.
Scan the code for signals that indicate which pattern categories to check. If reference files were loaded, use their ## Detection sections. Otherwise, use the inline recipes in Step 3.
| Signal in Code | Topic |
|---|---|
async, await, Task, ValueTask | Async patterns |
Span<, Memory<, stackalloc, ArrayPool, string.Substring, .Replace(, , in loops, |
Always check structural patterns (unsealed classes) regardless of signals.
Scan depth controls scope:
critical-only: Only critical patterns (deadlocks, >10x regressions)standard (default): Critical + detected topic patternscomprehensive: All pattern categoriesFor files under 500 lines, read the entire file first — you'll spot most patterns faster than running individual grep recipes. Use grep to confirm counts and catch patterns you might miss visually.
For each relevant pattern category, run the detection recipes below. Report exact counts, not estimates.
Core scan recipes (run these when reference files aren't available):
# 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
Rules:
## Detection recipesVerify-the-Inverse Rule: For absence patterns, always count both sides and report the ratio (e.g., "N of M classes are sealed"). The ratio determines severity — 0/185 is systematic, 12/15 is a consistency fix.
If an optimized pattern is found in one file, check whether sibling files (same directory, same interface, same base class) use the un-optimized equivalent. Flag as 🟡 Moderate with the optimized file as evidence.
After running scan recipes, look for these multi-allocation patterns that single-line recipes miss:
.Replace() chains: Methods that call .Replace() across multiple if/else branches — report total allocation count across all branches, not just per-line.+= with embedded allocating calls: Lines like result += $"...{Foo().ToLower()}" are 2+ allocations (interpolation + ToLower + concatenation) — flag the compound cost, not just the .ToLower().string.Format specificity: Distinguish resource-loaded format strings (not fixable) from compile-time literal format strings (fixable with interpolation). Enumerate the actionable sites.Assign each finding a severity:
| Severity | Criteria | Action |
|---|---|---|
| 🔴 Critical | Deadlocks, crashes, security vulnerabilities, >10x regression | Must fix |
| 🟡 Moderate | 2-10x improvement opportunity, best practice for hot paths | Should fix on hot paths |
| ℹ️ Info | Pattern applies but code may not be on a hot path | Consider if profiling shows impact |
Prioritization rules:
Scale-based severity escalation: When the same pattern appears across many instances, escalate severity:
Always report exact counts (from scan recipes), not estimates or agent summaries.
Keep findings compact. Each finding is one short block — not an essay. Group by severity (🔴 → 🟡 → ℹ️), not by file.
Format per finding:
#### 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)
Rules for compact output:
File.cs:L42 format..ToLower() calls go in one finding, not split by file).✅ Pattern — evidence.End with a summary table and disclaimer:
| 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.
Before delivering results, verify:
| Pitfall | Correct Approach |
|---|---|
Flagging every Dictionary as needing FrozenDictionary | Only flag if the dictionary is never mutated after construction |
Suggesting Span<T> in async methods | Use Memory<T> in async code; Span<T> only in sync hot paths |
| Reporting LINQ outside hot paths | Only flag LINQ in identified hot paths or tight loops; LINQ is acceptable in code that runs infrequently. Since .NET 7, LINQ Min/Max/Sum/Average are vectorized — blanket bans on LINQ are misguided |
Suggesting ConfigureAwait(false) in app code | Only applicable in library code; not primarily a performance concern |
Weekly Installs
96
Repository
GitHub Stars
703
First Seen
Mar 10, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode89
kimi-cli88
gemini-cli88
amp88
github-copilot88
codex88
Swift Actor 线程安全持久化:构建离线优先应用的编译器强制安全数据层
1,700 周安装
Swift应用架构与状态管理指南:axiom-app-composition最佳实践
160 周安装
AI辅助Git提交工具:自动生成规范提交信息,遵循Conventional Commits
160 周安装
tmux 终端复用器使用指南:隔离会话、安全输入与自动化脚本
161 周安装
Webpack 5 打包配置指南:优化代码分割、Tree Shaking 与构建性能
163 周安装
iOS/macOS 构建调试指南:解决 Xcode SPM/CocoaPods 依赖项与编译错误
163 周安装
AVFoundation 相机问题诊断指南:解决 iOS 相机预览冻结、旋转错误、捕获缓慢
164 周安装
.ToLower()+=params | Memory & strings |
Regex, [GeneratedRegex], Regex.Match, RegexOptions.Compiled | Regex patterns |
Dictionary<, List<, .ToList(), .Where(, .Select(, LINQ methods, static readonly Dictionary< | Collections & LINQ |
JsonSerializer, HttpClient, Stream, FileStream | I/O & serialization |
Recommending ValueTask everywhere | Only for hot paths with frequent synchronous completion |
Flagging new HttpClient() in DI services | Check if IHttpClientFactory is already in use |
Suggesting [GeneratedRegex] for dynamic patterns | Only flag when the pattern string is a compile-time literal |
Suggesting CollectionsMarshal.AsSpan broadly | Only for ultra-hot paths with benchmarked evidence; adds complexity and fragility |
Suggesting unsafe code for micro-optimizations | Avoid unsafe except where absolutely necessary — do not recommend it for micro-optimizations that don't matter. Safe alternatives like Span<T>, stackalloc in safe context, and ArrayPool cover the vast majority of performance needs |