はじめに
PythonのOSSプロジェクトで、3つのOS × 5つのPythonバージョン × 複数のテストパターンを実行すると、CI/CDの実行時間が大きな課題になります。本記事では、47個の並列ジョブを15分から5-6分に短縮(約65%削減)した実践的な高速化手法を紹介します。
プロジェクト概要
NanaSQLiteというPythonモジュールプロジェクトで以下の構成を実装しています:
- テストジョブ: 15並列(3OS × 5バージョン)
- 同期ベンチマークジョブ: 15並列(3OS × 5バージョン)
- 非同期ベンチマークジョブ: 15並列(3OS × 5バージョン)
- 集計・サマリージョブ: 2個
- 合計: 47ジョブ
高速化手法1: UVによる依存関係インストールの爆速化
pipの8-10倍高速なパッケージマネージャーuvを導入しました。
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true # キャッシュを有効化
- name: Install dependencies
run: |
uv pip install --system pytest pytest-xdist pytest-benchmark
uv pip install --system -e .
ポイント
-
astral-sh/setup-uv@v7が自動的にキャッシュを管理 -
--systemフラグでグローバル環境にインストール - 従来のpipと比較して依存関係解決が圧倒的に高速
高速化手法2: pytest-xdistによる並列テスト実行
テストを複数コアで並列実行することで大幅に時間短縮しました。
- name: Run tests
run: |
pytest tests/ -v -n auto --ignore=tests/test_benchmark.py
ポイント
-
-n auto: GitHub-hostedランナーの利用可能コア数を自動検出(通常2コア) - テストの独立性が保たれていれば、ほぼ2倍の高速化を実現
- ベンチマークテストは
--ignoreで除外(並列実行すると正確な計測ができないため)
高速化手法3: 大規模マトリックス設計
複数のOS・Pythonバージョンを効率的にテストするマトリックス戦略を採用しました。
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
ポイント
-
fail-fast: false: 1つが失敗しても全ての組み合わせを実行 - GitHub Educationで並列実行数40(通常20)を活用
- macOSは5並列制限があるため段階的に実行される
高速化手法4: 段階実行による効率化
テスト失敗時に無駄なベンチマークを実行しないよう、needsで依存関係を設定しました。
jobs:
test:
# ... テスト実行
benchmark:
needs: test # テスト成功後に実行
# ... 同期ベンチマーク
async-benchmark:
needs: test # テスト成功後に実行
# ... 非同期ベンチマーク
benchmark-summary:
needs: [benchmark, async-benchmark]
# ... 結果集計
実行フロー
- Stage 1: テスト(15並列)→ 約2-3分
- Stage 2: ベンチマーク(30並列)→ 約2-3分
- Stage 3: 集計・サマリー(2直列)→ 約数十秒
高速化手法5: Artifact収集とサマリー自動生成
全ジョブの結果を自動的に収集し、見やすいサマリーを生成します。
benchmark-summary:
needs: [benchmark, async-benchmark]
runs-on: ubuntu-latest
steps:
- name: Download sync benchmark results
uses: actions/download-artifact@v6
with:
pattern: benchmark-results-*
path: benchmark-results
- name: Generate Combined Benchmark Summary
run: |
echo "## 📊 Performance Benchmarks" >> $GITHUB_STEP_SUMMARY
python3 scripts/parse_benchmark.py benchmark-results sync >> $GITHUB_STEP_SUMMARY
出力内容
- ✅/❌ 各ジョブの成功・失敗ステータス
- 同期/非同期それぞれの最速・最遅・平均実行時間
- OS・Pythonバージョン別の一覧表
完全なワークフロー構成
全体の構成は以下の通りです:
name: NanaSQLite Tests
on:
push:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
uv pip install --system pytest pytest-xdist
uv pip install --system -e .
- name: Run tests
run: pytest tests/ -v -n auto
今後の展望
さらなる改善として以下を検討しています:
GitHub Pagesでのベンチマーク可視化
github-action-benchmarkを使用して、ベンチマーク結果を時系列グラフで可視化。パフォーマンスリグレッションを自動検出できます。
条件付き実行
PR時はUbuntu + 最新Pythonのみ実行し、mainマージ時に全OS・全バージョンをテストする戦略。
セルフホステッドランナー
将来的には自前サーバーで実行し、さらなる高速化とコスト削減を目指します。
まとめ
5つの手法を組み合わせることで、47ジョブの大規模CI/CDを15分から5-6分に短縮しました:
- UV + キャッシュ: 依存関係インストールを爆速化
- pytest-xdist: テストを並列実行
- 大規模マトリックス: 効率的な多環境テスト
- 段階実行: 無駄な実行を削減
- 自動サマリー: 結果の可視化
これらの手法は他のPythonプロジェクトでも応用可能です。特にOSSプロジェクトでは、GitHub Educationを活用することで並列実行数を倍増できるため、積極的に申請することをおすすめします。