mcpField
P
performance-optimizer

performance-optimizer

github.com/sickn33開発49.3k165 回インストール今日更新
リポジトリ

performance-optimizer について

開発者向けに、性能の測定、ボトルネックの分析、対象を絞った改善、結果の確認を案内するスキルです。フロントエンド、API、データベース、メモリを扱い、正確性と環境に応じた検証を重視します。

主な機能

  1. 01変更前の性能測定
  2. 02ブラウザーとNode.jsの負荷分析
  3. 03データベースクエリと実行計画の調査
  4. 04一般的なフロントエンド・APIのボトルネック対処
  5. 05結果の比較と正確性の検証
  6. 06負荷条件と安全境界への配慮

活用シーン

  1. 01ページ読み込みや操作の遅さの調査
  2. 02APIやデータベース処理の短縮
  3. 03メモリ使用量過多の診断
  4. 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

すぐにできる改善

効果の大きい簡単な最適化:

  1. よくクエリされるカラムにデータベースインデックスを追加する
  2. サーバーでgzip 圧縮を有効にする
  3. 時間のかかる処理にキャッシュを追加する
  4. 画像や重いコンポーネントを遅延読み込みする
  5. 静的アセットにCDN を使用する
  6. JavaScript/CSS を圧縮して縮小する
  7. 使用していない依存関係を削除する
  8. すべてのデータを読み込む代わりにページネーションを使用する
  9. 画像を最適化する(WebP、適切なサイズ)
  10. サーバーでHTTP/2 を有効にする

最適化チェックリスト

  • 現在のパフォーマンスを測定した
  • ボトルネックを特定した
  • 最適化を適用した
  • 改善を測定した
  • 機能が引き続き動作することを確認した
  • 新しいバグが発生していない
  • 変更を文書化した

最適化すべきでない場合

  • 早すぎる最適化(実際に遅くなってから最適化する)
  • 微細な最適化(ページの読み込みに 5 秒かかるのに 1 ミリ秒短縮する)
  • わずかな速度向上より、読みやすいコードのほうが重要
  • すでに十分な速度が出ている場合

基本原則

  • 最適化の前に測定する
  • 最大のボトルネックから解消する
  • 改善を証明するために、最適化の後にも測定する
  • わずかな改善のために読みやすさを犠牲にしない
  • 本番に近い環境でプロファイリングする
  • 80/20 の法則を考慮する(コードの 20%が遅さの 80%を引き起こす)

関連スキル

  • @database-design - クエリの最適化
  • @codebase-audit-pre-push - コードレビュー
  • @bug-hunter - デバッグ

制限事項

  • タスクが上記の説明にある範囲に明確に該当する場合にのみ、このスキルを使用してください。
  • 出力を、環境固有の検証、テスト、または専門家によるレビューの代わりとして扱わないでください。
  • 必要な入力、権限、安全上の境界、または成功基準が不足している場合は、作業を中断して確認を求めてください。

コメント