mcpField
A
analyzing-dotnet-performance

analyzing-dotnet-performance

github.com/dotnetテスト・品質5.8k5.1k 回インストール1 日前に更新
リポジトリ

analyzing-dotnet-performance について

C#/.NETコードのパフォーマンス上のアンチパターンを調べ、優先度付きの指摘と修正案を示すスキルです。コードレビューやホットパスの監査に適し、検出したコードの兆候とスキャン深度に応じて確認範囲を選びます。

主な機能

  1. 01C#/.NETの性能パターン検査
  2. 02コードの兆候に応じた項目選択
  3. 033段階のスキャン深度
  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 — 常に読み込みます(unsealed クラスはシグナルの有無にかかわらず確認します)
  • comprehensive:上記のトピック固有のリファレンスをすべて読み込みます

カバレッジを報告する際、選択したリファレンスは references/critical-patterns.md と、上記で選択したトピック固有のリファレンスのみです。選択したリファレンスが再試行後も利用できない場合は、不足するカバレッジに対してステップ 3 のインラインレシピを使用します。最終レポートには Reference coverage: reduced; unavailable: <paths>; used inline recipes for missing references. を含め、<paths> は選択したリファレンスのうち不足している相対パスのみで置き換えます。

読み込んだリファレンスファイルの ## Detection セクションと、リファレンスファイルが利用できないカテゴリのインラインレシピをステップ 3 で使用します。

コード内のシグナルトピック
async、await、Task、ValueTaskAsync パターン
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 とシリアライゼーション

シグナルの有無にかかわらず、構造パターン(unsealed クラス)を必ず確認します。

スキャンの深度で対象範囲を制御します。

  • 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 レシピも実行します

逆方向の確認ルール: 不在パターンについては、必ず両方の側を数えて比率を報告します(例:「クラス 185 個中 0 個が sealed」)。比率によって重大度が決まります。0/185 は体系的な問題、12/15 は一貫性を整える修正です。

ステップ 3b:ファイル間の一貫性チェック

あるファイルで最適化されたパターンが見つかった場合、兄弟ファイル(同じディレクトリ、同じインターフェース、同じ基底クラス)で最適化されていない同等の実装が使われていないか確認します。最適化されたファイルを証拠として、🟡 Moderate の指摘として報告します。

ステップ 3c:複合的な割り当てのチェック

スキャンレシピを実行した後、1 行のレシピでは見落とされる以下の複数割り当てパターンを確認します。

  1. 分岐した .Replace() チェーン: 複数の if/else 分岐にまたがって .Replace() を呼び出すメソッドでは、行ごとではなく、すべての分岐を通じた割り当ての合計数を報告します。
  2. メソッドをまたぐチェーン: 公開メソッドが、中間オブジェクトを割り当てる別のメソッド(例:A が 3 回の正規表現置換を行う B を呼び出し、その後 A が C を呼び出す)に処理を委譲する場合、チェーン全体のコストを 1 件の指摘として報告します。
  3. 割り当てを伴う呼び出しを含む複合 +=: result += $"...{Foo().ToLower()}" のような行は、2 回以上の割り当て(文字列補間 + ToLower + 連結)になります。.ToLower() だけでなく、複合的なコストを指摘します。
  4. string.Format の具体性: リソースから読み込まれる書式文字列(修正不可)と、コンパイル時のリテラル書式文字列(補間を使って修正可能)を区別します。対応可能な箇所を列挙します。
ステップ 4:指摘を分類して優先順位を付ける

各指摘に重大度を割り当てます。

重大度基準対応
🔴 重大デッドロック、クラッシュ、セキュリティ脆弱性、10 倍を超える性能低下必ず修正
🟡 中程度2~10 倍の改善機会、ホットパスにおけるベストプラクティスホットパスでは修正を推奨
ℹ️ 情報該当するパターンだが、そのコードがホットパスとは限らないプロファイリングで影響が確認された場合に検討

優先順位付けのルール:

  1. ユーザーがホットパスのコードを特定した場合、そのコード内のすべての指摘を最大の重大度に引き上げます。
  2. ホットパスかどうか不明な場合、🔴 重大な指摘は必ず報告します。🟡 中程度の指摘には 「このコードがホットパスにある場合は影響があります」 という注記を付けて報告します。
  3. 明らかに性能が重要でないコードに対して、マイクロ最適化を提案してはいけません。

規模に応じた重大度の引き上げ: 同じパターンが多数の箇所に現れる場合は、重大度を引き上げます。

  • 同じアンチパターンが 1~10 件 → パターンの基本重大度で報告
  • 11~50 件 → ℹ️ 情報のパターンを 🟡 中程度に引き上げ
  • 50 件以上 → 優先度を上げて 🟡 中程度に引き上げ、コードベース全体に及ぶ体系的な問題として指摘

推定値やエージェントによる要約ではなく、必ず正確な件数(スキャンレシピから取得)を報告します。

ステップ 5:指摘を作成する

指摘は簡潔にまとめます。 各指摘は短いブロック 1 つとし、長文にはしません。ファイルごとではなく、重大度順(🔴 → 🟡 → ℹ️)にまとめます。

指摘ごとの形式:

#### 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)

簡潔な出力のルール:

  • キーワード、パラメーター、型の変更を追加するだけの簡単な修正では、❌/✅ のコードブロックを使いません。 1 行の修正説明で十分です。
  • コードブロックを含めるのは、単純ではない変換(例:LINQ チェーンを foreach ループに置き換える、クロージャを外に出す)のみです。
  • ファイル位置は、表ではなくインラインのカンマ区切りリストで示します。 File.cs:L42 形式を使用します。
  • 影響行を超える説明文は記載しません。 重大度アイコンがすでに緊急度を示しています。
  • 同じ修正方法で対応できる関連指摘は統合します(例:すべての .ToLower() 呼び出しを、ファイルごとに分けず 1 件の指摘にまとめます)。
  • 良い点は表ではなく箇条書きで示します。 パターンごとに 1 行で、形式は ✅ パターン — 根拠 とします。

最後に、要約表と免責事項を記載します。

| 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 が必要だと指摘する構築後に辞書が一度も変更されない場合にのみ指摘してください
async メソッドで Span<T> を提案するasync コードでは 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 といった安全な代替手段で、パフォーマンス上のニーズの大部分を満たせます

コメント