每次看到別人分享「我用 4090 跑了什麼什麼」,我都會默默把視窗關掉,然後看一眼自己機箱裡那張 2019 年的卡。它叫 RTX 2070 SUPER,Turing 架構,compute capability 7.5,實際能用的顯存約 8.6 GB,配 PyTorch 2.8.0 和 CUDA 12.8。在 2026 年跑語音和視覺模型,它就像穿著三年前的球鞋去參加路跑,能跑,但你會很清楚自己的極限在哪。
這篇不是抱怨,是把這張卡的邊界寫清楚,因為每一條都影響過我的決定。手上是同一代或更老的卡的人,這些大概都適用。手上是新卡的人,可以當成一種考古。
flash-attn 跑不了,SDPA 是能拿到的那一個
flash-attn 2 需要 Ampere 以上的架構,Turing 不在支援清單裡,裝了也載不起來。這件事我是在裝了之後才知道的,pip 裝了十分鐘,編譯了一堆東西,最後匯入的時候給我一個很客氣的錯誤,意思是「你的卡不行」。
很多模型套件在匯入時會提醒沒有 FlashAttention,把它明寫成不裝,用 PyTorch 內建的 scaled_dot_product_attention。實測 SDPA 比 eager 快約 11.7%。不是 flash-attn 那種倍數級的差距,但免費,改一個字串就有。vLLM 也不裝,理由相同,它假設的硬體不是這一張,硬裝只會得到一堆你看不懂的錯誤和一個更慢的結果。
沒有原生 bf16
Turing 沒有原生的 bfloat16。多數模型用 fp16 沒問題,但有些新模型只提供 bf16 的 checkpoint,套件在 CUDA 上又不給你改 dtype 的路徑,這時 PyTorch 會用模擬的方式跑,慢一些但能跑。
看別人公布的速度數字時,記得他們多半是在有原生 bf16 的卡上量的。同一個模型在我的卡上可能慢兩三成,這不是模型的問題,也不是我設定錯,是硬體世代的稅。繳稅的時候心情不會好,但至少知道錢去哪了。
顯存的邊界會安靜地被越過
這一段是整篇最重要的,因為它會騙你。
Windows 上的 NVIDIA 驅動程式有一個行為:顯存不夠時,會把溢出的部分退到系統記憶體繼續跑,程式不會收到記憶體不足的錯誤,只是慢很多。我量過同一個模型四句一批峰值 7.21 GB,八句一批 10.18 GB,後者超過了這張卡,卻照樣跑完,只是慢得很奇怪。
這是最危險的一種失敗:看起來能跑,數字不能信。你會以為「八句一批比四句一批快 30%」,其實它應該快 60%,剩下的被記憶體搬運吃掉了,而你完全不知道。任何批次大小的實驗,先看峰值顯存,再看速度。nvidia-smi 開著放旁邊,像開車看時速表一樣。
鎖高時脈反而慢了一成四
這個實驗我一定要講,因為它太反直覺了。
自迴歸解碼時顯卡看起來被限制住了:標稱 2145 MHz 只跑約 500 MHz,215 W 只吃 39 W,風扇幾乎不轉。我看著這些數字想「它在偷懶」,然後用 nvidia-smi -lgc 把時脈鎖到 1800 MHz,準備迎接飛快的速度。
同一句從 71.9 秒變成 82.0 秒,慢了 14%。GPU 使用率反而從 33% 掉到 14%。
我盯著那個數字大概三分鐘,重跑了兩次,一樣。原因是瓶頸根本不在 GPU:每個 token 都要經過 Python、kernel 啟動和 Windows 顯示驅動的排程,GPU 大部分時間在等 CPU 把下一個工作送過來。鎖高時脈只是讓它等得更快,等得更快就是更浪費電、更熱、然後更慢。nvidia-smi -rgc 復原。這件事教我的是:一台看起來在偷懶的機器,可能只是在等另一台。
小劇場:室友的建議
「你這張卡跑那麼慢,為什麼不去借系上的機器?」
「借得到的時候借過。問題是借來的機器跑得動的東西,回到我這裡跑不動,我還是要知道我這裡的邊界。」
「所以你在自己的爛卡上受苦是為了學東西?」
「講爛卡太過分了,它 2019 年的時候很厲害。」
「現在是 2026 年。」
「我知道。」
幾個環境,幾種 PyTorch
不同模型套件釘的 Transformers 版本可能只差一個小版號,卻不能混用,所以每個模型家族一個 venv。這聽起來很浪費硬碟,實際上是用硬碟空間換心理健康,因為兩個套件在同一個環境裡互相覆蓋對方的相依,是那種要花一整晚才找得到的問題。
真的需要顯卡的才裝 CUDA 版的 torch;判斷模型、影像分層這類能在 CPU 上跑的,裝 CPU 版,SAM 2 安裝時關掉 CUDA 擴充的編譯。顯卡只有一張,能不上 GPU 的工作就不上,把它留給真的需要的那幾個。就像宿舍只有一台微波爐,能用電鍋的就不要搶微波爐。
我對這張卡的看法
它是 2019 年的卡,在 2026 年跑這些模型並不體面,但它逼我量。每一個「應該會快」的改動都要量,因為在這張卡上直覺錯的比對的多,而直覺錯的時候它不會告訴你,它只會默默地慢。
哪天換卡,這套量測方法會跟著過去,那大概是它留給我最有用的東西。至於什麼時候換,大概是它某天早上決定不再醒來的時候。在那之前,我們繼續一起穿舊球鞋跑步。
顯卡世代那張表
NVIDIA 的每一代架構都有一個「運算能力」的版號,決定它支援哪些指令。Turing 是 7.5,2018 年的卡;Ampere 是 8.x,2020 年,從這一代開始有原生的 bf16 和 flash-attn 需要的那些指令;Ada 是 8.9,2022 年;Hopper 是資料中心用的 9.0。很多模型套件的文件會寫「需要 compute capability 8.0 以上」,看到這行字,Turing 的使用者就可以直接關掉頁面了,省得裝了才知道。
fp16 和 bf16 的差別在哪?兩個都是 16 位元的浮點數,fp16 精度高但範圍小,bf16 精度低但範圍跟 32 位元一樣大。訓練大模型時 bf16 比較不會溢位,所以新模型多半用 bf16 存 checkpoint。Turing 的 tensor core 只認得 fp16,遇到 bf16 就得用軟體模擬,慢,但能跑。這是硬體世代和軟體趨勢之間的一道縫,我這張卡剛好卡在縫裡。
PyTorch 本身是 2016 年 Meta 從一個叫 Torch 的 Lua 函式庫改寫成 Python 的,2022 年移交給 Linux 基金會底下的 PyTorch 基金會。2.0 版加的 torch.compile 可以把模型編譯成更快的形式,理論上對我這種 CPU 瓶頸的情況會有幫助,但很多語音模型的自訂層還不相容,我試過一次,放棄了。
常見的三個誤會
第一個誤會是「顯存越大越好,其他不重要」。顯存決定你能不能載入,架構決定你能用哪些加速。一張 12 GB 的舊卡可能比一張 8 GB 的新卡慢很多,因為新卡有原生 bf16 和更快的 tensor core。買卡看兩個數字,不是一個。
第二個誤會是「系統記憶體可以補顯存」。Windows 的驅動會這樣做,但那是逃生口,不是功能。走那條路的速度可以慢十倍以上,而且它不告訴你。
第三個誤會是「PyTorch 版本越新越好」。新版的 PyTorch 配舊的 CUDA 或舊的驅動可能裝不起來,模型套件釘的 Transformers 版本又配特定範圍的 PyTorch。三個版本要對齊,不是各自最新。這是為什麼每個模型家族一個 venv,各自釘死。
一個週末可以試的小練習
打開 Python,import torch,然後 torch.cuda.get_device_capability(),看你的卡是哪個版號。再 torch.cuda.is_bf16_supported(),看它怎麼回答。這兩行會直接告訴你,網路上那些「需要 8.0 以上」的模型你能不能跑。
然後做一個小實驗:建一個大一點的張量,torch.randn(8192, 8192, device="cuda"),做一次矩陣乘法,用 torch.cuda.max_memory_allocated() 看峰值。把尺寸加倍再做一次。持續加大,直到它變得很慢但沒有報錯,那一刻你就親眼看到了「顯存溢出到系統記憶體」。之後 torch.cuda.reset_peak_memory_stats() 清掉再來。這個實驗十分鐘,會讓你對自己的卡有一個很具體的認識。
後記
室友說我在爛卡上受苦是為了學東西,這句話我後來想了很久。其實不完全是為了學東西,是因為換卡要錢,而我沒有。但「沒有錢所以只好搞懂」這件事,回頭看反而是這幾年最划算的一筆投資。等哪天有錢換了新卡,我大概會很快,然後很快地忘記那些邊界在哪。希望到時候我會回來讀這篇。
官方網站:pytorch.org
留言(0)
還沒有留言。讀完有想法,歡迎留下第一則。