Triển khai DeepSeek-V4-Flash trên AMD MI300X: Hành trình vượt qua rào cản phần mềm

Phần cứng02 tháng 6, 2026·10 phút đọc

Bài viết chia sẻ quá trình thử nghiệm và tối ưu hóa mô hình AI DeepSeek-V4-Flash trên phần cứng AMD MI300X. Mặc dù sở hữu thông số phần cứng ấn tượng và giá thuê rẻ hơn so với NVIDIA, đội ngũ phát triển đã phải đối mặt với nhiều thách thức về phần mềm như sự khác biệt trong chuẩn FP8 và thiếu các kernel tối ưu hóa. Kết quả cho thấy MI300X là một lựa chọn khả thi và hiệu quả về chi phí cho các tác vụ suy luận AI nếu giải quyết được các vấn đề phần mềm.

Triển khai DeepSeek-V4-Flash trên AMD MI300X: Hành trình vượt qua rào cản phần mềm

Tại Doubleword, chúng tôi đang xây dựng một nền tảng đám mây suy luận (inference cloud) được thiết kế để xử lý khối lượng lớn. Để làm được điều đó, chúng tôi buộc phải tìm cách giải quyết tình trạng thiếu hụt tài nguyên tính toán trầm trọng hiện nay.

AMD MI300X được ra mắt vào tháng 12 năm 2023 như một câu trả lời của AMD trước NVIDIA H100. Đây là một sản phẩm khá đặc biệt trong thế giới các bộ tăng tốc AI cao cấp. Trong khi giá của H100 không ngừng tăng và nguồn cung khan hiếm, MI300X lại bị đánh giá thấp hơn dù sở hữu 192GB bộ nhớ HBM3 trên mỗi card (so với 80GB của H100), hiệu năng tính toán FP8 tương đương và giá niêm yết thấp hơn khoảng một nửa. Thực tế, bạn có thể thuê một chiếc MI300X ngay lập tức với mức giá thấp hơn đáng kể so với công suất tương đương của NVIDIA.

Tuy nhiên, lý do khiến MI300X chưa được phổ biến rộng rãi hơn nằm ở phần mềm. Các vấn đề khi chạy tác vụ AI trên AMD đã được bàn luận rất nhiều, và có dấu hiệu cho thấy khoảng cách phần mềm đang được thu hẹp trên các chip mới hơn của AMD. Nhưng sự tập trung vào phần mềm này chưa lan ngược sang các dòng chip cũ. Tính đến đầu tháng 5 năm 2026, việc chạy vLLM với DeepSeek-V4-Flash trên MI300X đơn giản là không hoạt động. Trên giấy tờ, MI300X là một bộ tăng tốc tuyệt vời và chúng tôi rất muốn nó hoạt động. Bài viết này là nhật ký công việc về những khó khăn và con đường vòng vo mà chúng tôi đã tìm thấy khi cố gắng đưa nó vào hoạt động.

Vấn đề về phương ngữ FP8

MI300X thuộc thế hệ bộ tăng tốc bắt đầu xu hướng chuyển sang các độ rộng bit thấp hơn. Các trọng số LLM, và ở mức độ thấp hơn là các activation và bộ nhớ đệm KV, ít nhạy cảm hơn với sự không chính xác về số học so với các tác vụ HPC điển hình. Kết quả là chúng ta có thể áp dụng gấp đôi số lượng FLOPs cho các khối lượng công việc tương ứng với chỉ một nửa lượng dữ liệu cần truyền tải.

Vấn đề nảy sinh từ sự bất đồng về cách tốt nhất để xây dựng kiểu dữ liệu FP8. Graphcore và AMD đã đề xuất một tiêu chuẩn, trong khi Arm, Intel và NVIDIA lại đề xuất một tiêu chuẩn khác thông qua Dự án Máy tính Mở (Open Compute Project - OCP). Các nhà cung cấp khác nhau đã xây dựng các hành vi khác nhau và không tương thích.

Có lẽ không ngạc nhiên khi tiêu chuẩn của AMD/Graphcore không được chọn. Các chip MI325, MI350 và MI355X mới hơn của AMD đều đã chuyển sang sử dụng FP8 chuẩn OCP. Tuy nhiên, MI300X vẫn chỉ hoạt động ở phương ngữ fnuz (finite, nans, unsigned zero - tức là không có -0 và không có inf). Mặc dù đây là những điều hợp lý để cắt giảm cho các tác vụ AI, nhưng phương ngữ này không bao giờ thực sự phổ biến và các thế hệ AMD sau đó đã quay lại kiểu FP8 trông "bình thường" hơn.

Do đó, các công việc ban đầu trên vLLM để triển khai DeepSeek trên AMD thực sự không hoạt động đối với MI300X. Nhiều đường dẫn FP8 của vLLM nhận biết về e4m3 so với e5m2 nhưng không phân biệt được fnuz so với OCP. Hai phương ngữ này chia sẻ cách bố trí bit nhưng khác nhau về độ lệch số mũ (exponent bias) một đơn vị, vì vậy cùng một byte được đọc dưới dạng phương ngữ sai sẽ trả về kết quả sai lệch đúng gấp hai lần. MI300X là bộ tăng tốc chính duy nhất mà sự phân biệt này thực sự quan trọng trong thực tế.

Thiếu các đường dẫn tối ưu hóa cho Attention

Cơ chế chú ý (attention) của DeepSeek v4 là thưa (sparse). Mỗi truy vấn chỉ chú ý đến một tập hợp con top-k của bộ nhớ đệm KV được chọn bởi một bộ chỉ mục đã học, với ngữ cảnh cửa sổ trượt (sliding-window) được xử lý riêng biệt.

Nó có nhiều thành phần di chuyển: nén KV, bộ chỉ mục, đường dẫn cửa sổ trượt, và bộ nhớ đệm FP8 cung cấp cho từng phần. Trong một triển khai sản xuất để có hiệu suất tối đa, mỗi phần cần sự chú ý đặc biệt dưới dạng một kernel đã được tinh chỉnh.

Nguồn cung cấp các kernel tinh chỉnh trên AMD là AITER. AITER là thư viện kernel tinh chỉnh của AMD, tương đương với những gì người dùng NVIDIA nhận được từ sự kết hợp giữa cuBLAS, cuDNN, FlashAttention và Transformer Engine. vLLM sẽ quay lại sử dụng Triton chung khi AITER không có đường dẫn cho một hình dạng nhất định, và attention Triton chung chậm hơn nhiều lần so với một kernel tinh chỉnh. Khả năng hỗ trợ của AITER cho DSV4 không đồng đều và những gì tồn tại thường nhắm đến các phần AMD sau này (CDNA4) thay vì các lõi CDNA3 (gfx942) trong MI300X.

Hậu quả của việc này có hai hình dạng. Một số thành phần hoàn toàn thiếu đường dẫn AITER trên gfx942: logits paged MQA, sparse MLA prefill, sparse MLA decode. Đối với mỗi cái, chúng tôi cần đưa vào một trình trợ giúp cụ thể của ROCm gọi vào AITER nơi nó tồn tại và chuyển sang triển khai Triton nơi không có. Một số thành phần có đường dẫn AITER nhưng lại bị lỗi cụ thể trên gfx942. Giải pháp là từ chối dispatch vào chúng khi current_platform báo cáo gfx942 và để phần dự phòng Triton xử lý cuộc gọi thay thế.

HIP Graphs

HIP Graphs là bản tương đương của AMD với CUDA Graphs, về cơ bản có cùng ngữ nghĩa: ghi lại luồng thao tác một lần tại thời điểm khởi động (warmup) và phát lại đồ thị đã ghi lại ở mọi bước tiếp theo. Lợi ích là loại bỏ chi phí Python cho mỗi lần khởi chạy, điều rất quan trọng khi bạn khởi chạy hàng trăm kernel nhỏ cho mỗi token. Vì DeepSeek v4 có nhiều thành phần di chuyển, sẽ có rất nhiều lần khởi chạy kernel nếu chúng tôi không tận dụng đồ thị.

Giá phải trả là vùng đã ghi lại phải là một hàm thuần túy của các đầu vào thiết bị của nó. Bất cứ thứ gì đọc từ máy chủ (host), cấp phát một tensor có hình dạng phụ thuộc vào lô (batch) trực tiếp, hoặc đồng bộ hóa bên trong vùng đã ghi lại sẽ được ghi lại với bất kỳ giá trị nào nó có tại thời điểm khởi động và phát lại mãi mãi sau đó.

Các kernel tinh chỉnh AITER được cấu tạo để hoạt động với điều này. Các kernel AITER là các lần khởi chạy C++ nhận các con trỏ thiết bị và kích thước; chúng không cấp phát bộ nhớ tạm (scratch) lởm chởm từ Python và chúng không đọc các vô hướng máy chủ giữa luồng. Viết một kernel Triton hoạt động tốt với điều này khá khó khăn, và chúng tôi đã làm sai điều đó một vài lần. Chúng tôi đã phải xây dựng lại siêu dữ liệu giải mã sparse MLA dưới dạng các tensor tĩnh, an toàn để ghi lại: không có cấp phát động lởm chởm, không có ghi vô hướng từ máy chủ sang thiết bị dưới chế độ ghi.

Các vấn đề nhỏ lẻ

Chúng tôi cũng gặp phải một loạt các vấn đề nhỏ hơn:

  • Một lỗi định tuyến MoE trong đó hình dạng mặt nạ chuyên gia (expert-mask) bị phụ thuộc vào việc ROCm AITER có được bật toàn cầu hay không, chứ không phải phụ thuộc vào phép nhân ma trận sắp được gọi có thực sự là của AITER hay không. Với AITER bật toàn cầu nhưng MXFP4 chuyển sang phần mềm giả lập (emulation backend), kernel đã nhận được mặt nạ sai và các token bị định tuyến đến các chuyên gia sai.
  • Một kernel Triton che các lane đệm (padded lanes) dựa trên giới hạn tensor toàn cầu thay vì kích thước khối logic. Tại mức độ đồng thời cao, các lane đệm này đã ghi đè lên bitmatrix định tuyến MoE.

Tinh chỉnh hiệu năng

Khi đã giải quyết được vấn đề tính đúng đắn (correctness), chúng tôi có thể thực hiện một số tối ưu hóa cơ bản.

Hồ sơ (profile) đầu tiên của một phiên bản DSV4-Flash hoạt động trên MI300X cho thấy các lớp tốn kém nhất là đường dẫn MLA thưa và đường dẫn MoE MXFP4. Đây là tin tốt — nếu không phải vậy, chúng tôi sẽ gặp rắc rối lớn.

Tuy nhiên, sau lần khởi động đầu tiên, một phần thời gian đáng kể không nằm trong các phép nhân ma trận (matmuls) mà nằm trong công việc quản lý và tinh chỉnh xung quanh chúng. Giải mã MLA thưa xây dựng lại siêu dữ liệu lởm chởm ở mỗi bước. Kernel giải mã ghi vào một tensor tạm và sau đó sao chép vào bộ đệm đầu ra của người gọi. Trọng số chiếu bf16 được tạo ra (materialise) ở mỗi bước giải mã thay vì được lưu trong bộ nhớ đệm. Một hình dạng khởi chạy Triton tĩnh bao gồm cả giai đoạn tăng lô nhỏ và phục vụ bão hòa. Hình dạng ô (tile shape) OGS MXFP4 tương tự là một lựa chọn tĩnh duy nhất trên các chế độ không giống nhau chút nào.

Trên điểm chuẩn đơn giản của chúng tôi, điều này đưa hộp từ 2485 lên 2699 tok/s đầu ra trên mỗi GPU, khoảng +8,6%.

Có đáng giá không?

Sau khi triển khai mô hình, tối ưu hóa và kiểm tra, chúng tôi có được những con số khá tốt:

Đây là một chiến thắng: MI300X có giá thuê thấp hơn khoảng một nửa so với công suất NVIDIA mà nó cạnh tranh, mang lại nhiều hơn gấp đôi bộ nhớ HBM trên mỗi card và có sẵn để thuê theo yêu cầu ngay bây giờ, ngay cả khi thời gian giao hàng của H100 và H200 bị kéo dài. Chúng tôi chưa thực hiện các phép tính để chứng minh rằng chúng ta có thể chiến thắng về số token trên giây trên mỗi đô la so với phần cứng NVIDIA, nhưng chúng tôi đã chứng minh rằng với sự làm việc chăm chỉ, chúng ta có thể tiến đủ gần để làm cho nó hữu ích.

Hầu hết những gì khiến nó trở nên khó khăn là tạm thời. Vấn đề phương ngữ FP8 là đặc thù của CDNA3: MI325, MI350 và MI355X đều đã chuyển sang FP8 chuẩn OCP, vì vậy bẫy "sai lệch gấp đôi" không tồn tại trên các phần mới hơn. Các khoảng trống hỗ trợ của AITER sẽ được lấp đầy theo thời gian khi công việc kernel của AMD bắt kịp với phần cứng của chính họ. Và kể từ khi chúng tôi thực hiện công việc này, ngay cả khi chúng tôi chuẩn bị mã nguồn mở, hiệu suất và sự ổn định của vLLM trên mô hình này đã được cải thiện.

Phần cứng của AMD đã tốt trong một thời gian. Lý do khoảng cách phần mềm cuối cùng cũng đang được thu hẹp một phần là do sự tập trung của chính AMD, và một phần là do chi phí thực hiện loại công việc này đã giảm xuống mức thấp nhất với sự trỗi dậy của lập trình tác nhân (agentic coding). Kết quả của cả hai yếu tố này là nếu bạn gửi các yêu cầu DeepSeek-V4-Flash của mình đến API Doubleword, phản hồi có thể được cung cấp bởi AMD.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗