accelint-ts-performance について
JavaScript/TypeScript の性能を体系的に監査・改善するスキルです。ボトルネックやアンチパターンの分析から、最適化手法と変更後の正当性確認までを案内します。
主な機能
- 01体系的な性能監査
- 02アルゴリズム上の非効率を特定
- 03CPU とメモリのトレードオフ評価
- 04問題別の最適化パターン適用
- 05変更後の正当性確認
活用シーン
- 01JavaScript コードベースの性能監査
- 02非効率なループや検索の特定
- 03メモリや I/O 動作の最適化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ gohypergiant/ agent-skills --skill accelint-ts-performanceインストール後、Claude Code で/accelint-ts-performanceを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdREADME.mdassets/output-report-template.mdreferences/avoid-allocations.mdbatching.mdbounded-iteration.mdcache-property-access.mdcache-storage-api.mdcurrying.mddefer-await.mdmemoization.mdobject-operations.mdperformance-misc.mdpredictable-execution.mdquick-reference.mdreduce-branching.mdreduce-looping.md詳細
TypeScript のパフォーマンス最適化
JavaScript/TypeScript のコードベースを体系的に最適化します。監査ワークフローと、実行時パフォーマンスを高める専門的な最適化パターンを組み合わせています。
パフォーマンス最適化で絶対にしてはいけないこと
注: any/enum による型安全性、null の回避、引数の変更を避けることなど、一般的なベストプラクティスについては、accelint-ts-best-practices スキルを使用してください。このセクションでは、パフォーマンスに特化したアンチパターンのみを扱います。
-
コードがコールドパスだと決めつけないでください - ユーティリティ関数、フォーマッター、パーサー、バリデーターは単純に見えても、ループ、レンダリングパイプライン、リアルタイムシステムで頻繁に呼び出されることがあります。パフォーマンスのアンチパターンがないか、常にすべてのコードを監査してください。使用頻度を推測したり、単純そうだからという理由で監査を省略したりしないでください。
-
すべての最適化を考えなしに適用しないでください - パフォーマンスパターンにはトレードオフがあります。最適化によるメリットとコードの複雑さのバランスを取ってください。監査では体系的な分析を通じてすべてのアンチパターンを特定し、期待できる改善効果とあわせて報告してください。どの最適化を適用するかは、ユーザーが個別の状況に応じて判断できるようにしてください。
-
アルゴリズムの計算量を無視しないでください - O(n²) のコードをマイクロ最適化しても効果はありません。n=1000 の場合、アルゴリズムを修正して O(n² → O(n)) にすれば1000倍高速化できますが、マイクロ最適化では最大でも1.1~2倍程度です。まずアルゴリズムを修正してください。O(1) で検索できる Maps/Sets を使い、ネストした反復をなくし、適切なデータ構造を選んでください。
-
速度のために正確性を犠牲にしないでください - パフォーマンス上のバグもバグです。最適化によって、手動ループでのインデックスのずれ、空配列での誤動作、null の処理漏れなど、エッジケースで問題が生じることはよくあります。変更の前後で動作が一致することを確認してください。最適化の前にエッジケースを網羅するテストを追加してください。本番環境でバグを見つけるコストは、パフォーマンス改善のメリットをはるかに上回ります。
-
自分が担当していないコードを最適化しないでください - 共有ユーティリティ、ライブラリの内部実装、または他の人が積極的に開発しているコードへの変更は、マージ競合を招き、作業を重複させ、担当範囲を曖昧にします。パフォーマンスに関する変更は、すべての呼び出し元に影響します。担当者と調整するか、コードが安定するまで最適化を見送ってください。
-
メモリと CPU のトレードオフを無視しないでください - キャッシュはメモリと引き換えに速度を向上させます。上限のないメモ化は、長時間稼働するアプリケーションでメモリリークを引き起こします。CPU を2倍高速化しても、メモリ使用量が10倍になれば、OOM クラッシュや頻繁な GC の一時停止を招き、元の遅さより悪化する可能性があります。CPU とあわせてメモリ使用量もプロファイルしてください。キャッシュサイズに上限を設け、ライフサイクルに紐づくキャッシュには WeakMap を使用してください。
-
環境を問わずパフォーマンスが同じだと思い込まないでください - V8 の最適化は、Node.js のバージョン(v18 と v20)、ブラウザー(Chrome と Safari)、アーキテクチャ(x64 と ARM)によって異なります。Chrome で3倍高速化する最適化でも、Safari では1.5倍遅くなる可能性があります。リリース前に、対象となるすべての環境でプロファイルしてください。環境固有の最適化には、フォールバック実装を用意してください。
-
配列メソッドを連結しないでください(.filter().map().reduce()) - 各メソッドは中間配列を作成し、それぞれ別々に反復処理を行います。1万件の配列では、
.filter().map()は、フィルターを通過する割合が50%の場合、1万件+5000件分の要素を割り当て、2回反復します。単一のreduceパスを使えば、1回の反復で中間割り当てをゼロにでき、ホットパスでは2~5倍の高速化が期待できます。 -
繰り返し検索に
Array.includes()を使わないでください - Array.includes() は O(n) の線形検索です。1000件の項目を100件の配列に対して検索すると、O(n×m) = 10万回の処理になります。代わりにSet.has()を使ってください。ハッシュテーブルによる O(1) の検索で、処理回数を10万回から1000回に減らし、約100倍の高速化を実現できます。事前に一度だけ Set を作成すれば、償却コストは無視できる程度です。 -
結果が必要か確認する前に、
awaitしてはいけません - 値が不要な場合でも、awaitは直ちに実行を一時停止します。実際に結果を使う条件分岐の中にawaitを移動してください。例:const data = await fetch(url); if (condition) { use(data); }は、condition が false のときに I/O 時間を無駄にします。より良い方法は、if (condition) { const data = await fetch(url); use(data); }です。結果が不要な場合は fetch 自体を省略できます。 -
ループ内で定数を再計算してはいけません - 不変値を再計算すると、反復ごとに CPU を浪費します。1 万回の反復では、エンジンがキャッシュしている場合でも
array.lengthの参照やMath.max(a, b)の実行が 1 万回発生し、不要なコストになります。不変値はループの外に移動してください。例:const len = array.length; for (let i = 0; i < len; i++)、または関数をカリー化して定数のパラメーターを一度だけ事前計算します。 -
上限のないループやキューを作成してはいけません - バグや悪意のある入力によるリソースの暴走を防ぎます。明示的な上限(
for (let i = 0; i < Math.min(items.length, 10000); i++))またはタイムアウトを設定してください。上限のないループは UI スレッドをフリーズさせる可能性があり、上限のないキューは OOM クラッシュの原因になります。使いものにならない状態まで徐々に劣化させるのではなく、明確な上限を設けて速やかに失敗させてください。 -
ホットパスに
try/catchを置いてはいけません - V8 は try-catch ブロックを含む関数をインライン化できず、関数全体を最適化対象外として扱います。ホットループ内に try-catch が 1 つあるだけで、インライン化、エスケープ解析、その他の最適化が妨げられ、処理速度が 3~5 倍低下します。型ガードを使用してホットパスの前に入力を検証してください。try-catch はループの外に移動して処理全体を囲むか、想定されるエラーには Result 型を使用してください。
パフォーマンスを最適化する前に確認すること
次の確認項目を使って、最適化の取り組みを効果的に優先順位付けしてください。
影響の評価
- このコードは実際に遅いですか? プロファイリングデータがある場合は、それを参考に優先順位を決めてください。データがない場合は、すべてのコードを監査してアンチパターンを確認してください。
- 実行時間の何パーセントを占めていますか? プロファイリングデータがある場合は、フレームグラフを使って影響の大きい問題を特定できます。データがない場合は、見つかったアンチパターンをすべて報告してください。
- 生のパフォーマンスが重要です - 現在の使用状況にかかわらず、すべてのコードを監査してパフォーマンスのアンチパターンを確認してください。ユーティリティ関数、フォーマッター、パーサー、データ変換処理は、一見単純に見えても、ループ、レンダリングパイプライン、リアルタイムシステムで頻繁に呼び出されます。
正確性の検証
- このコードを対象とするテストはありますか? パフォーマンスの不具合は見つけにくいものです。包括的なテストがあれば、最適化によるリグレッションを検出できます。最適化する前にテストを追加してください。
- エッジケースは何ですか? オフバイワンエラー、空の配列、null/undefined の値は、手動でループを最適化すると発生しやすくなります。徹底的にテストしてください。
複雑さとメリット
- アルゴリズムの計算量は最適ですか? O(n) → O(1) なら速度は 1000 倍になります。マイクロ最適化で得られる速度向上は、最大でも 1.1~2 倍です。まずアルゴリズムを改善してください。
- この最適化は長く維持されますか? コードが頻繁に変更される場合、最適化はすぐに破棄される可能性があります。まず安定したコードを最適化してください。
- 可読性への影響はどの程度ですか? 手動のループは
.map()より高速ですが、保守が難しくなります。パフォーマンスとチームの開発速度のバランスを取ってください。
使用方法
このスキルでは、コンテキストの使用量を最小限に抑えるため、段階的な開示を採用しています。
1. ワークフローから始める(SKILL.md)
以下の 4 フェーズの監査ワークフローに従い、体系的にパフォーマンスを分析してください。
2. パフォーマンスルールの概要を参照する(AGENTS.md)
AGENTS.md を読み込み、カテゴリ別に整理された簡潔なルールの概要を確認してください。
3. 必要に応じて具体的なパフォーマンスパターンを読み込む
具体的なパフォーマンスの問題を特定したら、該当するリファレンスファイルを読み込み、詳細な ❌/✅ の例を確認してください。
4. レポートテンプレートを使用する(明示的に監査を依頼された場合)
ユーザーからパフォーマンス監査を明示的に依頼された場合は、テンプレートを読み込み、一貫性のあるレポートを作成してください。
- assets/output-report-template.md - ガイダンス付きの構造化テンプレート
パフォーマンス最適化ワークフロー
2 つの動作モード:
-
監査モード - スキルが直接呼び出された場合(
/accelint-ts-performance <path>)、またはユーザーが明示的にパフォーマンス監査を依頼した場合- テンプレートを使って構造化された監査レポートを作成します(フェーズ 1~2 のみ)
- 実装前に、ユーザーが確認できるように調査結果を報告します
- どの最適化を適用するかは、ユーザーが決定します
-
実装モード - 機能の作業中にスキルが自動的に起動した場合
- 最適化を特定し、直接適用します(全 4 フェーズ)
- 正式なレポートは不要です
- 問題をその場で修正することに集中します
進捗を記録するには、このチェックリストをコピーしてください:
- [ ] Phase 1: Profile - Identify actual bottlenecks using profiling tools
- [ ] Phase 2: Analyze - Categorize issues by impact and optimization category
- [ ] Phase 3: Optimize - Apply performance patterns from references/
- [ ] Phase 4: Verify - Measure improvements and validate correctness
フェーズ 1:ボトルネックの特定に向けたプロファイリング
重要:パフォーマンスのアンチパターンについて、すべてのコードを監査してください。 使用頻度に関する推測に基づいてコードを調査対象から外さないでください。ユーティリティ関数、フォーマッター、パーサー、バリデーター、データ変換処理は、実装が単純に見えても、ループ、レンダリングパイプライン、リアルタイムシステムで頻繁に呼び出されることがあります。
プロファイリングツールを利用できる場合は、ベースラインの測定に使用してください:
- ブラウザー:Chrome DevTools の Performance タブ
- Node.js:
node --prof script.js && node --prof-process isolate-*.log
プロファイリングデータの有無にかかわらず、体系的な静的コード分析を行い、パフォーマンスのアンチパターンをすべて特定してください:
- O(n²) の計算量(ネストされたループ、検索の繰り返し)
- 過剰なメモリ割り当て(テンプレートリテラル、オブジェクトスプレッド、配列メソッド)
String()で十分な箇所でのテンプレートリテラルの割り当て- 配列メソッドのチェーン(.filter().map())
- 非同期処理のブロッキング
- ループ内の try/catch
出力:特定されたすべてのアンチパターンの完全な一覧と、それぞれの場所および想定されるパフォーマンスへの影響を記載します。「重大度」や「優先度」に基づいて絞り込まず、見つかったものをすべて報告してください。
監査レポートを作成する場合(スキルが /accelint-ts-performance <path> で直接呼び出された場合、またはユーザーが明示的にパフォーマンス監査を依頼した場合)は、構造化テンプレートを使用してください:
- レポートの構成については、assets/output-report-template.md を読み込みます
- 一貫した書式設定と問題のグループ化のため、テンプレートのガイダンスに従います
フェーズ 2:問題の分析と分類
フェーズ 1 で特定したすべての問題について、最適化の種類ごとに分類します:
すべての問題を最適化の種類ごとに分類してください:
| 問題の種類 | カテゴリー | 想定される効果 |
|---|---|---|
| ネストされたループ、O(n²) の計算量 | アルゴリズムの最適化 | 10-1000x |
| 高コストな計算の繰り返し | キャッシュとメモ化 | 2-100x |
| メモリ割り当ての多いコード | メモリ割り当ての削減 | 1.5-5x |
| 連続アクセスの違反 | メモリ局所性 | 1.5-3x |
| 過剰な I/O 操作 | I/O の最適化 | 5-50x |
| 非同期処理のブロッキング | I/O の最適化 | 2-10x |
| ループ内のプロパティアクセス | キャッシュとメモ化 | 1.2-2x |
問題の分類に関するクイックリファレンス:
問題とカテゴリーの詳細な対応付け、およびアンチパターンの検出については、references/quick-reference.md を読み込みます。
出力: すべての問題を最適化カテゴリーごとに分類した一覧を作成します。フェーズ 1 で見つかった内容を絞り込んだり、優先順位を付けたりせず、すべて列挙してください。
フェーズ 3:パフォーマンスパターンを使用して最適化する
ステップ 1: フェーズ 2 の分析に基づき、ボトルネックのカテゴリを特定します。
ステップ 2: 該当するカテゴリの必須リファレンスを読み込みます。範囲制限を設けず、各ファイルを最後まで読みます。
| カテゴリ | 必須ファイル | 任意 | 読み込まないファイル |
|---|---|---|---|
| アルゴリズム(O(n²)、ネストしたループ、ルックアップの繰り返し) | reduce-looping.md reduce-branching.md | — | memoization、caching、I/O、allocation |
| キャッシュ(ループ内のプロパティアクセス、計算の繰り返し) | memoization.md cache-property-access.md | cache-storage-api.md(Storage API の場合) | I/O、allocation |
| I/O(非同期処理のブロック、過剰な I/O 操作) | batching.md defer-await.md | — | algorithmic、memory |
| メモリ(割り当ての多さ、GC の負荷) | object-operations.md avoid-allocations.md | — | I/O、caching |
| 局所性(逐次アクセスの違反、キャッシュミス) | predictable-execution.md | — | その他すべて |
| 安全性(上限のないループ、制御不能なキュー) | bounded-iteration.md | — | その他すべて |
| マイクロ最適化(ホットパスの微調整、1.1~2 倍の改善) | currying.md performance-misc.md | — | その他すべて(アルゴリズム上の修正後にのみ適用) |
注:
- ボトルネックが複数のカテゴリにまたがる場合は、該当するすべてのカテゴリのリファレンスを読み込みます
- マイクロ最適化を適用するのは、ボトルネックがホットパスにあり、アルゴリズム上の最適化をすでに適用していて、さらに 1.1~2 倍のパフォーマンス向上が必要な場合に限ります
ステップ 3:最適化中にクイックリファレンスを確認する
AGENTS.md を読み込み、カテゴリ別に整理されたルールの要約を確認します。上記の詳細なリファレンスファイルのパターンを実装する際のクイックリファレンスとして活用します。
パターンを体系的に適用します:
- 特定した問題カテゴリのリファレンスファイルを読み込みます
- ❌/✅ の例を確認し、一致するパターンを探します
- 正しさを保つため、変更を最小限に抑えて最適化を適用します
- 最適化の内容と参照したパターンを説明するコメントを追加します
最適化の例:
// ❌ Before: O(n²) - nested iteration
for (const user of users) {
const items = allItems.filter(item => item.userId === user.id);
process(items);
}
// ✅ After: O(n) - single pass with Map lookup
// Performance: reduce-looping.md - build lookup once pattern
const itemsByUser = new Map<string, Item[]>();
for (const item of allItems) {
if (!itemsByUser.has(item.userId)) {
itemsByUser.set(item.userId, []);
}
itemsByUser.get(item.userId)!.push(item);
}
for (const user of users) {
const items = itemsByUser.get(user.id) ?? [];
process(items);
}
フェーズ 4:改善を検証する
パフォーマンス向上を測定します:
- 同じ入力を使用してプロファイラーを再実行します
- 実行時間の割合を最適化前後で比較します
- 高速化率を記録します(例:「2.3 倍高速」)
正しさを検証します:
- 既存のテストスイートを実行します。すべてのテストに合格する必要があります
- 最適化によって影響を受けるエッジケースのテストを新たに追加します
- ユーザー向けの機能を手動でテストします
最適化を記録します:
// Performance optimization applied: 2026-01-28
// Issue: Nested iteration causing O(n²) complexity with 10k items
// Pattern: reduce-looping.md - Map-based lookup
// Speedup: 145x faster (5200ms → 36ms)
// Verified: All tests pass, manual QA complete
最適化を維持するかどうかの判断:
- 10 倍を超える高速化: テストに合格すれば常に維持します
- 2~10 倍の高速化: テストに合格し、コードの保守性が保たれる場合は維持します
- 1.2~2 倍の高速化: ホットパス(1 秒あたり 1,000 回を超える実行)またはリアルタイムシステムの場合は維持します
- 1.05~1.2 倍の高速化: 変更がごくわずかな場合、または重要なレンダリング/アニメーションループの場合に限り維持します
- 1.05 倍未満の高速化: 可読性も向上する場合を除き、元に戻します
リアルタイムシステム(60fps のレンダリング、ライブデータの可視化): 重要なホットパスでは、1.05 倍の改善でも効果があります。フレームタイミングプロファイラーを使用して、フレーム予算(60fps では 16.67ms)への影響を検証します。
テストが失敗した場合: 最適化を修正するか、元に戻します。パフォーマンスの問題もバグです。
自由度の調整
最適化による効果に応じて、ガイダンスの具体性を調整します:
| 最適化の種類 | 自由度 | ガイダンスの形式 | 例 |
|---|---|---|---|
| アルゴリズム(10倍以上の改善) | 中程度 | 複数の有効なアプローチを示し、制約に応じて選択 | 「O(1)で検索するにはMap、重複を除去するにはSetを使用します」 |
| キャッシュ(2~10倍の改善) | 中程度 | 例を含むパターンと、キャッシュ無効化戦略 | 「ライフサイクルがソースオブジェクトと一致する場合は、WeakMapでメモ化します」 |
| マイクロ最適化(1.1~2倍) | 低い | リファレンスにある正確なパターンを使用し、まず測定する | 「ループ内でarray.lengthをキャッシュします:for (let i = 0, len = arr.length; ...)」 |
判断基準:「高速化の効果と保守コストはどれくらいですか?」
- 10倍以上の高速化 → 複雑さに見合う価値があります。パターンを示し、自由度は中程度にします
- 2~10倍の高速化 → 測定結果で正当性を示し、自由度は中程度にします
- 1.2~2倍の高速化 → ホットパスやリアルタイムシステムでは価値があります。正確なパターンを示し、自由度は低くします
- 1.05~1.2倍の高速化 → 変更がごく簡単な場合、または重要なホットパス(60fpsの描画など)に限ります
重要な注意事項
- すべてを監査する方針 - パフォーマンス上のアンチパターンがないか、すべてのコードを監査します。ユーティリティ関数、フォーマッター、パーサー、バリデーターは、一見単純でも、ループ内やリアルタイムシステムで頻繁に呼び出されます。使用頻度を推測で判断しないでください。
- すべての検出結果を報告する - プロファイリングデータの有無にかかわらず、体系的な静的解析を行い、予想される改善幅とともに、すべてのアンチパターンを特定して報告します。「深刻度」や「優先度」を基準に除外しないでください。
- リファレンスファイルは権威ある情報源です - references/ にあるパターンは検証済みです。測定結果で誤りが証明されない限り、正確に従ってください。
- ホットパスの定義 - ユーザー操作1回あたり1,000回を超えて実行されるコード、またはサーバーのコンテキストで1秒あたり100回を超えて実行されるコードです。リアルタイムシステム(60fpsの描画、ライブ可視化など)では、1フレームあたり1msを超えて消費するクリティカルな描画ループ内の関数がホットパスです。
- リアルタイムシステムには、より厳しい要件があります - 60fpsのフレーム予算は16.67ms、120fpsでは8.33msです。ホットパスでは、1.05倍の改善でも価値があります。実行時間の合計だけでなく、フレーム時間を計測してプロファイリングしてください。
- リグレッションテスト - パフォーマンス最適化では、エッジケースで微妙なバグが発生することがよくあります。最適化の前にテストを追加してください。
- メモリのプロファイリングも重要です - メモ化やキャッシュなどの最適化では、速度と引き換えにメモリを使用することがあります。本番環境でのメモリ使用量を監視してください。特に、長時間稼働するリアルタイムアプリケーションでは重要です。
クイック判断ツリー
この表を使って、どの最適化カテゴリが該当するかをすばやく特定してください。
すべてを監査する:現在の使用状況にかかわらず、コード内のパフォーマンス上のアンチパターンをすべて特定します。期待される改善効果とともに、すべての検出結果を報告してください。
| 次のような場合... | 根本原因 | 最適化カテゴリ | 期待される改善効果 |
|---|---|---|---|
同じデータに対するネストされた for ループ | O(n²) の計算量 | アルゴリズム(ループの削減) | 10~1000倍 |
.filter() の後に .find() または .map() を実行 | データを複数回走査 | アルゴリズム(ループの削減) | 2~10倍 |
array.find() または .includes() を何度も実行 | O(n) の線形探索 | アルゴリズム(ループの削減、Set/Map の使用) | 10~100倍 |
同じ変数に対する if/else チェーンが多い | 分岐の多いコード | アルゴリズム(分岐の削減) | 1.5~3倍 |
| 同じ入力で同じ関数を繰り返し呼び出す | 冗長な計算 | キャッシュ(メモ化) | 2~100倍 |
ループ内で obj.prop.nested.deep に複数回アクセス | プロパティアクセスのオーバーヘッド | キャッシュ(プロパティアクセスのキャッシュ) | 1.2~2倍 |
ループ内で localStorage.getItem() または sessionStorage を使用 | ループ内の高コストな I/O | キャッシュ(ストレージ API のキャッシュ) | 5~20倍 |
複数の await fetch() を順番に実行 | I/O の逐次処理によるブロック | I/O(バッチ処理、await の遅延) | 2~10倍 |
結果が不要な可能性のある条件分岐の前に await を実行 | 早すぎる非同期処理の中断 | I/O(await の遅延) | 1.5~3倍 |
オブジェクトスプレッド {...obj} または配列スプレッド [...arr] を多用 | メモリ割り当てのオーバーヘッド | メモリ(割り当ての回避) | 1.5~5倍 |
| 高頻度で実行されるループ内でオブジェクトや配列を作成 | メモリ割り当てによる GC 負荷 | メモリ(割り当ての回避) | 2~5倍 |
変更しても問題ない場面で Object.assign() またはスプレッド構文を使用 | 不要なイミュータブル処理のコスト | メモリ(オブジェクト操作) | 1.5~3倍 |
| 配列要素に順不同でアクセス | キャッシュ局所性の問題 | メモリ局所性(予測可能な実行) | 1.5~3倍 |
while(true) または上限のないキューの増大 | リソース使用量の暴走 | 安全性(反復回数の制限) | クラッシュを防止 |
| 最初の N 個の引数の大半が同じ状態で関数を呼び出す | 引数の受け渡しの繰り返し | マイクロ最適化(カリー化) | 1.1~1.5倍 |
高頻度で実行されるループ内に try/catch がある | V8 の最適化解除 | マイクロ最適化(パフォーマンスその他) | 3~5倍 |
ループ内で + による文字列連結 | 二次的な文字列コピー | マイクロ最適化(パフォーマンスその他) | 2~10倍 |
この表の使い方:
- プロファイラーで特定したボトルネックからパターンを識別します
- 「次のような場合...」列で該当する行を見つけます
- フェーズ 3 の対応する最適化カテゴリに進みます
- そのカテゴリの必須リファレンスファイルを読み込みます