-
Notifications
You must be signed in to change notification settings - Fork 1
Parallel Processing Performance
wikigenは、複数リポジトリのWiki生成において高いスループットを実現するため、2層構造の並列処理機構を採用している。本ページでは、-pフラグによるリポジトリ(プロジェクト)レベルの並列化と、-ppフラグによるページレベルの並列化の実装詳細、セマフォを用いた並行数制御、sync.WaitGroupによるゴルーチン同期、およびsync/atomicを活用したスレッドセーフな進捗追跡について解説する。CLI全般の仕様についてはCLI Usage & Commandsを、進捗表示の詳細についてはProgress Tracking & Output Modesを参照のこと。
wikigenの並列処理は「プロジェクトレベル」と「ページレベル」の2階層で構成される。プロジェクトレベルでは複数のWikiプロジェクトを同時に処理し、ページレベルでは1つのプロジェクト内の複数ページを同時に生成する。これらの並列度はそれぞれ-pフラグと-ppフラグで独立して制御できる。
flowchart TD
A[mainループ] --> B[プロジェクトセマフォ\nsem = make chan struct p]
B --> C[goroutine: generateWiki]
C --> D[ページセマフォ\npageSem = make chan struct pp]
D --> E[goroutine: claudeCall]
E --> F[ページファイル書き込み]
F --> G[pageSem 解放]
G --> H{全ページ完了?}
H -- No --> D
H -- Yes --> I[pageWg.Wait]
I --> J[sem 解放]
J --> K{全プロジェクト完了?}
K -- No --> B
K -- Yes --> L[wg.Wait\n結果集計]
Sources: main.go:949-950
flag.IntVar(¶llel, "p", envOrDefaultInt("WIKI_PARALLEL", 1), "parallel projects/repos")
flag.IntVar(&pageParallel, "pp", envOrDefaultInt("WIKI_PAGE_PARALLEL", 3), "parallel pages per project")| フラグ | 環境変数 | デフォルト値 | 説明 |
|---|---|---|---|
-p |
WIKI_PARALLEL |
1 |
同時処理するプロジェクト数 |
-pp |
WIKI_PAGE_PARALLEL |
3 |
プロジェクトあたりの同時ページ生成数 |
CLIフラグは対応する環境変数より優先される。環境変数のみ設定されている場合はその値が使用される。両方未設定の場合はデフォルト値が適用される。設定の優先順位の詳細についてはConfiguration & Environmentを参照のこと。
起動時には有効な並列度設定が標準エラー出力に表示される。
Sources: main.go:1054-1061
fmt.Fprintf(os.Stderr, "🚀 Processing %d wikis (parallel: %d, pages: %d, model: %s%s)\n\n",
len(tasks), parallel, pageParallel, ...)Sources: main.go:1063-1095
プロジェクトのタスクリストをイテレートする際、バッファ付きチャネルをセマフォとして使用し、同時実行数を-pの値に制限する。
sem := make(chan struct{}, parallel)
var wg sync.WaitGroup
var mu sync.Mutex
var failed []string
var results []*WikiResult
for _, t := range tasks {
wg.Add(1)
sem <- struct{}{} // セマフォ獲得(空きがなければブロック)
go func(t task) {
defer wg.Done()
defer func() { <-sem }() // セマフォ解放
result, err := generateWiki(...)
mu.Lock()
// results / failed スライスへの書き込み
mu.Unlock()
}(t)
}
wg.Wait()パターンの解説:
-
sem <- struct{}{}: チャネルへの送信でセマフォを獲得する。チャネルが満杯の場合はブロックし、空きができるまで待機する。 -
defer func() { <-sem }(): ゴルーチン終了時にチャネルから受信してセマフォを解放する。deferを使用することでパニック時にも確実に解放される。 -
mu.Lock() / mu.Unlock(): 共有スライス(results、failed)への並行書き込みをsync.Mutexで保護する。
Sources: main.go:1000-1003
type task struct {
name string
repos []string
}各タスクはプロジェクト名とリポジトリのURLまたはローカルパスのリストを保持する。タスクの構築についてはInput Formats & Repository Configurationを参照のこと。
sequenceDiagram
participant M as main
participant S as sem (channel)
participant G1 as goroutine 1
participant G2 as goroutine 2
participant G3 as goroutine 3
M->>S: sem <- struct{}{}
M->>G1: go generateWiki(task1)
M->>S: sem <- struct{}{} (空きあり)
M->>G2: go generateWiki(task2)
M->>S: sem <- struct{}{} (ブロック, p=2の場合)
G1-->>S: <-sem (解放)
S-->>M: ブロック解除
M->>G3: go generateWiki(task3)
G2-->>S: <-sem (解放)
G3-->>S: <-sem (解放)
M->>M: wg.Wait()
Sources: main.go:495
func generateWiki(claudePath, model string, projectName string, repos []string,
token, language, outputDir, cloneDir string,
pageParallel int, dryRun bool, localDir string,
progress *Progress) (*WikiResult, error)pageParallelパラメータにより、各プロジェクトのページ生成ゴルーチン数を制御する。
Sources: main.go:594-646
var pageDone int32
pageSem := make(chan struct{}, pageParallel)
var pageWg sync.WaitGroup
for i := range allPages {
pageWg.Add(1)
pageSem <- struct{}{}
go func(idx int) {
defer pageWg.Done()
defer func() { <-pageSem }()
page := &allPages[idx]
// 進捗更新(atomic操作)
pagePct := int(atomic.LoadInt32(&pageDone)) * 100 / len(allPages)
progress.set(projectName, fmt.Sprintf("📝 %d/%d (%d%%) %s",
atomic.LoadInt32(&pageDone)+1, len(allPages), pagePct, page.Title))
// リトライつきのClaude呼び出し(最大3回)
for attempt := 1; attempt <= maxRetries; attempt++ {
_, err := claudeCall(claudePath, model, repoDirs, "", pagePrompt(...), wikiDir)
// ファイル書き込み確認
}
atomic.AddInt32(&pageDone, 1)
}(i)
}
pageWg.Wait()ポイント:
- プロジェクトレベルと同一のセマフォパターンを採用
-
atomic.AddInt32/atomic.LoadInt32により、sync.Mutexを使わずにカウンタを安全に操作 - ページ生成失敗時は最大3回まで自動リトライ(詳細はError Handling & Retry Mechanismを参照)
flowchart TD
A[generateWiki呼び出し] --> B[Phase 1: 構造決定\nClaude呼び出し]
B --> C[allPages 構築]
C --> D[pageSem 初期化\nmake chan struct pp]
D --> E[pageWg 初期化]
E --> F{ページあり?}
F -- Yes --> G[pageWg.Add 1]
G --> H[pageSem <- struct{}{}]
H --> I[go func idx]
I --> J[claudeCall\nページ生成]
J --> K{成功?}
K -- No --> L{retry < 3?}
L -- Yes --> J
L -- No --> M[エラー記録]
K -- Yes --> N[atomic.AddInt32\npageDone++]
M --> N
N --> O[pageSem 解放]
O --> P[pageWg.Done]
F -- No --> Q[pageWg.Wait]
P --> Q
Q --> R[WikiResult 返却]
Sources: main.go:21-58
type Progress struct {
mu sync.Mutex
totalItems int
doneItems int32
current map[string]string
}| フィールド | 型 | 役割 |
|---|---|---|
mu |
sync.Mutex |
currentマップへの排他アクセス |
totalItems |
int |
全プロジェクト数(不変) |
doneItems |
int32 |
完了カウンタ(atomic操作) |
current |
map[string]string |
現在処理中のプロジェクトと状態 |
Sources: main.go:33-56
func (p *Progress) done(name string) {
atomic.AddInt32(&p.doneItems, 1) // ロック不要でカウンタ更新
p.mu.Lock()
defer p.mu.Unlock()
delete(p.current, name) // マップ操作はロックが必要
p.print()
}
func (p *Progress) print() {
done := int(atomic.LoadInt32(&p.doneItems)) // ロック不要で読み取り
pct := done * 100 / p.totalItems
// 進捗を標準エラーに出力
}doneItemsはint32型のatomic操作で更新・読み取りを行い、sync.Mutexのオーバーヘッドを回避している。一方、currentマップはスライスやマップの並行操作がGoのランタイムによりサポートされないため、sync.Mutexで保護する。
classDiagram
class Progress {
+sync.Mutex mu
+int totalItems
+int32 doneItems
+map current
+set(name, status)
+done(name)
-print()
}
class sync.Mutex {
+Lock()
+Unlock()
}
class sync.WaitGroup {
+Add(delta)
+Done()
+Wait()
}
Progress --> sync.Mutex : ミューテックス保護
Progress ..> sync.WaitGroup : 連携使用
flowchart TD
subgraph メインループ
A[tasks スライス] --> B[sem = make chan struct p]
B --> C[wg sync.WaitGroup]
end
subgraph プロジェクトgoroutine
D[generateWiki] --> E[Phase1: 構造決定]
E --> F[pageSem = make chan struct pp]
F --> G[pageWg sync.WaitGroup]
end
subgraph ページgoroutine
H[claudeCall] --> I[ファイル検証]
I --> J[atomic.AddInt32\npageDone++]
end
subgraph 共有状態
K[Progress struct\nmu + doneItems]
L[results slice\nmu保護]
M[failed slice\nmu保護]
end
C --> D
G --> H
J --> K
D --> L
D --> M
--retryフラグで起動するretryFailedPages関数は、pageParallelパラメータを受け取るが、失敗ページの再生成は現在プロジェクトごとに逐次処理される。
Sources: main.go:778-912
func retryFailedPages(claudePath, model, language, outputDir, cloneDir string, pageParallel int) {
// 出力ディレクトリをスキャンして失敗ページを検出
// "Content generation failed" マーカーまたは200バイト未満のファイルを対象
for projectDir, pages := range projectFiles {
for _, page := range pages {
os.Remove(filepath.Join(projectDir, page.filename+".md"))
_, err := claudeCall(...)
// 結果確認
}
}
}失敗ページの検出条件:
- ファイル内容に
"Content generation failed"が含まれる - ファイルサイズが200バイト未満
flowchart TD
A{ユースケース} --> B[単一リポジトリ]
A --> C[少数のプロジェクト\n2〜5件]
A --> D[大量プロジェクト\n10件以上]
B --> E[-p 1 -pp 5\nページ並列を増加]
C --> F[-p 3 -pp 3\nバランス型]
D --> G[-p 5 -pp 2\nプロジェクト並列を重視]
| シナリオ | 推奨 -p
|
推奨 -pp
|
理由 |
|---|---|---|---|
| 単一プロジェクト | 1 | 5〜10 | プロジェクト並列は不要。ページ生成を高速化 |
| 少数プロジェクト(2〜5件) | 2〜3 | 3 | バランス型。APIレート制限に注意 |
| 大量プロジェクト(10件以上) | 4〜5 | 2 | プロジェクト並列を優先。ページ並列は抑制 |
| メモリ制約あり | 1 | 2 | 最小構成 |
-
Claude APIのレート制限:
-pと-ppの積(最大同時Claude呼び出し数)がAPIのレート制限に影響する。p=3, pp=5の場合、最大15の同時リクエストが発生する。 - ローカルリソース: 各Claude呼び出しはサブプロセスを起動するため、高並列設定ではシステムのプロセス数上限に注意が必要。
- git cloneの競合: リポジトリのクローン処理はWiki生成前に行われるため、並列cloneによるディスクI/O競合が発生する場合がある。
-
原子性の保証: ページカウンタは
sync/atomicで保護されているが、resultsおよびfailedスライスへの書き込みはsync.Mutexで保護されている点に留意すること。
Sources: main.go:3-17(syncおよびsync/atomicのimport)
import (
...
"sync"
"sync/atomic"
"time"
)-
CLI Usage & Commands —
-p、-ppフラグを含む全CLIオプションのリファレンス - Architecture & Design — システム全体のアーキテクチャと処理パイプライン
- Wiki Generation Processing Flow — Phase 1(構造決定)とPhase 2(ページ生成)の処理フロー詳細
- Progress Tracking & Output Modes — Progress構造体の表示ロジックとJSONモード
-
Error Handling & Retry Mechanism — リトライ戦略と
_errors.logへの記録 -
Configuration & Environment —
WIKI_PARALLELおよびWIKI_PAGE_PARALLEL環境変数の設定方法
- System Overview
- Architecture & Design
- CLI Usage & Commands
- Configuration & Environment
- Input Formats & Repository Configuration
- Authentication & Git Integration
- Output Format & Wiki Structure
- Error Handling & Retry Mechanism
- Parallel Processing & Performance
- Input Validation & Security
- Build & Deployment
- Claude Code Integration
- Wiki Generation Processing Flow
- Multi-Repository Wiki Support
- Progress Tracking & Output Modes