Remove speed_improvement_proposal.md
This commit is contained in:
@@ -1,305 +0,0 @@
|
||||
# ComfyUI-QwenImageLoraLoader 速度改善提案書
|
||||
|
||||
**作成日**: 2026年1月7日
|
||||
**分析対象**: ComfyUI-QwenImageLoraLoader ノード全体
|
||||
|
||||
---
|
||||
|
||||
## 概要
|
||||
|
||||
本提案書は、ComfyUI-QwenImageLoraLoaderノードにおける潜在的な速度改善ポイントを分析し、具体的な改善案を提示するものである。
|
||||
|
||||
---
|
||||
|
||||
## 1. 現状分析
|
||||
|
||||
### 1.1 主要ファイル構成
|
||||
|
||||
| ファイル | 行数 | 役割 |
|
||||
|---------|------|------|
|
||||
| `nunchaku_code/lora_qwen.py` | 1521行 | LoRA合成・適用のコアロジック |
|
||||
| `wrappers/qwenimage.py` | 387行 | ComfyUI用モデルラッパー |
|
||||
| `wrappers/zimageturbo.py` | 478行 | Z-Image-Turbo用ラッパー |
|
||||
| `nodes/lora/qwenimage.py` | 382行 | LoRAローダーノード定義 |
|
||||
|
||||
### 1.2 処理フロー
|
||||
|
||||
```
|
||||
[LoRAローダーノード]
|
||||
↓
|
||||
[モデルラッピング(初回のみ)]
|
||||
↓
|
||||
[forward()呼び出し]
|
||||
↓
|
||||
[LoRA変更検出]
|
||||
↓
|
||||
[compose_loras_v2() → LoRA合成]
|
||||
↓
|
||||
[モデル推論実行]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 速度ボトルネック分析
|
||||
|
||||
### 2.1 【最重要】 `lora_qwen.py` - LoRAファイルの重複読み込み
|
||||
|
||||
**場所**: `compose_loras_v2()` 関数(1191-1210行)
|
||||
|
||||
**問題点**:
|
||||
```python
|
||||
# 1回目: デバッグログ用(1193行目)
|
||||
first_lora_state_dict = _load_lora_state_dict(first_lora_path_or_dict)
|
||||
for key in first_lora_state_dict.keys():
|
||||
parsed_res = _classify_and_map_key(key) # キー分類処理
|
||||
...
|
||||
|
||||
# 2回目: 実際の処理用(1210行目)
|
||||
for lora_path_or_dict, strength in lora_configs:
|
||||
lora_state_dict = _load_lora_state_dict(lora_path_or_dict) # ← 同じファイルを再読み込み!
|
||||
for key, value in lora_state_dict.items():
|
||||
parsed = _classify_and_map_key(key) # ← キー分類処理も再実行!
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> **同一LoRAファイルを2回読み込み、キー分類処理も2回実行している**
|
||||
|
||||
**改善案**:
|
||||
1. **読み込み結果のキャッシュ再利用**
|
||||
- 1回目で読み込んだ`first_lora_state_dict`を2回目のループで再利用
|
||||
|
||||
2. **`_classify_and_map_key()`結果のメモ化**
|
||||
- `@functools.lru_cache`でキー分類結果をキャッシュ
|
||||
- 同じキーに対する重複計算を完全に排除
|
||||
|
||||
**推定改善効果**: LoRA読み込み・処理時間 **40-50%** 短縮
|
||||
|
||||
---
|
||||
|
||||
### 2.2 【高優先度】 `lora_qwen.py` - キーマッピング処理
|
||||
|
||||
**場所**: `_classify_and_map_key()` 関数(320-374行)
|
||||
|
||||
**問題点**:
|
||||
- すべてのLoRAキーに対して、KEY_MAPPING配列(約30個の正規表現パターン)を**順次走査**している
|
||||
- safetensorsファイルには数百〜数千のキーが含まれるため、O(n×m)の計算量が発生
|
||||
|
||||
**改善案**:
|
||||
1. **プレフィックスベースのディスパッチ導入**
|
||||
- キーの先頭部分(`transformer_blocks`、`single_transformer_blocks`、`layers`など)でグループ分けし、該当グループのパターンのみ検索
|
||||
|
||||
2. **正規表現のプリコンパイルキャッシュ確認**
|
||||
- 現状: `re.compile()`済み ✅
|
||||
- 追加: `functools.lru_cache`デコレータで`_classify_and_map_key()`自体をキャッシュ可能
|
||||
|
||||
**推定改善効果**: LoRA読み込み時間 20-40% 短縮
|
||||
|
||||
---
|
||||
|
||||
### 2.3 【高優先度】 `lora_qwen.py` - LoRA状態辞書の読み込み
|
||||
|
||||
**場所**: `_load_lora_state_dict()` 関数(435-448行)
|
||||
|
||||
**問題点**:
|
||||
```python
|
||||
with safe_open(path, framework="pt", device="cpu") as f:
|
||||
for key in f.keys():
|
||||
state_dict[key] = f.get_tensor(key)
|
||||
```
|
||||
- すべてのテンソルを**CPUメモリに一括読み込み**している
|
||||
- 大容量LoRAファイル(100MB以上)で顕著なメモリ圧迫と読み込み遅延
|
||||
|
||||
**改善案**:
|
||||
1. **遅延読み込み(Lazy Loading)の導入**
|
||||
- 必要なキーのみを逐次読み込む
|
||||
- 未使用キー(LoKR形式など)はスキップ
|
||||
|
||||
2. **メモリマップドI/O**
|
||||
- `mmap=True`オプションの検討(safetensorsサポート状況確認要)
|
||||
|
||||
**推定改善効果**: 大容量LoRA読み込み時間 30-50% 短縮、メモリ使用量削減
|
||||
|
||||
---
|
||||
|
||||
### 2.4 【中優先度】 `qwenimage.py` (wrapper) - LoRA変更検出
|
||||
|
||||
**場所**: `forward()` メソッド(128-142行)
|
||||
|
||||
**現状のコード**:
|
||||
```python
|
||||
loras_changed = False
|
||||
if self._applied_loras is None or len(self._applied_loras) != len(self.loras):
|
||||
loras_changed = True
|
||||
else:
|
||||
for applied, current in zip(self._applied_loras, self.loras):
|
||||
if applied != current:
|
||||
loras_changed = True
|
||||
break
|
||||
```
|
||||
|
||||
**問題点**:
|
||||
- 毎回のforward()でリスト全体を走査
|
||||
- LoRAパス文字列の比較(文字列比較はコストが高い)
|
||||
|
||||
**改善案**:
|
||||
1. **ハッシュベースの変更検出**
|
||||
```python
|
||||
# LoRA追加時にハッシュを計算して保持
|
||||
self._loras_hash = hash(tuple((str(p), s) for p, s in self.loras))
|
||||
```
|
||||
|
||||
2. **フラグベースの明示的通知**
|
||||
- LoRA追加時に`self._loras_dirty = True`を設定
|
||||
- forward()では単純なフラグチェックのみ
|
||||
|
||||
**推定改善効果**: forward()オーバーヘッド 5-10% 削減
|
||||
|
||||
---
|
||||
|
||||
### 2.5 【中優先度】 `lora_qwen.py` - QKV/GLU融合処理
|
||||
|
||||
**場所**: `_fuse_qkv_lora()`(850-987行)、`_fuse_glu_lora()`(792-847行)
|
||||
|
||||
**問題点**:
|
||||
- テンソル連結(`torch.cat`)が複数回発生
|
||||
- ゼロ初期化した大きなテンソルを作成してから部分コピー
|
||||
|
||||
**現状のコード例**:
|
||||
```python
|
||||
B_fused = torch.zeros(out_q + out_k + out_v, 3 * r, dtype=B_q.dtype, device=B_q.device)
|
||||
B_fused[:out_q, :r] = B_q
|
||||
B_fused[out_q: out_q + out_k, r: 2 * r] = B_k
|
||||
B_fused[out_q + out_k:, 2 * r:] = B_v
|
||||
```
|
||||
|
||||
**改善案**:
|
||||
1. **ブロック対角行列の直接構築**
|
||||
- `torch.block_diag()`の利用検討
|
||||
|
||||
2. **事前アロケーションの最適化**
|
||||
- テンソルプール導入でアロケーション削減
|
||||
|
||||
**推定改善効果**: LoRA合成時間 10-15% 短縮
|
||||
|
||||
---
|
||||
|
||||
### 2.6 デバッグログについて
|
||||
|
||||
> [!NOTE]
|
||||
> **開発必須項目のため改善対象外**
|
||||
> 詳細なデバッグログ出力(`compose_loras_v2()` 内のキーインスペクション等)は、開発・デバッグに必須の機能であるため、速度改善の対象外とする。
|
||||
|
||||
---
|
||||
|
||||
### 2.7 【低優先度】 `nodes/lora/qwenimage.py` - 動的インポート
|
||||
|
||||
**場所**: `load_lora()` メソッド(97-110行)
|
||||
|
||||
**問題点**:
|
||||
```python
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"wrappers.qwenimage",
|
||||
os.path.join(lora_loader_dir, "wrappers", "qwenimage.py")
|
||||
)
|
||||
wrappers_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(wrappers_module)
|
||||
```
|
||||
- LoRA適用のたびに**毎回モジュールを読み込み実行**している
|
||||
|
||||
**改善案**:
|
||||
1. **モジュールレベルでの事前インポート**
|
||||
- ファイル先頭で一度だけインポート
|
||||
|
||||
2. **キャッシュ済みモジュールの再利用**
|
||||
```python
|
||||
if "wrappers.qwenimage" not in sys.modules:
|
||||
# インポート実行
|
||||
else:
|
||||
wrappers_module = sys.modules["wrappers.qwenimage"]
|
||||
```
|
||||
|
||||
**推定改善効果**: LoRAノード実行時間 2-5% 短縮
|
||||
|
||||
---
|
||||
|
||||
### 2.8 【低優先度】 `qwenimage.py` (wrapper) - テンソルIDキャッシュ
|
||||
|
||||
**場所**: `__init__()` メソッド(55-61行)
|
||||
|
||||
**現状**:
|
||||
```python
|
||||
self._img_ids_cache = {}
|
||||
self._txt_ids_cache = {}
|
||||
self._linspace_cache_h = {}
|
||||
self._linspace_cache_w = {}
|
||||
```
|
||||
|
||||
**確認事項**:
|
||||
- キャッシュが実際に活用されているか確認が必要
|
||||
- キャッシュヒット率の計測が望ましい
|
||||
|
||||
**改善案**:
|
||||
- 現状のキャッシュ実装が適切であれば変更不要
|
||||
- LRU制限の追加でメモリリーク防止
|
||||
|
||||
---
|
||||
|
||||
### 2.9 【低優先度】 `lora_qwen.py` - モジュールパス解決
|
||||
|
||||
**場所**: `_get_module_by_name()` 関数(382-404行)
|
||||
|
||||
**問題点**:
|
||||
- ドット区切りパスを毎回`split(".")`してトラバース
|
||||
- 同一パスへの繰り返しアクセスでも毎回トラバース
|
||||
|
||||
**改善案**:
|
||||
1. **パス解決結果のキャッシュ**
|
||||
- `@functools.lru_cache`の導入
|
||||
- 注意: モデル構造変更時にキャッシュ無効化が必要
|
||||
|
||||
**推定改善効果**: compose処理時間 3-5% 短縮
|
||||
|
||||
---
|
||||
|
||||
## 3. 優先度別まとめ
|
||||
|
||||
### 即効性が高い改善(推奨順)
|
||||
|
||||
| 優先度 | 改善項目 | 推定効果 | 実装難易度 |
|
||||
|--------|----------|----------|------------|
|
||||
| **最重要** | **LoRA重複読み込み排除** | **40-50%** | **低** |
|
||||
| 高 | キーマッピング最適化 | 20-40% | 中 |
|
||||
| 高 | LoRA遅延読み込み | 30-50% | 中 |
|
||||
| 中 | ハッシュベース変更検出 | 5-10% | 低 |
|
||||
| 中 | QKV/GLU融合最適化 | 10-15% | 中 |
|
||||
| 低 | 動的インポート排除 | 2-5% | 低 |
|
||||
| 低 | モジュールパスキャッシュ | 3-5% | 低 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 備考
|
||||
|
||||
### 4.1 計測の必要性
|
||||
|
||||
上記の改善効果はコード分析に基づく推定値である。実際の改善を行う前に、以下の計測を推奨する:
|
||||
|
||||
1. **プロファイリング実施**
|
||||
- `cProfile`または`py-spy`によるボトルネック特定
|
||||
|
||||
2. **ベンチマークテスト作成**
|
||||
- 改善前後の定量比較
|
||||
|
||||
### 4.2 互換性への配慮
|
||||
|
||||
- 外部APIの変更は最小限に抑える
|
||||
- 既存のワークフローが動作することを確認
|
||||
|
||||
### 4.3 この提案書について
|
||||
|
||||
- 本提案書はコード分析のみに基づく
|
||||
- 実装変更は行っていない
|
||||
- 実装着手前にユーザー様のレビュー・承認を要する
|
||||
|
||||
---
|
||||
|
||||
**以上**
|
||||
Reference in New Issue
Block a user