裝置後端
CodefyUI 執行於 PyTorch 之上,因此繼承了 PyTorch 的裝置後端:CPU、NVIDIA CUDA、**Apple Silicon(MPS)**與 AMD ROCm(Linux)。關於安裝正確的 wheel,請參閱 GPU 與裝置設定;本頁說明裝置選擇在執行時的行為。
裝置選擇
預設是 CPU,而且不會有任何機制替你切走。 一次執行依下列順序決定裝置,先命中者為準:
- 節點自己的 device 參數,且不是
auto時(位於「進階」,為舊圖保留;一張圖只在一個裝置上執行,需要兩個裝置的工作應拆成兩張圖)。 - 圖自己的裝置,也就是圖檔裡的
settings.device,由 Run 旁邊的「此圖的裝置」控制項設定。它會跟著圖一起存檔,所以 git 會追蹤它,圖在哪裡打開都跑在同一個裝置上。 - 設定裡的裝置(這個瀏覽器記住的值,改動前是
cpu)。新圖與尚未指定裝置的圖會使用它;設定面板也會提示這台伺服器能看到的最佳裝置。 cpu:請求完全沒有指定裝置時伺服器採用的值。
設定、圖的控制項與節點參數三個下拉選單列的都是同一份清單,也就是 PyTorch 實際看得到的裝置(device_utils.describe_accelerator()),多卡機器上也包含每張卡的 cuda:N。被請求的裝置會與可用的裝置比對,若不存在則退回 CPU 並發出警告。只有明確指定 auto(cdui run 或匯出腳本的 --device auto、run API 的 "device": "auto")才會解析成目前最好的加速器。更改圖的裝置會讓互動快取失效,所有節點都會重新執行。
裝置對齊由引擎保證
你不需要去推敲某個張量到底在哪個裝置上。節點執行前,graph_engine.invoke_node 會把它輸入裡的每個張量搬到該節點要跑的裝置——節點自己宣告了 device 參數且不是 auto 時就用它的,否則用這次執行的裝置(依上述順序解析)。由於所有進入節點的路徑都經過那一個函式,這個保證同時涵蓋內建節點、外掛節點與你自己的自訂節點。
這項機制很重要,因為只有 CPU 的開發環境不會發生裝置不一致,問題通常到其他人的 GPU 機器上才會出現。在輸入對齊移入引擎前,已有兩個隨附 graph 因此失敗,錯誤為 Input type (torch.FloatTensor) and weight type (torch.cuda.FloatTensor) should be the same。
對齊刻意不碰的東西:
- 模組。
nn.Module.to()會就地修改模組,因此搬移從其他節點收到的模型,可能會在擁有該模型的節點不知情時改變權重所在裝置。需要讓模型使用自身裝置的節點,必須明確呼叫to_device。 - Dataset、DataLoader、環境等非張量值。 它們原樣通過,所以 dataset 維持惰性,
TrainingLoop仍然是一次一個 batch 串流到 GPU,而不是整份常駐 VRAM。 - 宣告了
align_inputs = False的節點。 本質上就在 host 端做事的節點——直接把輸入交給 numpy、sklearn、matplotlib 或 PIL——會選擇退出,因為Tensor.numpy()在非 CPU 上會直接丟例外。內建的例子是TrainTestSplit。你自己的節點若是同一類,就寫上align_inputs = False;參見自訂節點。 - 節點在自己的
execute中建立的張量。 對齊只處理節點輸入,不會處理節點內部建立的torch.zeros(...)。若新張量要與輸入張量一起運算,請用device=<the input>.device建立。
:::note 匯出的腳本也遵守同一條規則
不帶 --device 執行 python graph.py 時,會用圖存檔時的裝置(settings.device),沒有的話就是 CPU,也就是設定為 cpu 時 canvas 對這張圖給的答案。想在執行的那台機器上用最好的加速器請傳 --device auto,想釘在某一張卡請傳 --device cuda:1。
:::
在多張卡之中指定其中一張
在有一張以上 CUDA 裝置的機器上,每個下拉選單也會逐張列出——cuda:0、cuda:1 等等——並與單純的 cuda 並列,後者代表「torch 目前指向的那一張」。只有一張 GPU 的機器只會顯示 cuda,因為在那裡兩者指的是同一塊硬體。
指定了這台機器上不存在的編號時,會退回目前的 CUDA 裝置而不是 CPU:一張在工作站上釘在 cuda:2 的圖,在筆電上打開時仍然應該用 GPU 訓練。每張卡也各自有自己的執行佇列。完整說明(包含刻意排除在外的分散式訓練)請參閱 訓練記憶體。
float64 + MPS 的限制
MPS 是 float32 原生的,會拒絕 float64 張量。CodefyUI 在 device_utils.to_device 中將其正規化,但如果你撰寫一個直接建立張量的自訂節點,請在 Apple GPU 上將它們維持為 float32,以避免執行時錯誤。
CodefyUI 也會在 import torch 前設定 PYTORCH_ENABLE_MPS_FALLBACK=1。MPS 不支援某項運算時,PyTorch 會改在 CPU 上執行。速度會較慢,但不會因不支援該運算而發生錯誤。若要讓不支援的運算拋出錯誤,請在執行 cdui start 前將此變數匯出為 0。
Apple Silicon 上的效能
以下數字在 M3 MacBook Air(24 GB、torch 2.11)上量測,三個內建範例各跑一個 epoch,執行之間留冷卻間隔。套用前兩項後:mps 上 CNN-MNIST 11.2 s → 4.1 s、ResNet-CIFAR10 12.1 s → 5.1 s、GPT-Mini 19.9 s → 13.1 s;cpu 上 13.7 s → 11.5 s、27.4 s → 24.4 s、22.4 s → 20.8 s。
- 訓練迴圈不會每個 batch 讀回 loss。
loss.item()是一次 host/device 同步。在 MPS 上它會排空 Metal 指令佇列,CPU 因此無法在 GPU 處理目前 batch 時準備下一個。TrainingLoop、EvaluateModel與DiffusionTrainingLoop在裝置上累計 loss,只在每個 epoch 結束、每個batch_metrics取樣點、以及每個進度訊框(每秒最多兩次)時讀回。單獨這一項:mps上每個 epoch CNN-MNIST 11.2 s → 5.5 s、ResNet-CIFAR10 12.1 s → 5.6 s、GPT-Mini 19.9 s → 15.8 s。 - 預設的
ToTensor+Normalize以 batch 為單位執行。 MNIST、FashionMNIST、CIFAR10、CIFAR100 在 transform 埠沒有接線時,Dataset對整個 batch 一次套用這兩步。輸出與逐樣本路徑逐位元相同;MNIST 每個 epoch 的主機時間從 1.5 s 降到 0.13 s。有接線的 transform 鏈使用 torchvision 的逐樣本路徑。 - 記憶體內資料集的
num_workers維持 0。 macOS 以spawn啟動 DataLoader worker。對 MNIST 規模的資料,啟動與逐 batch 的 IPC 成本高於節省的時間:CNN-MNIST 在 0 個 worker 時 11 s、2 個 25 s、4 個 16 s。worker 對需要解碼影像檔的資料集(ImageFolderDataset)有幫助。
自行量測時:一個 process 的第一次 MPS 執行需要 0.2–0.6 s 初始化 Metal,每種新的 kernel 形狀再加 0.1–0.2 s。同一台 Mac 之後的 process 會較快,因為 macOS 會快取編譯好的 shader。無風扇的 Mac 在持續 GPU 負載幾分鐘後會熱降頻;比較不同執行時請在中間留冷卻間隔。
MPS 上不使用混合精度。bf16 與 fp16 autocast 在 torch 2.11 可以執行,但量測結果比 fp32 慢 1.8–3.3 倍且不省記憶體。詳見訓練記憶體。
ROCm 呈現為 CUDA
在 AMD + Linux 上搭配 ROCm 版本的 PyTorch 時,torch.cuda.is_available() 會回傳 True,因為 ROCm 暴露了一個與 CUDA 相容的介面。該裝置在下拉選單中會顯示為 cuda;這是預期的行為。
實驗性:原生 MLX(spike)
有一個概念驗證 (proof-of-concept),把一個小型 MLP 的前向推論從 PyTorch 移植到 Apple 的 MLX 框架,產生數值上完全相同的結果(最大絕對差約 1.9e-7)。重點如下:
-
真正圖引擎中的 Apple 加速是 PyTorch MPS,它已接好並完成端到端驗證。MLX 並非已交付的執行後端。
-
MLX 是一個獨立的陣列框架,並非 PyTorch 後端——並沒有
torch.device("mlx")——所以它無法成為任何裝置下拉選單(它們驅動的是torch)中的一個值。 -
此 spike 僅供推論且為 float32,可臨時執行:
uv pip install mlx # Apple Silicon onlypython scripts/mlx_spike.py -
mlx並非已納入的相依套件;主應用程式從不匯入它。只透過device_utils.mlx_available()(偵測)與 spike 腳本來呈現它。
**建議:**將 MPS 維持為所有執行(訓練 + 推論)的 Apple 預設;把 MLX 當作選用的推論加速器,只在推論密集的教學示範上有可量測的效益時才回頭考慮。