LMSYS: Blog (Chatbot Arena )
85

Nghiên cứu

Ling-3.0-flash: Tối ưu hóa độ trễ giải mã lên 54% trên 4 GPU Blackwell

(giờ Việt Nam)

Tóm tắt AI

Đội ngũ Ant Group và RadixArk đã tối ưu hóa mô hình Ling-3.0-flash, giúp tăng tốc độ giải mã đơn yêu cầu từ 288 lên 606 tok/s và giảm độ trễ TPOT xuống còn 1.53 ms nhờ kiến trúc MoE cải tiến.

Bản dịch AI

Chasing the Batch-1 Floor: Ling-3.0-flash Speculative Decode on Blackwell

Giải mã Batch-1 (lô-1) ngày càng trở nên quan trọng. Ví dụ, Xiaomi MiMo đã công bố MiMo-V2.5-Pro UltraSpeed vào tháng 6, tuyên bố đạt tốc độ giải mã 1.000 tok/s trên một mô hình MoE có một nghìn tỷ tham số.

Batch 1 không để lại không gian cho một ngăn xếp suy luận (inference stack) che giấu chi phí vận hành (overhead). Không có lô nào để phân bổ chi phí khởi chạy, không có tính đồng thời để lấp đầy các khoảng trống trong đường ống (pipeline bubbles), và không đủ cường độ tính toán để các kỹ thuật tiling thông minh phát huy tác dụng. Mỗi micro giây trên đường dẫn tới hạn (critical path) là một micro giây mà người dùng phải chờ đợi.

Bài viết này tập trung vào việc giảm mức sàn đó cho Ling-3.0-flash, một mô hình MoE sử dụng cơ chế chú ý tuyến tính (linear-attention) lai, chạy trên 4 GPU NVIDIA Blackwell. Bài viết đề cập đến hai lộ trình giải mã suy đoán (speculative decoding). Trên lộ trình NEXTN/MTP, chúng tôi đã tăng tốc độ giải mã cho một yêu cầu đơn lẻ từ 288 tok/s lên 606 tok/s và giảm TPOT trung bình từ 3,33 ms xuống 1,53 ms. Lộ trình thứ hai là DSpark, một bộ giải mã suy đoán được lập lịch dựa trên độ tin cậy (confidence-scheduled) được xây dựng trên cùng một ngăn xếp: một lượt chạy 1.000 yêu cầu đạt 1.120 tok/s với TPOT trung bình là 0,78 ms và độ dài chấp nhận (accept length) là 9,95. So sánh cuối cùng này là so sánh có kiểm soát: NEXTN và DSpark được đo lường bằng cùng một lệnh trên cùng một máy, và TPOT trung bình thấp hơn 1,9 lần (từ 1,53 ms xuống 0,78 ms). Phần còn lại của bài viết sẽ giải thích thời gian đó đã đi đâu và cần những gì để lấy lại nó.

Các điểm nổi bật

Hình 1. Kết quả tiêu đề trên bốn cấu hình.

Trên GSM8K, cùng một ngăn xếp đạt kết quả: độ chính xác 0,889, không hợp lệ 0,000, độ trễ 341,5 s, thông lượng đầu ra 511,1 tok/s.

Tất cả các lượt chạy đều sử dụng Ling-3.0-flash trên 4 GPU Blackwell, TP4, bf16, độ đồng thời 1, giải mã tham lam (greedy decoding), và cùng một khối lượng công việc ngẫu nhiên cố định 8192-đầu vào / 1024-đầu ra. Từ trái sang phải, các cột hiển thị đường cơ sở NEXTN ban đầu, NEXTN sau khi sửa đồ thị draft-extend, NEXTN đã tinh chỉnh cuối cùng, và DSpark. Hai cột đầu là các điểm kiểm tra chiến dịch ngắn hơn; hai cột cuối là so sánh có kiểm soát, mỗi cột được đo trên cùng 1.000 yêu cầu trên cùng một máy. Thông lượng đỉnh chỉ được so sánh giữa hai lượt chạy cuối cùng vì đó là giá trị tối đa trong các cửa sổ một giây cố định.

Hai định nghĩa quan trọng ở đây, vì chúng giải thích tại sao thông lượng đầu ra không đơn giản là nghịch đảo của TPOT trung bình ngay cả ở độ đồng thời 1: TPOT của SGLang loại trừ TTFT, trong khi thông lượng đầu ra chia tổng số token đầu ra cho tổng thời gian thực tế của benchmark (xem hướng dẫn bench_serving). Tất cả các lượt chạy benchmark tiêu đề trong bài viết này đều sử dụng khối lượng công việc ngẫu nhiên tổng hợp; độ dài chấp nhận đặc biệt phụ thuộc vào prompt và phân phối đầu ra, vì vậy 9,95 là độ dài chấp nhận của khối lượng công việc này chứ không phải của mô hình.

Mô hình

Hình 2. Kiến trúc Ling-3.0-Flash: 42 lớp xen kẽ 35 lớp chú ý tuyến tính KDA với 7 lớp chú ý toàn phần MLA trên nền tảng MoE 512 chuyên gia.

Ling-3.0-flash là một mô hình MoE chú ý lai (BailingMoeV3), và hầu hết những gì theo sau đều xuất phát từ từ "lai" đó.

Cứ sáu lớp chú ý thì có năm lớp là KDA. Đó là lý do tại sao chú ý MLA chỉ tốn 244 µs mỗi bước ở ngữ cảnh 8k trong cấu hình cuối cùng, và tại sao mô hình này ngay từ đầu đã là một mục tiêu tốt cho batch-1: với chi phí chú ý thấp và lô cực nhỏ, những gì còn lại trên đường dẫn tới hạn là băng thông trọng số và độ trễ khởi chạy, đây chính xác là chế độ mà bài viết này đề cập.

Hình dạng của một bước batch-1

Chúng tôi giải mã bằng giải mã suy đoán NEXTN tại steps=5, topk=1, draft_tokens=6. Một bước giải mã là ba đồ thị CUDA trong một chuỗi chuyển tiếp.

Hình 3. Ba đồ thị mỗi bước. Mô hình dự thảo (draft model) đề xuất một chuỗi 6 token, mô hình mục tiêu (target model) chấm điểm tất cả sáu token trong một lần chuyển tiếp (forward), và đồ thị mở rộng (extend graph) phát lại tiền tố đã được chấp nhận với các trạng thái ẩn thực tế của mục tiêu để tạo ra hạt giống (seed) cho vòng tiếp theo. Bản thân phán quyết (eagle_sample) diễn ra bên trong đồ thị xác minh (verify graph); máy chủ biết được bao nhiêu token đã được chấp nhận chậm một bước.

Bản dự thảo là một mô hình NEXTN một lớp chạy tự hồi quy: năm bước nhưng chỉ có bốn lần chuyển tiếp, vì ứng viên đầu tiên đến từ hạt giống của vòng trước và ứng viên thứ năm được đọc từ top-k của lần chuyển tiếp thứ tư. Xác minh là một lần chuyển tiếp của toàn bộ mục tiêu 42 lớp trên tất cả sáu vị trí chuỗi. Mở rộng sửa lỗi bộ nhớ đệm KV của bản dự thảo, vốn chỉ thấy các dự đoán của chính bản dự thảo, và trả lại hạt giống cho vòng tiếp theo.

Những gì đi qua giữa ba đồ thị trên CPU là không có gì. Các hình dạng cố định cộng với phần đệm (padding) làm cho mọi số đếm phụ thuộc vào việc chấp nhận trở thành một chỉ mục GPU thay vì một giá trị máy chủ; các bộ đệm bền vững cho phép các đồ thị sản xuất ghi thẳng vào bộ đệm tiêu thụ; và các quyết định thực sự cần giá trị trên CPU (EOS, chuỗi dừng, giải mã token) đi qua một luồng phụ D2H và một sự kiện copy_done được tiêu thụ chậm một bước. Mọi thứ bên dưới đều dựa trên đặc tính đó.

Hai loại thời gian nhàn rỗi

Khi chúng tôi bắt đầu, GPU bận khoảng hai phần ba thời gian của bước. Thời gian nhàn rỗi ở batch 1 có hai loại, và chúng cần các chẩn đoán riêng biệt vì các bản sửa lỗi không có điểm chung:

Hình 4. Hai hình dạng của sự nhàn rỗi. Trên: vòng lặp máy chủ dài hơn công việc của GPU, vì vậy các lỗ hổng ít và rộng, nằm ở các đường nối giữa các đồ thị. Dưới: khi máy chủ đã được ẩn, những gì còn lại là vài trăm nút nhân (kernel nodes) 1,5-6 µs có mức sàn khởi chạy cạnh tranh với số học của chúng, cộng với chính việc đọc trọng số.

Hai loại nhàn rỗi này mô tả khía cạnh thời gian bước của TPOT. Đòn bẩy khác là số lượng token mỗi bước cam kết: TPOT trung bình ≈ thời gian bước / độ dài chấp nhận trung bình. Phần còn lại của bài viết theo sát các đòn bẩy đó. Việc chạy trước của máy chủ (host run-ahead) và công việc tại đường nối loại bỏ sự nhàn rỗi ở chế độ máy chủ; PDL, thay đổi kiểu dữ liệu, hợp nhất và tinh chỉnh lại làm ngắn đường dẫn tới hạn của GPU; tinh chỉnh suy đoán và DSpark làm tăng số lượng token được cam kết mỗi bước mục tiêu. DSpark sau đó xem xét lại danh mục đầu tiên khi một lần đọc D2H chặn lại giới thiệu lại một ghim máy chủ (host pin).

Sửa thước đo trước khi sửa máy

Ba đặc tính của thiết lập đo lường định hình mọi con số bên dưới.

Bộ phân tích hiệu năng (profiler) làm phồng các sự kiện phía máy chủ. CUPTI thêm chi phí vào mỗi sự kiện máy chủ mà nó ghi lại. Trên cùng một cấu hình, một bước được phân tích đo được 5,2 ms trong khi bước thực tế, được tính toán ngược từ TPOT × độ dài chấp nhận trên một lượt chạy không phân tích, là 4,9 ms. Khoảng cách 0,3 ms đó cùng bậc với các hiệu ứng phía máy chủ mà chúng tôi muốn suy luận, vì vậy một dấu vết (trace) được phân tích có thể hiển thị các lần chờ đợi chéo giữa các rank không tồn tại ngoài bộ phân tích. Thời lượng nhân GPU đến từ dấu thời gian phần cứng và đáng tin cậy hơn thời gian phía máy chủ, nhưng không miễn nhiễm: việc theo vết vẫn làm nhiễu thời gian khởi chạy, tính đồng thời, trạng thái bộ nhớ đệm và thực thi đồ thị CUDA, và Nsight Systems ghi lại chi phí đáng kể tiềm tàng cho việc theo vết CUDA và nút đồ thị (hướng dẫn người dùng). Vì vậy, mọi kết luận phía máy chủ ở đây đều được hiệu chuẩn profiled-vs-unprofiled trước.

Các microbenchmark chạy lạc quan cho các nhân có trọng số lạnh (cold-weight). Một vòng lặp gọi một nhân liên tục giữ trọng số cổng 2,6 MB của nó trong L2, trong khi mô hình thực tế xóa L2 với khoảng 94 MB lưu lượng chuyên gia giữa các lần gọi liên tiếp đến cùng một lớp. Nóng 7 µs, lạnh 11 µs: đủ để đảo ngược thứ hạng so với thư viện GEMV.

Thông lượng đỉnh là một thống kê cửa sổ đơn lẻ. Con số đỉnh của benchmark là giá trị tối đa trên lưới 1 giây cố định, vì vậy nó mang khoảng ±5% dải pha: TTFT/TPOT bị dịch chuyển sẽ cắt lại lưới, và một thay đổi cải thiện thông lượng trung bình 2,3% có thể hiển thị là mức giảm từ 909 xuống 858. Cả hai kết quả đọc đều tái tạo chính xác dưới một hạt giống cố định, vì vậy khả năng tái tạo không tách biệt tín hiệu khỏi pha. Các quyết định A/B ở đây được đưa ra dựa trên TPOT trung bình × độ dài chấp nhận trung bình. Tích đó là một ước tính dẫn xuất của thời gian bước thay vì thời gian được đo (tích của hai tổng hợp không phải là tổng hợp của tích), nhưng nó ổn định giữa các lượt chạy và không nhạy cảm với sự trôi độ dài chấp nhận trong các lượt chạy này, đó là điều mà tiêu chí A/B cần. Chúng tôi báo cáo đỉnh nhưng không bao giờ tối ưu hóa dựa trên nó.

Tính đúng đắn có cổng riêng, được áp dụng cho mọi thay đổi trước khi nó được giữ lại: so sánh chính xác từng byte của quá trình tạo tham lam 256-token, độ dài chấp nhận không thay đổi trong phạm vi 0,05, và chạy lại tham lam sau khi xen kẽ các yêu cầu lấy mẫu nhiệt độ để bắt lỗi ô nhiễm trạng thái. Các thay đổi làm thay đổi làm tròn một cách hợp pháp (cổng bf16, kết hợp làm tròn đơn) đã nêu rõ trong thông báo commit và được xác thực trên các chỉ số chấp nhận và tác vụ thay vì chẵn lẻ bit.

Để máy chủ chạy trước

Đây là thay đổi cấu trúc mà phần còn lại của chiến dịch dựa vào, và là một bản sửa lỗi nhàn rỗi ở chế độ máy chủ.

Hình 5. Từ khóa liên động (lockstep) đến đường ống sâu (deep pipelining). Trước: mỗi bước máy chủ chặn trong resolve_seq_lens_cpu chờ đồ thị xác minh trước đó hoàn thành trên GPU, vì vậy việc chạy trước đặt lại về 0 và mỗi phân đoạn chuẩn bị của máy chủ trở thành một bong bóng GPU. Sau: hàng đợi sâu một bước đầy đủ, việc khởi chạy xác minh k+1 dẫn trước quá trình thực thi của chính nó một bước đầy đủ, và sự đồng bộ hóa duy nhất còn lại là sự kiện copy_done được tiêu thụ chậm một bước.

cudaGraphLaunch luôn không đồng bộ, và thứ tự dự thảo → xác minh → mở rộng trên GPU là miễn phí: cùng luồng, FIFO. Vì vậy, câu hỏi không bao giờ là liệu xác minh có chờ dự thảo hay không. Mà là liệu máy chủ có bị ghim vào tiến trình GPU mỗi bước hay không.

Có. Dưới spec-v2, bộ lập lịch không biết độ dài chấp nhận, vì vậy FutureMap.resolve_seq_lens_cpu kéo new_seq_lens từ GPU về trong khi xây dựng lô tiếp theo: bị chặn bởi một sự kiện xuất bản, được sao chép trên một luồng riêng, sau đó synchronize d. Máy chủ không chờ đợi một bản sao quy mô micro giây. Nó đang chờ đồ thị xác minh trước đó thực thi xong. Chi phí trung bình: 485 µs mỗi bước, với độ sâu chạy trước đặt lại về 0 mỗi bước.

Nguyên nhân là cờ needs_cpu_seq_lens, được OR trên mọi backend liên quan đến spec-v2. trtllm_mla khai báo False trong cả ba vai trò; các backend chú ý tuyến tính anh em GDNAttnBackend và Mamba2AttnBackend đều khai báo False một cách rõ ràng. KDAAttnBackend chưa bao giờ khai báo nó và kế thừa mặc định True của lớp cơ sở, mặc dù nó chạy cùng mã siêu dữ liệu lớp cơ sở như hai anh em của nó.

Việc khai báo needs_cpu_seq_lens = False đã làm sụp đổ OR và loại bỏ sự đồng bộ hóa mỗi bước. Lập luận về tính đúng đắn là từng điểm: siêu dữ liệu của KDA không bao giờ đọc gương CPU, và phần đệm phát lại đến từ forward_batch.num_padding.

Làm thế nào máy chủ dám khởi chạy bước k+1 mà không biết bước k đã chấp nhận những gì? Bởi vì các giá trị không bao giờ chạm vào CPU. FutureMap là một rơ-le cư trú trên GPU: đồ thị của bước k ghi các token đầu ra, new_seq_lens, xác suất top-k và trạng thái ẩn vào các bộ đệm thiết bị được lập chỉ mục bởi req_pool_idx, và đồ thị của bước k+1 đọc chúng theo cùng chỉ mục đó. Máy chủ chỉ xử lý các chỉ mục, thứ mà nó đã biết.

Hình 6. Nơi sự chùng xuống (slack) tồn tại. Bảng A: vòng lặp máy chủ (~4,3 ms) nằm gọn dưới bước GPU (~4,9 ms), vì vậy nó được ẩn hoàn toàn. Bảng B: khi jitter (một chương trình phát sóng gloo hoặc tạm dừng GC) vượt quá độ chùng, máy chủ kết thúc muộn và GPU chờ tại ranh giới xác minh tiếp theo, nơi tập thể đầu tiên trong đồ thị hấp thụ độ lệch chéo rank.

Chạy trước cũng thay đổi hình dạng chi phí máy chủ. Thay vì mỗi rank trả thời gian máy chủ của nó trực tiếp mỗi bước, chỉ một rank làm cạn kiệt độ chùng hàng đợi của nó mới phải trả. Trong một dấu vết bốn rank, chính xác một rank ở trạng thái đó: phân đoạn bộ lập lịch của nó chạy dài hơn 5-10 lần so với các anh em của nó, đồ thị dự thảo của nó khởi chạy muộn 40-80 µs, đường nối dự thảo→xác minh của nó chạy +165 µs so với trung vị của những người khác, và nó cho thấy các gai 400-750 µs định kỳ với chữ ký GC. Ba rank còn lại chờ đợi nó tại mỗi điểm hẹn. Chẩn đoán khái quát hóa: thời lượng của một nhân không phải là công việc của nó. Một all-reduce nhúng 20 KB hiển thị 150-480 µs không phải là một all-reduce chậm; nó đang hấp thụ độ lệch, và chỉ sự căn chỉnh thời gian chéo rank mới cho bạn biết rank nào bị trễ.

Đóng các đường nối

Với việc ghim lockstep đã biến mất, các đường nối giữa các đồ thị trở nên đáng để thu hẹp. Trước khi một đồ thị CUDA phát lại, siêu dữ liệu chú ý cụ thể cho từng bước (chỉ mục kv, bảng khối, khe trạng thái mamba) phải được xây dựng lại từ req_to_token và seq_lens trực tiếp vào các bộ đệm tĩnh đã chụp của đồ thị. Việc nạp lại đó chạy một cách háo hức mỗi bước và là phần lớn những gì một đường nối chứa. Tại batch 1, nó hoàn toàn bị ràng buộc bởi máy chủ: mỗi op tốn 5-15 µs để gửi và 1-4 µs để thực thi.

Ling-3.0-flashBlackwellTối ưu hóa AIMoEĐộ trễ
Đọc bài gốc

Bài viết được AI dịch và tổng hợp tự động từ LMSYS: Blog (Chatbot Arena ). Liên kết bài gốc ở phía trên. Dữ liệu đồng bộ qua API công khai được ghi nguồn tại AI HOT (canonical) ↗. AIHOT.vn luôn dẫn nguồn đầy đủ — nếu bạn thấy điểm cần chỉnh sửa, hãy gửi ý kiến tại trang phản hồi.