
專案 GitHub:basetenlabs/deepseek-ultrafast
DeepSeek Ultrafast 是 Baseten 在 GitHub 公開的推論引擎,用 Rust 和 CUDA 寫成,只服務 DeepSeek-V4.1-Flash 一個模型,也只跑 NVIDIA Blackwell 世代的 GPU:README 寫的編譯目標是 sm_100a(B200)和 sm_103a(B300、GB300)。README〈Requirements〉列的硬體是 4 張 GB300、4 或 8 張 B300,或 4 或 8 張 B200;另外要 CUDA 13 工具組、放在 models/ 目錄的模型權重,以及一個 Redis,讓多台伺服器協調 KV 快取(KV cache)。KV 快取是模型把每個 token 在每一層算出的 key、value 向量存起來,產生下一個 token 時就不必重算。引擎支援 100 萬 token 的上下文,伺服器提供 OpenAI 相容的 API,啟動後約 20 秒,/readyz 回傳 200,就可以接請求。
Baseten 是提供模型推論 API 的公司,DeepSeek-V4.1-Flash 的加速版 DeepSeek-V4.1-Flash-Fast 就放在它的 Model APIs 上,但 README 沒有說 Flash-Fast 是不是用這個引擎服務。README 說之後還會有更多效能改進和開源釋出(「More performance improvements and open source releases are coming.」)。
GitHub 顯示倉庫在 2026 年 10 月 27 日建立。第一個 commit 在 10 月 29 日一次匯入 123 個檔案、36,157 行,之後只有兩個 commit:11 月 12 日更新相依套件,11 月 13 日在 README 補上每步時間和接受長度。Rust 程式約 17,300 行,CUDA 有 33 個 .cu 和 7 個 .cuh 標頭檔,約 14,200 行。本文依據的版本是 commit 3aad78f。
授權是 Elastic License 2.0(ELv2),授權人是 Baseten:使用者可以使用、複製、修改和散布,但不得把它當作託管服務提供給第三方。原始碼公開,授權卻不在 OSI 認可的開源授權之列。
DeepSeek-V4.1-Flash 是 MoE(mixture of experts,混合專家)模型:每個 MoE 層裡有許多個稱為「專家」的子網路,由路由器替每個 token 挑出其中幾個來計算,這些可挑選的專家叫路由專家;一個 token 實際用到的參數,叫啟用參數。依 Hugging Face 上的模型卡和設定檔,這個模型的骨幹(backbone)參數有 5,520 億,由 40 層 Transformer 組成;每個 MoE 層有 1 個共享專家和 384 個路由專家,每個 token 啟用 6 個路由專家。模型卡把推論分成預填(prefill,一次讀入整段提示)和解碼(逐個產生輸出 token):預填時每個 token 啟用 80 億參數,解碼時啟用 160 億。模型卡把原因歸於它的因果編碼器-解碼器(Causal Encoder-Decoder)架構:40 層分成 20 層編碼器和 20 層解碼器,解碼器的全域 KV 快取由編碼器最後一層的隱藏狀態投影而來;至於這為什麼讓預填只啟用一半的參數,模型卡沒有再說明。模型本身採 MIT 授權。
README 自己量到的單一請求解碼速度,4 張 GB300 是每秒 1,740 個 token,8 張 B300 是每秒 2,060 個;預填 100 萬 token 的提示,分別花 13.2 秒和 11.7 秒,解碼速度只來自一個要模型寫 Tetris 網頁遊戲的提示。以下說明它怎麼做推測解碼、怎麼讓多台伺服器共用 KV 快取,再整理 README 的數字怎麼量出來,以及使用前要知道的限制。
用模型自帶的 MTP 層草擬
推測解碼(speculative decoding)先用便宜的方式草擬接下來幾個 token,再讓完整模型一次算完這些位置,留下它認可的部分。完整模型驗證幾個位置花的時間,和只算一個位置相差不多,所以每一步能多產生幾個 token。README 的說法是:每個解碼步驟草擬一塊 token,再驗證這一塊;接受長度(acceptance length)就是平均每步產生的 token 數,包含驗證後完整模型補上的那一個。
引擎拿模型自帶的層來草擬。DeepSeek-V4.1-Flash 的權重除了 40 層 Transformer,還附了 3 個多 token 預測(multi-token prediction,MTP)層(inference/config.json 的 n_mtp_layers)。模型卡沒有說明 MTP 層原本的用途,只把這套推測解碼稱為 DSpark,說明是「半自迴歸的草擬生成,加上依信心排程的驗證」(semi-autoregressive draft generation with confidence-scheduled verification)。引擎在 crates/engine/src/model.rs 把這 3 層的 KV 快取和主模型分開,存成 mtp_caches。
引擎草擬一塊 token 分兩步(model.rs 的 dspark_draft)。第一步,把上一個確定的 token 放在第一格,其餘格子填入設定檔指定的佔位 token(dspark_noise_token_id),整塊一起跑過 3 個 MTP 層,得到每一格的預測。第二步,一個低秩的小型輸出層(程式裡叫 markov_head,秩 256)從第一格往後逐格處理:用前一個草擬 token 的嵌入修正這一格的預測,再抽出這一格的草擬 token(crates/kernels/cu/draft_markov.cu)。
引擎也邊草擬邊算信心。信心值來自最後一個 MTP 層附帶的信心頭(權重 confidence_head.proj.weight):它對這一格的隱藏狀態和前一個 token 的嵌入做線性投影,得到一個分數。分數經 sigmoid 轉成機率後逐格連乘,乘積低於門檻就停止草擬,這一步只驗證前面那幾格。門檻在 crates/cuda/src/profile.rs 設定,4 卡是 0.3,8 卡是 0.2;草擬塊有 3 格以上時,最後兩格的門檻降為一半和四分之一(model.rs 第 3900 到 3904 行)。
草擬塊的大小,模型設定檔寫 5;引擎在 profile.rs 依硬體覆寫:sm_103a 的 4 卡和 8 卡組態(也就是 README 測速用的 GB300 和 B300)用 6,8 張 B200 用 7,4 張 B200 維持 5。驗證時每一步最多產生的 token 數,比草擬塊多一個,因為完整模型在接受的草擬 token 之後還會補上一個。
逐 token 驗證與整塊驗證
推測解碼要保證輸出的機率分布不變:開了推測,輸出仍和完整模型自己一個一個取樣時同分布。docs/SERVER.md 寫明 temperature 和 top_p 固定為 1,其他值會被拒絕,所以伺服器一律照完整模型的機率分布取樣。驗證時就不能只看草擬 token 是不是最可能的那個,要用拒絕取樣維持分布。
符號照取樣的 GPU 核心程式(kernel,crates/kernels/cu/sample.cu)從 0 起算。草擬塊有 格,第 格的草擬 token 記為 ; 是草擬層在這一格的機率分布, 是完整模型在這一格的機率分布。第 格沒有草擬 token,留給完整模型補上的那一個。核心程式有兩種模式,crates/engine/src/bin/test_block_verify.rs 第 149 到 177 行用 float64 寫了兩者的參考實作。
逐 token 模式(測試裡叫 rejection)從第 0 格開始,以 的機率接受 。第 格被拒絕就停下,改從殘差分布 (正規化後)抽一個 token 補上; 格都接受時,從 再抽一個 token 補上。
整塊模式(block)先沿著草擬塊算累積權重:
完整模型比草擬層更看好某個前綴時,權重封頂在 1;越不看好,權重越小。接著對每個長度 (1 到 )各抽一個亂數,和這個長度的門檻比較:
是第 格剩下的機率質量;最後一個長度的門檻直接用 。接受的草擬 token 數 ,取亂數低於門檻的最長長度,都沒通過就是 0。 小於 時,補上的 token 從 正規化後的分布抽出,這個殘差質量為 0 時改從 抽; 等於 時,和逐 token 模式一樣從 補一個 token。所以兩種模式每一步都產生 個 token,接受長度就是 的平均。
兩種模式的差別在遇到失敗之後:逐 token 模式在第一個失敗處停下;整塊模式每個長度都檢查,較短的前綴沒通過,較長的前綴仍可能通過,所以平均能接受較長的前綴。
這就是 Sun 等人 2024 年在〈Block Verification Accelerates Speculative Decoding〉提出的整塊驗證:累積權重、門檻和殘差分布,都對得上論文的演算法 2 與式(4)、(5)。論文證明,它在每一步期望產生的 token 數上是最佳的,也不會比逐 token 驗證差;實驗中,相對逐 token 驗證,實際時間快 5% 到 8%。倉庫裡沒有引用這篇論文。
test_block_verify.rs 在五組只有三個可能 token 的合成分布上跑兩種模式,預設每組 10,000 次,檢查三個條件:核心程式算出的 和 float64 參考實作一致;連續三個 token 的出現頻率,落在完整模型分布的統計誤差範圍內;整塊模式平均接受的草擬 token 數,不少於逐 token 模式的 98%。引擎本身在 model.rs 的 pick_head 把模式固定為整塊(block: 1),逐 token 模式在這個版本只出現在測試裡,當作對照。
草擬塊為什麼最多 7 個 token?
驗證時,上一個確定的 token 和所有草擬 token 排成一批列(row),一起跑一次前向計算,7 個草擬 token 就是 8 列。這裡牽涉兩種矩陣運算指令:tcgen05 是 Blackwell 第五代張量核心(tensor core)的指令,mma.sync 是較早世代就有的 warp 層級矩陣乘法指令。
model.rs 的 moe_rows_ok(第 2449 到 2464 行)拒絕 9 到 16 列的解碼步驟,錯誤訊息有兩則。第一則說,張量核心版本的 MoE 一次最多處理 8 列,請把草擬塊設在 7 以內。第二則說,解碼用的幾個矩陣向量乘法(wqkv、wq_b、wo_a/wo_b、gate)走 tcgen05 時只支援 8 列以內;列數更多,這一步會改走 mma.sync,一般解碼卻走 tcgen05,兩者數值不同,訊息寫成「spec != plain」。超過 16 列的情況更早就被擋下:capture_spec_blk 要求草擬塊加 1 不超過程式常數 MAX_M,也就是 16(第 3958 行)。第 3003 行在 MoE 計算前又檢查一次,訊息是草擬塊大於 7 的解碼步驟不支援。
拒絕取樣的保證有個前提:驗證時算出的 ,就是一般解碼會算出的 。驗證換了指令路徑, 本身就有微小差異,開推測和不開推測的輸出也就不再一致。所以 7 個 token 是這個引擎草擬塊的上限。
多台伺服器怎麼共用 KV 快取?
README〈KV cache sharing in multi-server setups〉說明,每台伺服器把最近幾段對話的 KV 快取留在主機記憶體,也就是 CPU 端的記憶體,不是 GPU 記憶體。同一段對話的下一輪落到另一台伺服器時,那台伺服器直接從持有快取的伺服器抓過來,用 RDMA(remote direct memory access,遠端直接記憶體存取)從主機記憶體傳到主機記憶體,不必重算整段提示。Redis 記錄每段對話的快取歸哪一台伺服器,並廣播擁有權的變動。README 接著寫:
顯示原文與出處
Thanks to DeepSeek’s extreme KV cache compression, the transfers are very fast and can be done on every request.
This setup removes the need for prefix-aware routing and enables better load balancing.
多虧 DeepSeek 對 KV 快取的極度壓縮,傳輸非常快,每個請求都可以做。
這樣的配置不再需要前綴感知路由,也能把負載平衡得更好。
README 沒有給壓縮率的數字。模型卡寫道,全域 KV 快取每個 token 約 890 bytes,約為 DeepSeek-V4-Flash 的四分之一。
前綴感知路由(prefix-aware routing)把開頭相同的請求導到已經算過那段前綴的副本(replica,也就是一台載入完整模型的伺服器),好重用那裡的 KV 快取。在這個引擎裡,一台伺服器用 4 或 8 張 GPU 跑一份完整模型;任何一台伺服器都能把快取抓過來,請求就可以只照負載分配。
快取的擁有權以對話為單位。伺服器從請求標頭(例如 X-Session-ID)取得對話 ID,算出 SHA-256 當作鍵(crates/kv-transfer/src/lib.rs 的 lookup);docs/SERVER.md 說,多輪請求沒帶對話 ID 會回 HTTP 400。
Redis 上的目錄由 crates/kv-transfer/src/directory.rs 管理,每段對話一筆紀錄,存紀元(epoch)、序號和擁有者。查詢、認領和檢查都是一段原子執行的 Lua 腳本。伺服器認領一段對話時,必須附上先前讀到的紀元與序號;Redis 上的值已經變了,認領就失敗,回傳 STALE ownership changed, expired or directory restarted。認領成功時,序號加一、擁有者換成新的伺服器,並在 pub/sub 頻道發出通知。這是以版本號做的樂觀鎖:不先上鎖,提交時才比對版本。
紀錄每次被存取都重設 24 小時的期限,一天沒人存取就過期。紀元是 Redis 第一次使用時寫入的隨機 UUID;Redis 重啟、資料清空後會產生新的紀元,舊紀元下的版本全部作廢。docs/SERVER.md 的說法是,Redis 重啟只會失去快取擁有者的紀錄,不會失去模型狀態。
搬運快取的部分在 lib.rs 和 crates/kv-transfer/src/snapshot.rs。要抓快取的伺服器透過 Velo 向擁有者發出請求;Velo 是 ai-dynamo 組織的函式庫,README 列它負責伺服器之間經 RDMA 的 KV 傳輸,Cargo.toml 把它鎖定在 commit 2c754eb。擁有者回傳一份清單(manifest)和資料的 RDMA 位址,接收端再用 RDMA 讀取資料。清單記錄格式版本、相容性指紋(fingerprint)、這份快取對應的 token 序列、資料大小和 BLAKE3 校驗碼。接收端先檢查版本和指紋,再比對清單上的 token 序列和新請求的共同前綴,只重用這段前綴;資料讀完後重算 BLAKE3,和清單上的值比對。
docs/SERVER.md 寫明,只有模型、tokenizer、--max-seq、GPU 數量和「decode numerics」都相同的伺服器,才能共用 KV 快取。SERVER.md 沒有再解釋 decode numerics;從 crates/server/src/peer.rs 看,它指幾個解碼核心程式實際選用的切塊與執行方式(例如 wo_a 的切塊設定),這些設定和其他條件一起算進相容性指紋。
Baseten 量到的速度怎麼來?
README〈Performance〉的表格如下。TP4、TP8 指用張量平行(tensor parallelism)把模型分到 4 張、8 張 GPU 上。
| 4 × GB300(TP4) | 8 × B300(TP8) | |
|---|---|---|
| 解碼(Tetris 提示) | 每秒 1,740 token | 每秒 2,060 token |
| 每步時間 | 3.30 ms | 2.71 ms |
| 接受長度 | 每步 5.6 token | 每步 5.7 token |
| 預填 | 100 萬 token 花 13.2 秒(每秒 76,000 token) | 100 萬 token 花 11.7 秒(每秒 85,000 token) |
README〈Reproduce the demo〉附了量解碼速度的 Python 腳本。它送出一個請求,提示是「implement the tetris game. output HTML code directly - no explanation.」,關掉思考模式("thinking": {"type": "disabled"}),用串流接收輸出。速度是伺服器回報的輸出 token 數,除以第一個內容 token 出現到輸出結束的時間,不含首 token 延遲。README 給的範例輸出是 5142 tokens at 1,717 tokens/s (first token after 0.04 s)。
README 說,他們在 4 × GB300 上看到每秒 1,700 到 1,770 個 token,8 × B300 上是 1,980 到 2,170 個,沒有寫跑了幾次。每步時間和接受長度來自伺服器 request_end 日誌的 average_iteration_ms 與 average_accept_length,30 次的範圍是:4 × GB300 為 3.24 到 3.37 ms、每步 5.1 到 6.1 token;8 × B300 為 2.69 到 2.74 ms、每步 5.3 到 6.1 token。
解碼速度大約等於接受長度除以每步時間:5.6 ÷ 3.30 ms 約為每秒 1,700 個 token,5.7 ÷ 2.71 ms 約為每秒 2,100 個,和表上的 1,740、2,060 相近。30 次之間,每步時間的範圍不到平均值的 4%,接受長度的範圍卻有平均值的 18%(4 × GB300:(6.1 − 5.1) ÷ 5.6),所以同一個提示重跑時,速度的起伏主要來自接受長度。這兩種組態的草擬塊是 6 個 token,每步最多產生 7 個,5.6 和 5.7 是上限的八成左右。
預填的數字,README 只給結果,沒有說明用什麼提示、怎麼量。README 也沒有 B200 的數字。
用之前要知道哪些限制?
數字都是 Baseten 自己量的,而且只用一個提示,README 也沒有拿 vLLM、SGLang 這類通用推論框架在同一硬體上對照。依本文的推想,寫 Tetris 的 HTML 是高度可預測的程式碼輸出,草擬層容易猜中;即使是同一個提示,30 次的接受長度也在 5.1 到 6.1 之間。docs/SERVER.md 寫明伺服器對一般請求預設開啟思考模式,測速卻把它關掉;一般對話或開著思考模式時,接受長度和解碼速度都可能不同,README 沒有這些情境的數字。
單一請求的速度也不等於吞吐量。從 crates/server/src/engine.rs 和 request_queue.rs 看(這是本文從程式讀出的,README 沒寫),一台伺服器一次只解碼一個請求,其他請求排隊。排隊期間,另一條執行緒逐一替請求做準備:到 Redis 查這段對話的快取歸哪台伺服器,在本機就挑出可重用的快取,在別台就用 RDMA 抓到本機的主機記憶體;準備好、等著輪到的請求最多 6 個。README 沒有併發或總吞吐量的數字,讀者也無從判斷它比通用框架快多少。
它只支援一個模型、一代 GPU:DeepSeek-V4.1-Flash,以及 sm_100a、sm_103a。docs/SERVER.md 還要求一定要有 Redis 和可用的 RDMA 埠。
授權限制託管服務。LICENSE〈Limitations〉的第一條是:
顯示原文與出處
You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.
你不得把本軟體當作託管或代管服務提供給第三方,只要該服務讓使用者能取用本軟體相當一部分的特性或功能。
核心程式怎麼寫出來的,README 只交代了一句。README〈Acknowledgments〉寫道:
顯示原文與出處
The kernels were optimized by coding agents that could read the source of FlashInfer and SGLang while they worked.
這些核心程式由程式碼代理最佳化,代理在工作時可以讀 FlashInfer 和 SGLang 的原始碼。
README 沒有說明這些代理怎麼工作、改了哪些核心程式,也沒有說明人工審查到什麼程度;第一個 commit 是一次匯入,開發過程也看不到。倉庫在 2026 年 10 月 27 日建立,README 的每步時間與接受長度是 11 月 13 日的 commit 3aad78f 才補上的,引用數字時要對照版本。
參考資料
- Baseten,basetenlabs/deepseek-ultrafast(ELv2)。本文依據 commit
3aad78f,讀了README.md、LICENSE、docs/SERVER.md、crates/engine/src/model.rs(moe_rows_ok第 2449–2464 行、第 3003 行、pick_head、dspark_draft)、crates/engine/src/bin/test_block_verify.rs(第 149–177 行)、crates/kernels/cu/draft_markov.cu、crates/cuda/src/profile.rs、crates/kv-transfer/src/的lib.rs、directory.rs、snapshot.rs,以及crates/kernels/cu/sample.cu、crates/server/src/的engine.rs、request_queue.rs、peer.rs。 - DeepSeek,DeepSeek-V4.1-Flash 模型卡與
inference/config.json。 - Ziteng Sun、Uri Mendlovic、Yaniv Leviathan、Asaf Aharoni、Jae Hun Ro、Ahmad Beirami、Ananda Theertha Suresh,〈Block Verification Accelerates Speculative Decoding〉,arXiv:2403.10444,2024 年 3 月 15 日首次上傳。
- Elastic License 2.0。
- ai-dynamo/velo(Apache 2.0)。
- 本站的〈Strata 拆解:一張 12 GB 顯示卡怎麼跑 1250 億參數的模型〉同樣是只服務單一模型的推論引擎,目標硬體卻相反:Strata 針對一張 12 GB 的家用顯示卡,DeepSeek Ultrafast 針對 4 到 8 張資料中心的 Blackwell GPU。