performance-optimizer について
開発者向けに、性能の測定、ボトルネックの分析、対象を絞った改善、結果の確認を案内するスキルです。フロントエンド、API、データベース、メモリを扱い、正確性と環境に応じた検証を重視します。
主な機能
- 01変更前の性能測定
- 02ブラウザーとNode.jsの負荷分析
- 03データベースクエリと実行計画の調査
- 04一般的なフロントエンド・APIのボトルネック対処
- 05結果の比較と正確性の検証
- 06負荷条件と安全境界への配慮
活用シーン
- 01ページ読み込みや操作の遅さの調査
- 02APIやデータベース処理の短縮
- 03メモリ使用量過多の診断
- 04基準値との比較による改善確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill performance-optimizerインストール後、Claude Code で/performance-optimizerを入力して読み込まれたか確認します。
同梱ファイルREADME.md
詳細
パフォーマンス最適化ツール
パフォーマンスのボトルネックを見つけて解消します。測定し、最適化し、検証します。高速化しましょう。
このスキルを使用する場面
- アプリの動作が遅い、またはカクつく
- ユーザーからパフォーマンスについて不満が寄せられている
- ページの読み込み時間が長い
- API の応答が遅い
- データベースクエリに時間がかかる
- ユーザーが「遅い」「カクつく」「パフォーマンス」「最適化」といった言葉を使っている
最適化のプロセス
1. まず測定する
測定せずに最適化してはいけません。
// Measure execution time
console.time('operation');
await slowOperation();
console.timeEnd('operation'); // operation: 2341ms
測定する項目:
- ページの読み込み時間
- API の応答時間
- データベースクエリの実行時間
- 関数の実行時間
- メモリ使用量
- ネットワークリクエスト
2. ボトルネックを特定する
プロファイリングツールを使って、遅い箇所を特定します。
ブラウザー:
DevTools → Performance tab → Record → Stop
Look for long tasks (red bars)
Node.js:
node --prof app.js
node --prof-process isolate-*.log > profile.txt
データベース:
EXPLAIN ANALYZE SELECT * FROM users WHERE email = '[email protected]';
3. 最適化する
最も遅い箇所から修正します(最大の効果が得られる箇所)。
よくある最適化
データベースクエリ
問題:N+1 クエリ
// Bad: N+1 queries
const users = await db.users.find();
for (const user of users) {
user.posts = await db.posts.find({ userId: user.id }); // N queries
}
// Alternative: batched population; inspect actual generated queries
const users = await db.users.find()
.populate('posts'); // May issue additional batched queries; not necessarily a JOIN
問題:インデックスがない
-- Check slow query
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';
-- A sequential scan may be optimal for small tables/low-selectivity queries.
-- Add index
CREATE INDEX idx_users_email ON users(email);
-- Check again
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';
-- Compare actual cost/latency and write/storage impact.
**問題:SELECT ***
// Bad: Fetches all columns
const users = await db.query('SELECT * FROM users');
// Good: Only needed columns
const users = await db.query('SELECT id, name, email FROM users');
問題:ページネーションがない
// Bad: Returns all records
const users = await db.users.find();
// Good: Paginated
const users = await db.users.find()
.limit(20)
.skip((page - 1) * 20);
API のパフォーマンス
問題:キャッシュがない
// Bad: Hits database every time
app.get('/api/stats', async (req, res) => {
const stats = await db.stats.calculate(); // Slow
res.json(stats);
});
// Good: Cache for 5 minutes
const cache = new Map();
app.get('/api/stats', async (req, res) => {
const cached = cache.get('stats');
if (cached && Date.now() - cached.time < 300000) {
return res.json(cached.data);
}
const stats = await db.stats.calculate();
cache.set('stats', { data: stats, time: Date.now() });
res.json(stats);
});
問題:処理が逐次実行される
// Bad: Sequential (slow)
const user = await getUser(id);
const posts = await getPosts(id);
const comments = await getComments(id);
// Total: 300ms + 200ms + 150ms = 650ms
// Good: Parallel (fast)
const [user, posts, comments] = await Promise.all([
getUser(id),
getPosts(id),
getComments(id)
]);
// Total: max(300ms, 200ms, 150ms) = 300ms
問題:ペイロードが大きい
// Bad: Returns everything
res.json(users); // 5MB response
// Good: Only needed fields
res.json(users.map(u => ({
id: u.id,
name: u.name,
email: u.email
}))); // 500KB response
フロントエンドのパフォーマンス
問題:不要な再レンダリング
// Bad: Re-renders on every parent update
function UserList({ users }) {
return users.map(user => <UserCard user={user} />);
}
// Good: Memoized
const UserCard = React.memo(({ user }) => {
return <div>{user.name}</div>;
});
問題:バンドルが大きい
// Bad: Imports entire library
import _ from 'lodash'; // 70KB
// Good: Import only what you need
import debounce from 'lodash/debounce'; // 2KB
問題:コード分割がない
// Bad: Everything in one bundle
import HeavyComponent from './HeavyComponent';
// Good: Lazy load
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
問題:画像が最適化されていない
<img src="photo.jpg" />
<img
src="photo-small.webp"
srcset="photo-small.webp 400w, photo-large.webp 800w"
loading="lazy"
width="400"
height="300"
/>
アルゴリズムの最適化
問題:効率の悪いアルゴリズム
// Bad: O(n²) - nested loops
function findDuplicates(arr) {
const duplicates = [];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) duplicates.push(arr[i]);
}
}
return duplicates;
}
// Good: O(n) - single pass with Set
function findDuplicates(arr) {
const seen = new Set();
const duplicates = new Set();
for (const item of arr) {
if (seen.has(item)) duplicates.add(item);
seen.add(item);
}
return Array.from(duplicates);
}
問題:同じ計算の繰り返し
// Bad: Calculates every time
function getTotal(items) {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
// Called 100 times in render
// Good: Memoized
const getTotal = useMemo(() => {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}, [items]);
メモリの最適化
問題:メモリリーク
// Bad: Event listener not cleaned up
useEffect(() => {
window.addEventListener('scroll', handleScroll);
// Memory leak!
}, []);
// Good: Cleanup
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
問題:メモリ内のデータが大きい
// Bad: Loads entire file into memory
const data = fs.readFileSync('huge-file.txt'); // 1GB
// Good: Stream it
const stream = fs.createReadStream('huge-file.txt');
stream.on('data', chunk => process(chunk));
正確性とベンチマークの注意点
2 つの findDuplicates の例では、出力の意味が異なります。ネストしたループは一致するペアごとに項目を繰り返し出力しますが、Set 版は重複した値をそれぞれ 1 回だけ出力します(また、NaN の等価性の扱いも異なります)。アルゴリズムを変更する前に、必要な仕様を明確にしてください。統計情報のキャッシュが適しているのは、共有の公開データに限られます。ユーザーやテナント固有の結果には、認可に対応したキーと無効化の仕組みが必要です。ストリーミングでは、エラーやバックプレッシャー、および非同期処理に対応する必要があります。EXPLAIN ANALYZE はクエリを実行するため、使用前に対象と負荷を確認してください。以下の時間やサイズはすべて例示であり、実際に観測された結果ではありません。代表的な複数回の実行を、ワークロード、環境、サンプルの統計情報と照らし合わせて比較し、リグレッションも確認してください。
効果の測定
最適化の前後で必ず測定します。
// Before optimization
console.time('query');
const users = await db.users.find();
console.timeEnd('query');
// query: 2341ms
// After optimization (added index)
console.time('query');
const users = await db.users.find();
console.timeEnd('query');
// query: 23ms
// Improvement: 100x faster!
パフォーマンスの目標値
目標値を設定します。
Page Load: < 2 seconds
API Response: < 200ms
Database Query: < 50ms
Bundle Size: < 200KB
Time to Interactive: < 3 seconds
ツール
ブラウザー:
- Chrome DevTools の Performance タブ
- Lighthouse(監査)
- Network タブ(ウォーターフォール)
Node.js:
node --prof(プロファイリング)clinic(診断)autocannon(負荷テスト)
データベース:
EXPLAIN ANALYZE(クエリプラン)- スロークエリログ
- データベースプロファイラー
モニタリング:
- New Relic
- Datadog
- Sentry Performance
すぐにできる改善
効果の大きい簡単な最適化:
- よくクエリされるカラムにデータベースインデックスを追加する
- サーバーでgzip 圧縮を有効にする
- 時間のかかる処理にキャッシュを追加する
- 画像や重いコンポーネントを遅延読み込みする
- 静的アセットにCDN を使用する
- JavaScript/CSS を圧縮して縮小する
- 使用していない依存関係を削除する
- すべてのデータを読み込む代わりにページネーションを使用する
- 画像を最適化する(WebP、適切なサイズ)
- サーバーでHTTP/2 を有効にする
最適化チェックリスト
- 現在のパフォーマンスを測定した
- ボトルネックを特定した
- 最適化を適用した
- 改善を測定した
- 機能が引き続き動作することを確認した
- 新しいバグが発生していない
- 変更を文書化した
最適化すべきでない場合
- 早すぎる最適化(実際に遅くなってから最適化する)
- 微細な最適化(ページの読み込みに 5 秒かかるのに 1 ミリ秒短縮する)
- わずかな速度向上より、読みやすいコードのほうが重要
- すでに十分な速度が出ている場合
基本原則
- 最適化の前に測定する
- 最大のボトルネックから解消する
- 改善を証明するために、最適化の後にも測定する
- わずかな改善のために読みやすさを犠牲にしない
- 本番に近い環境でプロファイリングする
- 80/20 の法則を考慮する(コードの 20%が遅さの 80%を引き起こす)
関連スキル
@database-design- クエリの最適化@codebase-audit-pre-push- コードレビュー@bug-hunter- デバッグ
制限事項
- タスクが上記の説明にある範囲に明確に該当する場合にのみ、このスキルを使用してください。
- 出力を、環境固有の検証、テスト、または専門家によるレビューの代わりとして扱わないでください。
- 必要な入力、権限、安全上の境界、または成功基準が不足している場合は、作業を中断して確認を求めてください。