いつの間にかROCm 7.14がリリースされたので、Strix HaloでComfyUIを動かしてみた。

今年も半分が終わり、PCハードウェアの問題がひと段落着くかと思ったら、さらに悪くなっているらしい。とにかくもの値段が高い、必要なものが手に入らない。仮想環境向けの汎用サーバやパソコンはほんと大変らしい。AI向けのほうが潤沢なのかというと、パソコンレベルではそんなことはなく、一体どこにものが言ったんだよぉという状態。しかし、どういうわけか、Strix Haloはメジャーなベンダーのものでなければ比較的手に入りやすい。ただし、手に入るから格安というわけではないが。
Strix Haloが激推しかというと、やはりDGX Sparkと比較すると速くはない。やはり値段と比例するのかという感じ。だったんだが、ROCm 7.14で大躍進したような気がする。
 
 
閑話休題
 
 
最近、ROCm 7.14がリリースされたので、EVO-X2ことAMD Strix Halo環境へ導入さらに、コンテナイメージも更新してComfyUIを動かしてみた。
Nvidia系は電気を食うのと熱いので、夏はお休み傾向。一方AMD Strix HaloはいわばNUCなのでそれはない。ただスピードなどをある程度犠牲にする必要がある。
 
実際、ROCM7.14での生成性能は、Z-Image Turboワークフローのデフォルトサンプルで、
 
7.2.4:  25秒
7.14:   20秒
 
と5秒も速くなった。DGXSparkのnvfp4の5秒には敵わないが、これだと完全に実用域だと思う。
 
しかし、初回のモデルロードだけ約526秒(約9分)かかる
 
COMFY_ARGS: “”
 
一度ロードが終われば、その後の生成は非常に高速なので気にしなければいいレベル。「まぁROCmはまだこういうものなのかな」と思っていたが気になるので調査してみた。

環境

  • AMD Ryzen AI Max+(Strix Halo)
  • Ubuntu 26.04 LTS
  • ホスト:ROCm 7.14
  • コンテナイメージ:rocm7.14_ubuntu26.04_py3.14_pytorch_release_2.12.0
  • ComfyUI:0.28.2
  • Docker Compose

症状

いくつか不具合があって、最初は、1回目の実行は、OOMが発生して、2回目から成功するという問題があった。コンテナ内のファイルディスクリプタを使い切っていたらしい。
以下を設定して、クリアした。
ulimits:
nofile:
soft: 65536
hard: 65536
 
初回実行も通るようになったのだが、なぜか初回実行時だけ極端に時間がかかる。
Prompt executed in 533.xx seconds
しかし2回目以降は正常。
つまり
  • モデルファイルの破損ではない
  • 毎回遅いわけではない
ということになる。

最初に疑ったもの

最初は次のようなものを疑う。
  • safetensorsの読み込み
  • SSD性能
  • Docker
  • Triton JIT
  • mmap
  • NFS
  • Python
しかし調べても、それらで533秒も説明できない。

py-spyで調査

Pythonプロセスを py-spy --native で確認してみるとメインスレッドは待機状態。
実際にCPU時間を使っていたのは、
load_clip
load_text_encoder_state_dicts
load_sd
torch.nn.Module.load_state_dict()
param.copy_()
hsakmt_ioctl()
という経路。
さらに py-spy top --native を見ると、
ioctl
BusyWaitSignal
がほとんどの時間を占めています。
つまり、
Pythonが遅いのではなく、ROCmランタイムの待ち時間
であることが判明。

ComfyUIのコードを追う

次にComfyUI側を確認すると、Text Encoder生成部分は
params[“device”] = text_encoder_initial_device(…)
 
self.cond_stage_model = clip(**params)
 
self.load_sd(…)
という流れになっていて、気になったのが
text_encoder_initial_device()
 

text_encoder_initial_device()

実装は概ね次のようになっているっぽい。
if load_device == offload_device:
return offload_device
 
if model_size <= 1GB:
return offload_device
 
if GPUメモリが十分ある:
return load_device
 
return offload_device
つまり、
GPUメモリが十分あると判断すると、最初からGPUへText Encoderを生成する
ようになっている。

仮説

通常のディスクリートGPUなら問題ないが、Strix HaloはCPUとGPUが物理メモリを共有するUMA。
ComfyUIは
「GPUへ直接ロードした方が速い」
と判断していたが、ROCm側では
param.copy_
HSA/KFD
ioctl
という処理が大量に発生しているように見える。
この仮説が正しいなら、最初だけCPUへロードすれば速くなる
はず。

試してみた

ComfyUI起動オプションへ
–lowvram
を追加。
するとログが変わる。
以前は
CLIP/text encoder model load device: cuda:0
だったものが、
CLIP/text encoder model load device: cpu
offload device: cpu
current: cpu
 

結果

初回ロード時間は
約526秒
約35秒
まで短縮された。その後の生成時間も
Prompt executed in 20.3 seconds
Prompt executed in 20.4 seconds
と非常に高速になった。興味深いことに、
生成モデル本体(Lumina2)はGPUへロードされている。
Requested to load Lumina2
loaded completely
つまりText EncoderだけCPUへ初期ロードされている状態。
 
COMFY_ARGS: “–lowvram”
 

結論

今回の環境では、
ROCmそのものが遅かったわけではなく、原因はText Encoderを最初からGPUへロードする経路だった。
--lowvramによりText EncoderだけCPUロードになった結果、
初回ロード時間が約9分から約35秒まで短縮。
 

ComfyUIの問題なのか?

少し興味深いのは、この症状が以前のComfyUIではあまり目立たなかったことである。
最近のComfyUIでは、
  • Dynamic VRAM
  • Async Weight Offloading
  • メモリ管理の改善
などが積極的に進められている。
その結果、通常VRAMモードではText EncoderをGPUへ積極的に初期配置する経路が選択されるようになったように見える。
実際、ソースコードを追うと、text_encoder_initial_device()は利用可能なGPUメモリ量を見てGPUへロードするかCPUへロードするかを判断している。
今回の調査では、通常モードではText Encoderのロード中にROCmランタイム内部で大量の待ち時間が発生していた。一方、--lowvramではText EncoderがCPUへ初期ロードされ、この待ち時間はほぼ消えた。
どのコミットでこの挙動になったのかまでは追跡できていないが、ComfyUIのメモリ管理の変更が今回の現象を顕在化させた可能性は十分あると考えている。
 
 
DGX Sparkも同様なのか?と思ったら、DGX Sparkも再起動直後の初回実行ではものすごく時間がかかるようになっていた。しかし、この方法では改善せず。なぞ。
 
 
 
それにしてもAMDは、ROCm7.14でかなり改善されている。だいぶDGX Soarkモドキになってきたような。
 
Strix Haloは、もはや、
  • エコシステムがある程度揃っている
  • GPU付き
  • UMA128GB RAM
が100万円以下で買えるほぼ唯一の選択肢かもしれない。
 
Nvidiaで揃えると100万円以上、MACはもう大容量のUMA機は手に入らない。
 
Strix Haloのエグい値上げや品不足が起きなきゃいいと思うが。

コメントする