見出し画像

How to fix torch.compile on Windows (※for WaveSpeed)

2025年8月20日、最適化キャッシュファイルの動的管理について、タスクスケジューラーと組み合わせた手法について追記しました。


Inductor backendによる動作の為に

下記事作成時点では、動作させられなかったtorch.compileを司る「Compile Model+」ノードですが、
(但し、現時点では当記事の内容を反映させ、記事を更新しています)

2025年2月24日、ようやく公式推奨設定であるInductorバックエンドモードでの動作に成功しました。

これでWaveSpeedの全機能を、公式推奨設定で使えることになりました。
(但し、一部のノードに対してはInductor backend自体が非対応である為、cudagraphs backendによる安定重視の設定を組むことになります)

StableFastの時と同様、今回も難易度高かったですけどね…私は、こういうミッションは燃えるのですよ。いつも成功する訳ではありませんが。

尚、今度こそtritonなしでは動作しません。

かつ最新の3.2.0以降のインストールが必須です。3.1.0以前では、Inductor backendは使用できません。

以下の設定は、Cursor先生と相談しながら構築した改造環境ですが、「削っても動作する」部分はあるかもしれません。

かつまた、これが最善の設定ではなく、より公式に近い形での設定も成立しうるのかもしれません。

その前提で記述します。

Ⅰ custom_nodesフォルダにglobal_config.pyファイルを作成

global_configなので、理論上は全Custom nodesに影響してしまうpyですが、記述はtorch.compileに限定しているので、多分他のノードに影響はしないと思いますが…ちなみに、このコードは別の形で実装する事もできる筈(どこかには書くことになる)です、が、私は「これ」以外の方法では、どの形でもうまくいきませんでした。

当初、Cursor先生はこの記述をmain.pyに追記するように指示してきましたが、それをやると、そもそもComfyUIが起動しなくなります。

尚、このファイルの意味は以下のようになっています。

この記述が torch.compile に与える影響を考えると、以下のような動作の変化が生じる可能性があります


1. torch.compile のエラーが抑制される

torch._dynamo.config.suppress_errors = True を設定すると、torch.compile() が内部で発生した コンパイルエラーを無視 して、代わりに Eager Mode(通常のPyTorch実行) にフォールバックするようになります。

通常、torch.compile() は PyTorch のグラフベースのコンパイルを試みますが、最適化がうまくいかない場合はエラーを出して実行を停止します。しかし、この設定を有効にすると:

  • エラーが発生してもクラッシュしない

  • 該当のモデルや関数がコンパイルされず、通常の実行モードで動作する

つまり、コンパイルがうまくいかないノードがあっても、エラーを出さずに動作する可能性がある ということです。


2. ComfyUI における影響

ComfyUI はノードベースのシステムであり、各ノードが torch.compile を使用する可能性があります。
この設定による影響として、以下のようなことが考えられます。

メリット

  • torch.compile で発生するエラーによる ComfyUI のクラッシュを防ぐ

  • 未対応のノードでも実行可能になる(ただし、最適化されない)

デメリット

  • torch.compile の エラーを明示的に知ることができなくなる

  • 本来最適化できるはずの処理が Eager Mode で動作してしまう

  • パフォーマンスが 意図せず悪化 する可能性がある(torch.compile の恩恵を受けられない場合)


3. NODE_CLASS_MAPPINGS = {} の影響

この部分は ComfyUI のカスタムノードを定義するためのマッピング ですが、空の辞書 {} にしているため、特に torch.compile には直接影響を与えません。
ただし、このファイルを ComfyUI の custom_nodes/ に置いた場合、ComfyUI に新しいノードは追加されません


結論

  • torch.compile のエラーが無視され、失敗時に Eager Mode にフォールバックする

  • ComfyUI で torch.compile を使うノードが、意図せずコンパイルされないことがある

  • デバッグ時にエラーが発生しないため、コンパイル最適化の問題を見逃しやすくなる

この設定を使う場合は、torch.compile の動作を確認しながら適用するか、デバッグ目的で一時的に使うのが良い でしょう。

2025年2月25日現在、Inductor backendを完走させる為のメソッドが確立した上は、以下解説する私の設定において必ずしも必須ではありませんが、あった方がプログラムの完走率は上がります。

Ⅱ 環境変数を追加する (sage-attentionも想定)

set PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:32,garbage_collection_threshold:0.8
set TORCH_COMPILE_DEBUG=0
set TORCH_COMPILE_OPT_LEVEL=2
set TORCH_DYNAMO_CONFIG=force_parameter_static_shapes=false
set TRITON_CACHE_DIR=D:\USERFILES\triton (任意のフォルダ指定) 
set TORCH_HOME=D:\USERFILES\torch (任意のフォルダ指定)
set TORCHINDUCTOR_CACHE_DIR=D:\USERFILES\Inductor (任意のフォルダ指定)
set TEMP=D:\USERFILES\Temp (任意のフォルダ指定)
set TEMP=D:\USERFILES\Temp (任意のフォルダ指定)

これは、起動用バッチファイルの冒頭に書きます。また、新しい高速化手段として注目されつつあるsage-attentionも併用する形を見据えての設定も追記しています。各項目の意味は下記の通りです。

①PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:32,garbage_collection_threshold:0.8

max_split_size_mb:32      # CUDAメモリの断片化を防ぐための最大分割サイズ(MB)
garbage_collection_threshold:0.8   # メモリの80%使用時にGCを実行

②TORCH_COMPILE_OPT_LEVEL=2`

0: 最小限の最適化
1: 標準的な最適化
2: 積極的な最適化(推奨)
3: 最大限の最適化(実験的)

③TORCH_LOGS=recompiles,dynamo,inductor

recompiles: 再コンパイルの理由を記録
dynamo: TorchDynamoの動作ログを記録
inductor: Inductorの最適化ログを記録

④TORCH_DYNAMO_CONFIG=force_parameter_static_shapes=false


モデルパラメータの動的な形状変更を許可
再コンパイルの回数を減らす可能性がある

⑤-

これらは、torch.compileの過程で発生するキャッシュファイルや一時ファイルの保存場所を強制的に指定するもので、これらの指定がなくても動作はしますが、新しい高速化手段であるsage-attentionの使用を想定した時に、任意の場所に分離しておいた方が良い為、このように設定します。

また、この時作成したキャッシュ用フォルダに対してユーザーがフルアクセスできるように、フォルダのセキュリティを変更してください。

Ⅲ ノードの設定を使い分ける

①Inductor backend on WaveSpeed

torch.compile機能を使用する場合、基本的には下図のように、WaveSpeedによるCompil Model+ノードをロードした初期状態でのInductor backendで使用します。

②Inductor backend with max-autotune mode on KJNodes

しかし現状、WaveSpeedによるCompile Model+ノードでは、Inductor backendで最大の最適化を実現するmax-autotuneモードが使用出来ません。モードを指定すると、ブログラムがエラーを起こして完走できません。

KJNodesを使用する最大の利点は、下図のように、現状torch.compileにおける最大性能を発揮するInductor backendでのmax-autotune機能が正常に動作する点です。

max-autotuneは、初回ロード時にcompileの為の処理時間は要するものの、推論の時間…つまりサンプラー処理の時間が確実に短縮されます。

この特性は、例えば私のようにRTX4070 12GBクラスのGPUでFlux1での生成を行い、専用VRAM12GBを超えてしまう処理の際に威力を発揮します。

先日、基幹部分をDDR4からDDR5にアップグレードしている為に、その恩恵もありますが、Compileに時間をかけてもその後の推論時間を短縮できる場合、専用VRAM12GBを超えてしまうFlux1のような処理の場合、このmax-autotuneは効果的という事になります。

Ⅳ モード入力ウインドウの改行エラーを改善する

WaveSpeed版のtorch.compileノードには、初期状態ではエラーを起こす原因になるコードの記述不足があり、それを改善する事により、安定性が向上します。

"ComfyUI\custom_nodes\Comfy-WaveSpeed\misc_nodes.py"の119行目(バージョンにより上下する可能性有)の記述を以下のように変更してください。

mode = mode.strip() if mode else None

問題の背景

  • ユーザー入力(特に default で与えられる値)が改行や余計なスペースを含む場合、パース時にエラーになることがある。

  • これを防ぐには、入力値から余計な空白や改行を除去(トリム)する処理が必要。

対策の意味

  • ここで、mode 変数に対して .strip() を使い、前後の空白や改行を除去しています。

  • これにより、ユーザーが mode に改行やスペースを含めて入力しても、エラーになりにくくなっています。

動画生成における優位性の低下について

WaveSpeedの公式設定においては、動画生成用のワークフローではtorch.compileは使用されていません。
(但し、KJNodesからは動画生成モデルに対応したtorch.compileノードが公開されているので、それらは使用した方が良い)

この理由については、Cursor先生の分析によれば、以下の点が挙げられると思われます。

動画生成モデル(hunyuanvideoなど)とFlux1でのtorch.compileの効果の違いについて分析してみましょう:

  • モデルの特性の違い:

Flux1:

  • 静的な画像生成

  • 一貫した計算パターン

  • 予測可能な実行フロー

  • → torch.compileが最適化しやすい

動画生成モデル:

  • フレーム間の時系列処理

  • 動的な計算パターン

  • 条件分岐が多い可能性

  • メモリアクセスパターンが複雑

  • → torch.compileの最適化が難しい可能性

  • 最適化の効果が限定的な理由:

  • フレーム間の依存関係

  • 動的なバッチ処理

  • メモリの再利用パターン

  • I/O処理の比重が大きい

結果

Multi ControlNetを使用し、VRAM消費量が13GB位になる以下で公開しているFlux1のベースプログラムで比較テストを行いました。

torch.compile無しよりも確実に速いです。特に2回目以降の高速化は顕著に表れます。専用VRAMを超えて共有VRAMを使いに行く事が多く、どうしても速度が低下しやすいFlux1においては非常に効果的なノードです。

First Block cacheのみだと、以下のようになります。特に2回目以降の生成速度においては、倍近くを要しています。

Advanced tips

MultiGPU

また、以下記事で解説しているMultiGPUノードを併用すると、非常に効果的です。特にFlux1、各種動画生成において有効です。

最適化キャッシュファイルの動的管理

torch.compileとは一種のキャッシュ技術でもあり、使用回数と時間経過と共にサイズが肥大化します。特に、triton関係のサイズは長期間使用し続けると数十GBにも達します。

しかし、これを全削除してしまうと、せっかくtorch.compileがモデルごとに最適化した設定が消えます。

そこで、サイズ上限を設けて上限に到達したら、スケジューラーと共に自動的に古いキャッシュファイルから削除するロジックを開発しました。

まず、上で作成したモデルごとのキャッシュファイルフォルダ直下に、以下の内容でps1を作成してください。この場合は上限を50GBとしていますが適宜調節してください。ファイル名は、triton-lru-size-cleanup-50gb.ps1としていますが、下記コードと共に適宜変更してください。

Param(
    [string]$Target = "D:\USERFILES\wan22vace\triton",
    [int]$MaxSizeGB = 50,            # 上限を 50GB に設定
    [int]$KeepMinItems = 2,          # 最低直下アイテム数(フォルダ/ファイルの合計)
    [string[]]$WhitelistPatterns = @("WAN2.2","wan22","wan22vace"), # マッチした名前は削除対象外
    [string]$LogFile = "D:\USERFILES\wan22vace\triton-lru-cleanup-50gb.log",
    [switch]$DryRun                   # DryRun があれば削除は行わない(ログのみ)
)

function Write-Log($msg) {
    $line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $msg"
    $line | Out-File -FilePath $LogFile -Encoding utf8 -Append
    Write-Output $line
}

Write-Log "=== triton-lru-size-cleanup start (Target=$Target MaxSizeGB=$MaxSizeGB KeepMinItems=$KeepMinItems DryRun=$DryRun) ==="
Write-Log "Whitelist patterns: $($WhitelistPatterns -join ',')"

# 1) 安全確認 - ComfyUI が動いていれば中止
try {
    $comfy = Get-CimInstance Win32_Process -ErrorAction SilentlyContinue |
             Where-Object { ($_.Name -ieq 'python.exe' -or $_.Name -ieq 'python3.exe') -and ($_.CommandLine -match 'ComfyUI') }
} catch {
    $comfy = $null
}

if ($comfy) {
    Write-Log "ComfyUI process detected. Cleanup aborted to avoid race conditions."
    Write-Log "Processes: " + ($comfy | Select-Object ProcessId, CommandLine | Out-String)
    exit 0
}

if (-not (Test-Path $Target)) {
    Write-Log "Target path not found: $Target"
    exit 0
}

# ヘルパー: フォルダ/ファイルのサイズをバイトで取得
function Get-ItemSizeBytes($item) {
    if ($item.PSIsContainer) {
        try {
            $sum = Get-ChildItem -LiteralPath $item.FullName -Recurse -Force -ErrorAction SilentlyContinue |
                   Where-Object { -not $_.PSIsContainer } |
                   Measure-Object -Property Length -Sum
            return [int64]($sum.Sum)
        } catch {
            return 0
        }
    } else {
        return [int64]$item.Length
    }
}

# 現在の合計サイズ(再帰)
function Get-SizeBytes($path) {
    try {
        $sum = Get-ChildItem -LiteralPath $path -Recurse -Force -ErrorAction SilentlyContinue |
               Where-Object { -not $_.PSIsContainer } |
               Measure-Object -Property Length -Sum
        return [int64]$sum.Sum
    } catch {
        return 0
    }
}

$maxBytes = [int64]$MaxSizeGB * 1GB
$currentBytes = Get-SizeBytes -path $Target
Write-Log ("Current size: {0:N0} bytes (~{1:N2} GB)" -f $currentBytes, ($currentBytes / 1GB))

if ($currentBytes -le $maxBytes) {
    Write-Log "Size within limit. No action required."
    Write-Log "=== triton-lru-size-cleanup finished ==="
    exit 0
}

# 取得:triton直下の子要素(ファイルやサブフォルダ)
$children = Get-ChildItem -LiteralPath $Target -Force -ErrorAction SilentlyContinue

if (-not $children) {
    Write-Log "No child items found under $Target"
    exit 0
}

# 各要素に「アクセス時刻キー」とサイズを付与(LastAccessTime が使えない環境では LastWriteTime にフォールバック)
$items = @()
foreach ($c in $children) {
    # ホワイトリスト判定(名前にパターンが含まれていれば除外)
    $isWhitelisted = $false
    foreach ($p in $WhitelistPatterns) {
        if ($c.Name -match [regex]::Escape($p)) {
            $isWhitelisted = $true
            break
        }
    }
    if ($isWhitelisted) {
        Write-Log "Skipping whitelisted item: $($c.FullName)"
        continue
    }

    $accessTime = $null
    try {
        $accessTime = $c.LastAccessTime
        if ($accessTime -eq [datetime]::MinValue -or $accessTime -eq $null) {
            $accessTime = $c.LastWriteTime
        }
    } catch {
        $accessTime = $c.LastWriteTime
    }

    $sizeBytes = Get-ItemSizeBytes -item $c
    $items += [PSCustomObject]@{
        Item = $c
        KeyTime = $accessTime
        SizeBytes = $sizeBytes
    }
}

# 古い順(KeyTime 小さい順)にソート
$items = $items | Sort-Object KeyTime

# 削除ループ(KeepMinItems を守りつつ削る)
foreach ($entry in $items) {
    $remainingChildren = (Get-ChildItem -LiteralPath $Target -Force -ErrorAction SilentlyContinue | Where-Object {
        # 残りカウントでもホワイトリストを除外してカウントする
        $name = $_.Name
        $skip = $false
        foreach ($p in $WhitelistPatterns) { if ($name -match [regex]::Escape($p)) { $skip = $true; break } }
        -not $skip
    }).Count

    if ($remainingChildren -le $KeepMinItems) {
        Write-Log "Reached KeepMinItems ($KeepMinItems). Stop deleting further items."
        break
    }

    if ($currentBytes -le $maxBytes) { break }

    $itm = $entry.Item
    $itemBytes = $entry.SizeBytes
    $timeStr = $entry.KeyTime.ToString('yyyy-MM-dd HH:mm:ss')

    Write-Log ("Candidate: {0} LastAccess/Write: {1} Size: {2:N0} bytes (~{3:N2} GB)" -f $itm.FullName, $timeStr, $itemBytes, ($itemBytes / 1GB))

    if ($DryRun) {
        # DryRun: 実削除しないがサイズを見積もる
        $currentBytes -= $itemBytes
        continue
    }

    try {
        Remove-Item -LiteralPath $itm.FullName -Recurse -Force -ErrorAction Stop
        Write-Log "Removed: $($itm.FullName) freed $itemBytes bytes"
        $currentBytes -= $itemBytes
        Write-Log ("New size estimate: {0:N0} bytes (~{1:N2} GB)" -f $currentBytes, ($currentBytes / 1GB))
    } catch {
        Write-Log "ERROR removing $($itm.FullName): $($_.Exception.Message)"
    }
}

Write-Log ("Final estimated size: {0:N0} bytes (~{1:N2} GB)" -f $currentBytes, ($currentBytes / 1GB))
Write-Log "=== triton-lru-size-cleanup finished ==="

作成後、以下で動作テストを行ってください。

powershell.exe -ExecutionPolicy Bypass -File 設置フォルダ\triton-lru-size-cleanup-50gb.ps1" -DryRun
powershell.exe -ExecutionPolicy Bypass -File "D:\USERFILES\wan22vace\triton-lru-size-cleanup-50gb.ps1"

以下の様に、表示されればテストは成功です。

[2025-08-20 13:49:45] === triton-lru-size-cleanup start (Target=D:\USERFILES\wan22vace\triton MaxSizeGB=50 KeepMinItems=2 DryRun=False) ===
[2025-08-20 13:49:45] Whitelist patterns: WAN2.2,wan22,wan22vace
[2025-08-20 13:49:46] Current size: 0 bytes (~0.00 GB)
[2025-08-20 13:49:46] Size within limit. No action required.
[2025-08-20 13:49:46] === triton-lru-size-cleanup finished ===
PS D:\USERFILES\wan22vace>

スケジューラーに登録します。以下は毎週日曜日AM4:30実行の例です。

schtasks /Create /SC WEEKLY /D SUN /TN "ComfyUI_Triton_LRU_Cleanup_50GB" /TR "powershell.exe -ExecutionPolicy Bypass -File \"D:\USERFILES\wan22vace\triton-lru-size-cleanup-50gb.ps1\"" /ST 04:30 /RL HIGHEST /F

以下コマンドで、登録内容の確認も可能です。

PS D:\USERFILES\wan22vace> schtasks /Query /TN "ComfyUI_Triton_LRU_Cleanup_50GB" /V /FO LIST

フォルダー\
ホスト名:                                       USSOEWWIN
タスク名:                                       \ComfyUI_Triton_LRU_Cleanup_50GB
次回の実行時刻:                                 2025/08/24 13:53:00
状態:                                           準備完了
ログオン モード:                                対話型のみ
前回の実行時刻:                                 1999/11/30 0:00:00
前回の結果:                                     267011
作成者:                                         ussoewwin\ussoewwin
実行するタスク:                                 powershell.exe -ExecutionPolicy Bypass -File " D:\USERFILES\wan22vace\triton-lru-size-cleanup-50gb.ps1\ /ST 12:30 /RL HIGHEST /F
開始:                                           N/A
コメント:                                       N/A
スケジュールされたタスクの状態:                 有効
アイドル時間:                                   無効
電源管理:                                       バッテリ モードで停止, バッテリで開始しない
ユーザーとして実行:                             ussoewwin
再度スケジュールされない場合はタスクを削除する: 無効
タスクを停止するまでの時間:                     72:00:00
スケジュール:                                   スケジュール データをこの形式で使用することはできません。
スケジュールの種類:                             毎週
開始時刻:                                       13:53:00
開始日:                                         2025/08/20
終了日:                                         N/A
日:                                             SUN
月:                                             1 週ごと
繰り返し: 間隔:                                 無効
繰り返し: 終了時刻:                             無効
繰り返し: 期間:                                 無効
繰り返し: 実行中の場合は停止:                   無効
PS D:\USERFILES\wan22vace>

タスクスケジューラーから管理、削除が可能です。

もっと直接的に、関連ファイルをすべて削除する事も可能です。以下の内容でバッチファイルを作成して、モデルごとに作成したキャッシュフォルダな置いて実行すると、関連全ファイルを削除できます。

以下は、wan22vaceの場合です。

@echo off
del /q /s "D:\USERFILES\wan22vace\Inductor\*"
del /q /s "D:\USERFILES\wan22vace\Temp\*"
del /q /s "D:\USERFILES\wan22vace\triton\*"

いいなと思ったら応援しよう!