跳到主要內容

【2026 最新】rGPU vs SSH 怎麼選?Mac 本機 Python 借遠端 GPU 的五步教學與延遲評估

最後更新: ·
rGPU vs SSH:Mac 本機 Python 與遠端 Tensor 的運算分工

你的 Mac 裡有 Python、筆記本與正在修改的資料處理程式,GPU 卻在另一台主機。每次想試一小段訓練,都要決定把檔案搬過去,還是讓本機程式隔著網路叫 GPU 做事。rGPU vs SSH 的選擇,先從這個問題開始:你希望程式住在哪裡?

這篇專為第一次接觸遠端 GPU、願意照著貼指令的讀者寫。先用白話分清兩種架構,再完成雙端連線、小算式驗證與同工作量比較。實作前提是已有 Python 3.10 以上的 Mac,以及你獲准使用、可 SSH 登入的 NVIDIA GPU 主機;不需要先租新機器。

以下核對至 2026 年 10 月 12 日的官方文件與固定程式版本。本次環境沒有已授權的遠端 NVIDIA 主機,因此提供的是操作與評估方法;不填入 AlphaLab 的 GPU 跑次、延遲或加速倍率。

先說結論:rGPU vs SSH,先決定 Python 留在哪裡

rGPU=本機 Python 指揮遠端 Tensor;SSH 整段執行=把 Python 和 Tensor 一起放到遠端。Tensor(張量)就是模型處理的多維數字陣列,像放在廚房的食材。前者是人在家中下單、廚房在外地;後者是把整份食譜交給外地廚房做完。

  • 需要本機資料處理、互動筆記本或本機工具一起工作,先評估 rGPU。
  • 訓練能完整放進遠端程式,先建立 SSH 執行的基準。
  • rGPU 也使用 SSH 通道;兩者比較的是工作分工,不是加密方式。

原理:同一張 GPU,網路介入的位置不同

用 VS Code Remote – SSH 開遠端資料夾時,視窗在 Mac,命令與相關擴充執行在遠端。直接用 SSH 啟動訓練也類似:內層 Python 迴圈與 GPU 呼叫都在主機裡,網路主要承擔登入、檔案搬移和你要求的輸出。編輯器顯示在本機,不等於 Python 也在本機。

依 rGPU 架構文件,PyTorch 路線讓本機張量包裝保存形狀等資訊與遠端張量的參照;真正的資料和運算在服務端。操作可以排隊,等你讀結果才要求答案。換句話說,Mac 拿的是食材的編號,食材本身仍在外地廚房。

rGPU vs SSH 雙端流程:本機 Python 與遠端完整 Python 的分工
上:rGPU 把本機操作送往遠端;下:SSH 啟動遠端完整程式。兩條路線都可以使用 SSH。

這也回答了 Show HN 裡「為什麼不直接 SSH?」的疑問:如果整段工作搬過去很容易,SSH 是清楚的起點。rGPU 的價值,是讓你保留本機 Python 工作流;這個便利要用傳輸、等待與相容性成本來交換。你原本就熟悉 遠端連線管理 的話,這裡新增的是運算分工,而非另一套遠端桌面。

rGPU vs SSH 上手:五步接通並核對兩端

①「先選通道」:Mac 從 PyTorch device 路線開始

看到兩種安裝方法,不知道該選哪個?先選可以明確改成 device="rgpu" 的 PyTorch 路線。另一條 CUDA shim 是替 Linux 程式攔截 CUDA 函式的方案;Mac 走那條路需要 Linux 容器,兩條路的服務端與通訊協定也不同。本文只操作前者。

本篇固定官方 commit a1d63cacd9374bb183dff407452b91f3fc681581;套件設定 要求 Python ≥3.10、Torch ≥2.14。雙端 Python 都須符合套件條件;先確認你已有合適 Python,再建立專用虛擬環境,避免改動原本工作專案。以下用已安裝的 Python 3.11;命令失敗時先核對 Python 與 wheel 相容性。

mkdir rgpu-lab
cd rgpu-lab
python3.11 -m venv .venv
source .venv/bin/activate
git clone https://github.com/ymcrcat/rgpu.git rgpu
git -C rgpu checkout a1d63cacd9374bb183dff407452b91f3fc681581
python -m pip install -e ./rgpu/python
python -c 'import torch, rgpu; print(torch.__version__)'
python -m pip freeze > client-requirements.txt

②「先留門禁」:部署服務,但保留 localhost 綁定

怕把運算入口暴露出去?先確認可信任的 SSH 主機,再用官方部署腳本。下方 user@gpu-host、2222 與金鑰路徑都要換成你的值;第一次登入核對管理者提供的主機指紋,保留正常的 host-key 檢查。

./rgpu/scripts/deploy_opserver.sh user@gpu-host -p 2222 -i ~/.ssh/gpu_key

腳本會複製服務程式、建立遠端環境、依本機 Torch 主/次版本安裝 CUDA wheel,並檢查服務啟動。此腳本會重新啟動既有 opserver;部署前先停用連線中的客戶端。第一次部署有大型套件下載,看到 LISTENING 才往下走。它會操作遠端的 ~/rgpu、~/opvenv 與日誌,請在自己的測試主機執行,先確認這些位置沒有其他工作。

依 部署文件,PyTorch opserver 預設綁 127.0.0.1:9720,協定本身沒有用戶驗證。保留回環綁定,用 SSH 搬運流量;不要另外開公開入站 9720。SSH 加密通道不會替你關掉另一個公開監聽埠。

③「點名兩端」:版本配對與 GPU 是兩張檢查單

安裝結束,不代表裝對 GPU 版。本機與遠端各印一次版本;遠端再驗 CUDA。下面讀取部署產生的遠端 Python:

ssh -p 2222 -i ~/.ssh/gpu_key user@gpu-host   '~/opvenv/bin/python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"'

客戶端與服務端 Torch 的主/次版本要一致,例如同為 2.14;CUDA wheel 的 +cu... 後綴則是另一件事。GPU 主機的 CUDA 可用檢查應為 True。若不符合,先讀 故障排查 與遠端 ~/opserver.log;不要把版本不匹配覆寫旗標變成固定設定。

④「算一張收據」:8.0 驗連線,GPU 位置另外驗

怕 Python 其實還在 Mac 自己算?先執行官方小算式,再交叉核對主機。把以下存成 smoke.py:

import torch
import rgpu
x = torch.ones(4, device="rgpu")
print((x * 2).sum().item())
rgpu-run --host user@gpu-host --ssh-port 2222 -i ~/.ssh/gpu_key python smoke.py

官方 Quickstart 的預期輸出是 8.0。啟動器建立私有 SSH tunnel、替子程式設定連線並在結束後關閉它;程式本身仍須匯入 rgpu、選擇 rgpu device。8.0 證明這段算式有回傳,不能單獨證明 NVIDIA GPU 真的參與。

部署日誌、遠端 torch.cuda.is_available() 與實際服務設定一起看。較長工作執行時,在 GPU 主機另開 nvidia-smi 觀察程序和顯存;小算式可能短得看不見使用率變化。不要用「沒有看到尖峰」判定它一定沒上 GPU,也不要把 x.device 印出的 rgpu 標籤當硬體證據。

⑤「把模型先搬好」:前向之前選 device,再建 optimizer

想把原本訓練改接遠端?先移模型,再建立最佳化器,輸入也放同一端。官方訓練指引 要求在第一次追蹤梯度的 forward 前完成移動;已有 autograd 圖保留參照時,搬動參數可能失敗。Autograd 像記住每一道製作步驟的帳本,計算開始後換食材位置會讓帳本對不上。

先用下節的小模型跑通,再增加模型大小。若還在認識訓練迴圈,可讀 MiniMind 訓練入門。官方目前列出非 ATen 自訂運算子不支援;同一個匯入 rgpu 的程序也有本機 CUDA/MPS backward 限制。因此本文的原生 CUDA 基準用另一個程序,並且不匯入 rgpu。

怎麼公平比較?同一段工作,留下四種等待

先分清 RTT(一次往返延遲)與頻寬(每秒能搬多少資料)。打電話最快多久得到回答,與卡車一次能載多少食材,是兩個瓶頸。讀取 loss.item() 要把標量回傳 Python,是同步點;但一則操作訊息未必對應一次往返,因為 rGPU 可以批次送出工作。

評估時分開保存啟動/匯入、初始化/傳輸、暖身、已完成的訓練區間。下面是依官方 API 組合的最小評估程式,存成 bench.py。它固定 CPU 端初始權重與假資料,兩條路各暖身五步、量五十步,終點額外做一次同樣的 forward 與標量讀取,確保讀到了完成結果。

import argparse
import json
import platform
import time
import torch
from torch import nn

p = argparse.ArgumentParser()
p.add_argument("device", choices=["cuda", "rgpu"])
p.add_argument("--read-every", type=int, default=10)
a = p.parse_args()
if a.read_every < 0:
    p.error("read-every must be nonnegative")
if a.device == "rgpu":
    import rgpu
if a.device == "cuda" and not torch.cuda.is_available():
    raise RuntimeError("CUDA is unavailable")

t0 = time.perf_counter()
torch.manual_seed(7)
model = nn.Sequential(nn.Linear(16, 32), nn.ReLU(), nn.Linear(32, 1))
x = torch.randn(64, 16)
target = torch.randn(64, 1)
model = model.to(a.device)
x, target = x.to(a.device), target.to(a.device)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

def step():
    optimizer.zero_grad()
    loss = (model(x) - target).square().mean()
    loss.backward()
    optimizer.step()
    return loss

def fence():
    # An extra forward + host read, included equally in both paths.
    return (model(x) - target).square().mean().item()

fence()
init_s = time.perf_counter() - t0
for _ in range(5):
    step()
fence()
before = dict(rgpu.stats()) if a.device == "rgpu" else None
t0 = time.perf_counter()
for i in range(50):
    loss = step()
    if a.read_every and (i + 1) % a.read_every == 0:
        loss.item()
final_loss = fence()
loop_s = time.perf_counter() - t0
after = dict(rgpu.stats()) if a.device == "rgpu" else None
delta = ({k: after[k] - before[k] for k in
          ["messages", "waits", "bytes_out", "bytes_in"]}
         if after is not None else None)
print(json.dumps({"device": a.device, "torch": torch.__version__,
                  "python": platform.python_version(),
                  "read_every": a.read_every, "warmup_steps": 5,
                  "steps": 50, "init_s": init_s, "loop_s": loop_s,
                  "mean_step_s": loop_s / 50,
                  "final_loss": final_loss, "rgpu_delta": delta}))

這份教學程式尚未加入 checkpoint 與故障後續跑,也不在故障時自動重試整段訓練。失敗跑次留下錯誤,另起新程序重新開始。init_s 從匯入完成後才起算;mean_step_s 是五十步加終點驗收除以五十,並非純 GPU kernel 時間。最後的 loss 是一致性檢查線索,不要求不同後端逐位元相同。

先把程式複製到遠端,再分別執行;下面採用同一個 GPU 主機、同一份原始碼與相同的讀取頻率。請另記 commit、GPU 型號、驅動、Torch 完整版本及服務設定,特別是數值精度與 TF32。

scp -P 2222 -i ~/.ssh/gpu_key bench.py user@gpu-host:~/bench.py
ssh -p 2222 -i ~/.ssh/gpu_key user@gpu-host   '~/opvenv/bin/python ~/bench.py cuda --read-every 10'
rgpu-run --host user@gpu-host --ssh-port 2222 -i ~/.ssh/gpu_key   python bench.py rgpu --read-every 10

每條路至少跑五次,交替順序,保存 JSON 與失敗紀錄;報中位數與範圍,不只挑最快一次。若要量「敲命令到結束」,在 Mac 使用 /usr/bin/time -p 包住完整 SSH 或 rgpu-run 命令,另存為端到端時間,別混入程式內的暖態平均。

rGPU vs SSH 決策比較:Python 位置、同步成本與適用工作
這張表比較架構與適用條件,不是效能排名。先有整段遠端基準,才知道保留本機 Python 的代價。

延遲評估:一次只改讀取頻率,接著才改網路

先將兩條命令的 --read-every 同時改成 1、10、0,分別表示每步讀、每十步讀、迴圈內不讀;零仍有終點讀取。保留相同權重、資料、形狀、步數與精度。觀察 rGPU 的 waits 差值是否改變,並將時間和 bytes_in/bytes_out 一起對帳。

若要比較 RTT,先量當時網路,並保留測量工具及樣本;若有兩個已授權的網路路徑,分開標明延遲與頻寬。人工加延遲只在可還原、與正式連線隔離的測試網路進行,記錄是單向延遲還是往返增量;不要在正式 SSH 管理通道直接改網路規則。這裡沒有提供任何人工加延遲後的觀測數字。

評估的判準是:當操作碎、同步頻繁時,網路等待可能主導;工作能成批留在遠端時,成本結構可能改善。增加的串行等待數 × RTT 可以當粗略估算,但排隊、批次、資料量及服務端執行會改變實際值,不能把 messages 直接乘 RTT 當完整秒數。

依 官方效能文件,歷史紀錄比較過本機 MPS opserver 與遠端 A40,另有訊息減少但短跑時間增加的編譯例子。它們不是原生遠端 Python 對 rGPU 的同工作量比賽,其中 A40 跑次還使用不同 Torch 主/次版本及覆寫設定。因此本文不用這些紀錄替兩條路線算加速倍率。

遠端 GPU 評估順序:固定工作、量同步、核對結果與復原
先保持材料相同,再改一個變因;速度紀錄與結果、失敗情境放在一起。

怎麼選?先回答三個問題,再試編譯

  • 本機 Python 有必要嗎?需要本機工具、資料前處理或筆記本互動,就試 rGPU;只是編輯遠端專案,Remote – SSH 已能提供那種工作方式。
  • 訓練能整段移過去嗎?能的話先跑原生 CUDA 基準,少一層跨網路操作分發。若搬移資料和環境本身耗時,把那段準備也記進選擇。
  • 互動需要多頻繁讀結果?高頻讀取就先用真實 RTT 做比較;能降低同步、固定形狀並重複同段工作,再評估 rGPU 編譯。

選用編譯時,明確寫 torch.compile(model, backend="rgpu", dynamic=False);這會把圖送到服務端供重複執行。首次編譯單獨記時間,再比較後續完成時間,避免用「訊息少了」代替「整段更快」。加入編譯是新的一組實驗,不與未編譯跑次混算。

如果你擔心的是買 GPU 是否值得,先讀 本機模型硬體與成本選擇。本題比較的是已有遠端 GPU 後如何安排工作;rGPU 本身不替你提供算力,也不替你終止雲端主機。

斷線怎麼復原?通道重接,與張量重建分開驗

依部署文件,服務端預設保留中斷 session 一百二十秒,客戶端可嘗試接回保留狀態;這不是顯存的永久保存。服務重啟或 session 過期會丟失遠端狀態。張量 handle 像置物櫃的號碼:櫃子清空後,舊號碼不能讓物品回來。

在自己的測試主機安排兩種演練:短暫中斷網路但保留服務;以及明確重啟服務。各留下中斷時間、錯誤、是否接回同一狀態與結果。本文沒有執行這兩種演練,因此不承諾某次斷線必定恢復。遇到 session lost,重新啟動工作程序、重建模型和 optimizer、從自己的 checkpoint 載入,別在原 Notebook 裡沿用舊張量。

Checkpoint 應把權重、optimizer state、步數與必要的隨機狀態保存到可持續存在的位置,並實際驗一次載入;它與 SSH 是否重接成功是不同關卡。需要進一步做訓練存檔,可依 PyTorch 保存與載入教學 增加這一層。結束 rgpu-run 只關它管理的通道與子程序,雲端資源要另外停止。

FAQ:rGPU vs SSH 的八個直接答案

1. rGPU 是讓 Mac 裝上 CUDA 嗎?

不是這條路線的意思。Mac 的 Python 使用 rgpu device;NVIDIA 運算由遠端主機處理。

2. 已經能 SSH,還需要 rGPU 嗎?

看需求。若要保留本機 Python,值得評估;整段程式搬過去很自然時,先跑 SSH 基準。

3. 印出 8.0 就證明 GPU 加速嗎?

不夠。CPU opserver 也可跑這個算式,另外核對服務設定、CUDA 可用性與主機程序。

4. 所有 CUDA 程式都能直接改 device 嗎?

不能作這種推論。本文操作 PyTorch device;Linux CUDA shim 有另一組條件,自訂運算子的支持也要逐項核對。

5. 每步印 loss 有代價嗎?

有。item 取得主機可讀標量時會等待;先比較每步、每十步與只讀終點,讓你的網路決定代價。

6. torch.compile 一定比較快嗎?

不一定。先付編譯成本,再看重複執行能否攤回;明確指定 rgpu backend,並保存完整計時。

7. Notebook 重啟後舊張量還能用嗎?

重新建立。遠端張量屬於 kernel 的服務 session,重啟 kernel 後不要沿用先前的 handle。

8. 關掉筆電就停止 GPU 帳單嗎?

不會由 rgpu-run 替你做到。它管理通道和子程序;租用主機、儲存與關機要到供應商端另外處理。

給新手的三個重點

  • 先選程式住哪裡,再選操作工具;SSH 同時可以是登入方式和 rGPU 的安全通道。
  • 先驗版本、服務與運算位置,再看秒數;8.0 是連通收據,暖態時間是另一張收據。
  • 先留下相同工作量的原生遠端基準,再改同步頻率與編譯;失敗和復原也一起記。

接著閱讀

左右滑動查看更多推薦

下一步:先交出兩份同工作量的收據

記住:rGPU 是本機指揮遠端 Tensor;SSH 整段執行是連 Python 一起搬過去。今天先確認兩端版本,跑小算式,再各跑一次 bench.py,把時間、最終 loss 與服務設定存好。接著只改一次讀取頻率,寫下你要保留哪條路線,以及原因落在哪個等待區間。

想把這種小實驗接成可靠的 AI 工作流,可以從 AlphaLab AI 專區 選下一個系統主題,或到 AlphaLab 課程 練習整合模型、工具與驗收。先有能解釋的比較,再擴大工作量。

ALPHALAB 社群

有問題?來 Telegram 聊

和 Terry、編輯、其他網友一起討論這篇文章。提問、分享觀點,回覆更即時。

加入 Telegram 討論

📩 訂閱 AlphaLab 電子報

每週最多三封:一封 Weekly 週報與最多兩封關鍵 Alpha Signal。

我們不會 spam,隨時可退訂。已訂閱?管理主題偏好(會寄登入連結到你的信箱)