📖 Bản tiếng Anh (English Edition)

Điều kiện tiên quyết: Nắm vững cơ chế vận hành của AI Agent và bộ nhớ đa tầng trong Phần 7 — Agentic Memory Systems.

Answer-first: Hạ tầng phục vụ mô hình ngôn ngữ lớn thường nghẽn cổ chai tại bộ nhớ GPU do phân mảnh KV Cache khi chịu tải cao. Ứng dụng vLLM với bộ nhớ ảo PagedAttention, RadixAttention tái sử dụng tiền tố và kỹ thuật Speculative Decoding giúp tăng gấp đôi thông lượng và giảm 58% độ trễ P99.


Bài Toán Kinh Tế Khi Phục Vụ LLM: Điểm Nghẽn Băng Thông Bộ Nhớ

Trong kỹ thuật hạ tầng AI doanh nghiệp, chi phí suy luận ($/triệu tokens) chiếm phần lớn ngân sách vận hành. Trong khi quá trình huấn luyện mô hình (training) bị giới hạn bởi năng lực tính toán (Compute-Bound / FLOPS), thì giai đoạn sinh suy luận tự hồi quy (Autoregressive Inference) lại bị thắt cổ chai bởi băng thông bộ nhớ GPU (Memory-Bandwidth Bound) và dung lượng VRAM khả dụng.

Khi sinh từng token kế tiếp, hệ thống buộc phải nạp hàng chục tỷ trọng số mô hình từ bộ nhớ băng thông cao (HBM3e/HBM3) vào bộ nhớ đệm SRAM của chip GPU. Đồng thời, GPU phải duy trì và liên tục cập nhật bộ nhớ đệm Key-Value (KV Cache) cho toàn bộ các token đã sinh ra trước đó trong chuỗi.

+-------------------------------------------------------------------------------+
|                      HAI ĐIỂM NGHẼN KÉP KHI SUY LUẬN LLM                      |
+-------------------------------------------------------------------------------+
| 1. Pha Nạp Ngữ Cảnh - Prefill Phase (Xử Lý Prompt Ban Đầu)                    |
|    - Bản chất: Giới hạn bởi tính toán (Compute-bound - nhân ma trận lớn)     |
|    - Mục tiêu: Tối đa hóa hiệu suất Tensor Cores để xử lý song song prompt     |
+-------------------------------------------------------------------------------+
| 2. Pha Sinh Tự Hồi Quy - Decode Phase (Tạo Token Từng Bước)                   |
|    - Bản chất: Giới hạn bởi băng thông bộ nhớ (Memory-bandwidth bound)        |
|    - Mục tiêu: Tối đa hóa kích thước batch để san sẻ chi phí nạp trọng số     |
|    - Điểm nghẽn: Dung lượng VRAM của KV Cache giới hạn số lượng batch đồng thời|
+-------------------------------------------------------------------------------+

Nếu máy chủ doanh nghiệp không thể xếp vừa các chuỗi yêu cầu đồng thời vào VRAM, kích thước batch xử lý sẽ sụt giảm nghiêm trọng. Điều này khiến hiệu suất sử dụng GPU rớt xuống dưới 15%, để lãng phí các cỗ máy đắt đỏ như H100 hay H200 trong trạng thái chờ nạp dữ liệu từ bộ nhớ.


Phân Tích Cơ Cấu Bộ Nhớ Của KV Cache

Đối với một mô hình Transformer tự hồi quy gồm $L$ tầng, chiều ẩn $d$, $H_{kv}$ đầu chú ý (attention heads) cho key-value, mỗi đầu có kích thước $d_k$, tổng dung lượng VRAM mà KV Cache tiêu thụ cho một chuỗi có độ dài $S$ token ở độ chính xác 16-bit (2 bytes/phần tử) được tính theo công thức:

$$\text{Memory}{KV}(S) = 2 \times 2 \times L \times H{kv} \times d_k \times S = 4 \times L \times H_{kv} \times d_k \times S \text{ bytes}$$

Xét mô hình Llama-3-70B áp dụng kiến trúc Grouped-Query Attention (GQA: $L=80$, $H_{kv}=8$, $d_k=128$):

  • Mỗi token tiêu tốn: $4 \times 80 \times 8 \times 128 = 327.680 \text{ bytes} \approx 320 \text{ KB}$.
  • Một phiên làm việc với ngữ cảnh 8.192 tokens đòi hỏi: $320 \text{ KB} \times 8.192 \approx 2.56 \text{ GB}$ VRAM chuyên dụng.
  • Khi phục vụ 64 yêu cầu đồng thời, chỉ riêng KV Cache đã ngốn tới $163.8 \text{ GB}$ VRAM—vượt xa dung lượng 80GB của một GPU H100 ngay cả khi chưa nạp 140GB trọng số mô hình!
graph LR
    subgraph Traditional_KV_Allocation ["Cấp Phát Tĩnh Truyền Thống"]
        A1["Cấp Phát Tĩnh Khối Liền Kề (Tối Đa 8.192 Tokens)"]
        B1["Token Thực Tế Đang Dùng (1..320) [Chiếm Dụng]"]
        C1["Phân Mảnh Nội Bộ (321..8.192) [BỊ KHÓA & LÃNG PHÍ]"]
        A1 --> B1
        A1 --> C1
    end

    subgraph PagedAttention_Allocation ["Cơ Chế Phân Trang Bộ Nhớ Ảo PagedAttention"]
        Table["Bảng Phân Trang Khối Ảo (Block Table)"]
        P1["Trang Vật Lý #42 (16 Tokens) [Đang Dùng]"]
        P2["Trang Vật Lý #108 (16 Tokens) [Đang Dùng]"]
        P3["Trang Vật Lý #19 (16 Tokens) [Đang Dùng]"]
        PFree["Kho Trang Tự Do Chung [Cấp Phát Cho Bất Kỳ Phiên Nào]"]
        
        Table -->|Trang Ảo 0| P1
        Table -->|Trang Ảo 1| P2
        Table -->|Trang Ảo 2| P3
        PFree -.->|Cấp Phát Theo Nhu Cầu| Table
    end

Tại Sao Các Framework Cũ Lãng Phí 60% Đến 80% VRAM?

  1. Lãng Phí Cấp Phát Trước (Pre-Allocation Waste): Các framework cũ như HuggingFace Transformers mặc định cấp phát vùng nhớ liền kề cho toàn bộ kích thước ngữ cảnh tối đa (ví dụ 8k token) ngay khi yêu cầu vừa gửi tới.
  2. Phân Mảnh Nội Bộ (Internal Fragmentation): Nếu câu trả lời của người dùng hoàn tất chỉ sau 250 tokens, 7.942 slot token còn lại vẫn bị khóa chặt và không thể chia sẻ cho các yêu cầu khác.
  3. Phân Mảnh Bên Ngoài (External Fragmentation): Vòng đời kết thúc ngẫu nhiên của các phiên làm việc tạo ra các lỗ hổng bộ nhớ nằm rải rác, gây lỗi CUDA Out-Of-Memory (OOM) dù tổng dung lượng VRAM trống trên danh nghĩa vẫn còn hàng chục GB.

PagedAttention: Ảo Hóa Phân Trang Bộ Nhớ Cho Key và Value

Lấy cảm hứng từ cơ chế phân trang bộ nhớ ảo trong hệ điều hành (Virtual Memory Paging), PagedAttention của vLLM xóa bỏ hoàn toàn yêu cầu bộ nhớ liền kề. Thay vì lưu trữ vector Key và Value trên một dải địa chỉ liên tục trên GPU, PagedAttention chia nhỏ KV Cache thành các khối vật lý có kích thước cố định (thường là 16 hoặc 32 tokens mỗi block).

Thực Thi Kernel Toán Học Trong CUDA

Trong cơ chế Multi-Head Attention tiêu chuẩn, điểm chú ý của vector truy vấn $\mathbf{q}_i$ trên toàn bộ ma trận key liên tục $\mathbf{K}$ được tính bằng:

$$\mathbf{A}_i = \text{Softmax}\left(\frac{\mathbf{q}_i \mathbf{K}^T}{\sqrt{d_k}}\right)$$

Dưới PagedAttention, các key và value được phân tán trên các khối vật lý không liền kề $\mathcal{B} = {B_1, B_2, \dots, B_m}$, trong đó khối $B_j$ chứa các vector key $\mathbf{K}{(j)} = [\mathbf{k}{(j, 1)}, \dots, \mathbf{k}{(j, B{size})}]$. Kernel CUDA của PagedAttention tính toán điểm chú ý từng khối một thông qua thuật toán Online Softmax:

$$\mathbf{A}_{i, j} = \frac{\mathbf{q}i \mathbf{K}{(j)}^T}{\sqrt{d_k}}$$

$$\mathbf{o}i = \sum{j=1}^m \sum_{t=1}^{B_{size}} a_{i, j, t} \mathbf{v}_{(j, t)}$$

Nhờ vào việc ánh xạ qua Bảng Phân Trang (Block Table), các khối vật lý có thể nằm ở bất kỳ đâu trên bộ nhớ GPU HBM. Các khối chưa dùng được gom vào một kho chung (Shared Free Pool), loại bỏ hoàn toàn phân mảnh bộ nhớ bên ngoài và giảm tỷ lệ lãng phí VRAM xuống dưới 4%.


RadixAttention: Tái Sử Dụng Tiền Tố (Prefix Caching) Cho Đa Người Dùng

Trong các bài toán doanh nghiệp, các phiên tương tác liên tiếp thường xuyên chia sẻ các đoạn tiền tố token giống hệt nhau:

  • System prompt và định nghĩa vai trò doanh nghiệp (500–2.000 tokens).
  • Các ví dụ Few-Shot và đặc tả JSON Schema (1.000–4.000 tokens).
  • Tài liệu quy trình trong bài toán Agentic RAG (4.000–16.000 tokens).

Các engine truyền thống phải tính toán lại toàn bộ KV Cache cho phần tiền tố này ở mỗi lượt gọi, làm lãng phí năng lực tính toán của GPU và tăng vọt độ trễ phản hồi token đầu tiên (Time-To-First-Token - TTFT).

RadixAttention tổ chức các khối KV Cache theo cấu trúc cây tiền tố Radix (Radix Tree). Khi một yêu cầu mới gửi tới, vLLM thực hiện so khớp tiền tố:

  1. Nếu chuỗi token trùng với các nút đã có trong cây, vLLM tái sử dụng trực tiếp các trang bộ nhớ vật lý trên GPU bằng cách tăng bộ đếm tham chiếu (ref_count++).
  2. Giai đoạn nạp ngữ cảnh (Prefill Phase) cho phần tiền tố trùng khớp được bỏ qua hoàn toàn, giúp giảm tới 85% độ trễ TTFT.
  3. Khi bộ nhớ GPU chịu áp lực tải cao, thuật toán dọn dẹp LRU (Least Recently Used) sẽ giải phóng các khối có ref_count == 0.

Speculative Decoding: Phá Vỡ Rào Cản Tốc Độ Tự Hồi Quy

Trong khi PagedAttention giải quyết triệt để bài toán dung lượng bộ nhớ, độ trễ sinh từ lại bị giới hạn bởi tính tuần tự của thuật toán tự hồi quy: để sinh $N$ tokens cần thực hiện tuần tự $N$ lượt lan truyền tiến (Forward Passes).

Speculative Decoding (Giải Mã Phỏng Đoán) giải quyết vấn đề này bằng cách kết hợp một Mô Hình Nháp Siêu Tốc (Draft Model) (ví dụ: Llama-3.2-1B) với một Mô Hình Đích Dung Lượng Lớn (Target Model) (ví dụ: Llama-3.1-70B).

sequenceDiagram
    autonumber
    actor Client as "Ứng Dụng Client"
    participant Engine as "Động Cơ Suy Luận vLLM"
    participant Draft as "Mô Hình Nháp (1B / Eagle)"
    participant Target as "Mô Hình Đích (70B Chính)"

    Client->>Engine: Gửi Yêu Cầu Prompt (Ngữ cảnh: C)
    loop Vòng Lặp Xác Thực Phỏng Đoán Song Song
        Engine->>Draft: Dự đoán K token ứng viên tự hồi quy (K=4)
        Draft-->>Engine: Trả về các token phỏng đoán: [y_1, y_2, y_3, y_4] (~14ms)
        Engine->>Target: Chạy 1 lượt forward pass song song qua [C, y_1, y_2, y_3, y_4]
        Note over Target: Đánh giá phân phối xác suất đồng thời P(y_k | Context)
        Target-->>Engine: Chấp nhận [y_1, y_2, y_3], Từ chối [y_4], Sinh Token Sửa Đổi [y_4']
    end
    Engine-->>Client: Stream các token đã xác thực (Tăng tốc từ 2.4x - 3.1x)

Thuật Toán Chấp Nhận & Từ Chối Phỏng Đoán (Rejection Sampling)

Để đảm bảo phân phối xác suất của văn bản đầu ra hoàn toàn đồng nhất về mặt toán học với mô hình đích (chất lượng câu trả lời không suy giảm 0%), Speculative Decoding áp dụng giải thuật chọn mẫu hiệu chỉnh:

Đối với token ứng viên $x$ được đề xuất bởi mô hình nháp $M_d$ với xác suất $q(x)$ và mô hình đích $M_t$ với xác suất $p(x)$:

  1. Nếu $p(x) \ge q(x)$, token được chấp nhận.
  2. Nếu $p(x) < q(x)$, token được chấp nhận với xác suất $\frac{p(x)}{q(x)}$.
  3. Nếu bị từ chối, token mới được lấy mẫu lại từ phân phối thặng dư hiệu chỉnh: $$P_{resample}(x) = \frac{\max(0, p(x) - q(x))}{\sum_{x’} \max(0, p(x’) - q(x’))}$$ và các token phỏng đoán phía sau $[x_{k+1}, \dots]$ sẽ bị hủy bỏ.

Trong các bài toán viết code và Agentic RAG thực tế, tỷ lệ chấp nhận token nháp thường đạt từ 70% đến 85%, giúp tăng tốc độ sinh từ thực tế từ 2.5x đến 3.2x mà không cần sửa đổi bất kỳ trọng số nào của mô hình đích.


Triển Khai Python 3.12+ Async vLLM Serving Engine Chuẩn Doanh Nghiệp

Mã nguồn Python 3.12+ dưới đây triển khai engine phục vụ chuẩn sản xuất bằng AsyncLLMEngine, hỗ trợ tiền tố RadixAttention, Speculative Decoding và thu thập số liệu viễn trắc TTFT/TPOT:

"""
Động Cơ Phục Vụ vLLM Thông Lượng Cao Cho Doanh Nghiệp (AsyncLLMEngine).
Yêu cầu: Python 3.12+, vllm >= 0.6.0, torch >= 2.4.0
"""

import asyncio
import time
from typing import AsyncGenerator, Dict, List, Optional
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.engine.async_llm_engine import AsyncLLMEngine
from vllm.sampling_params import SamplingParams
from vllm.utils import random_uuid


class EnterpriseVLLMEngine:
    """
    Trình bọc phục vụ vLLM chuẩn sản xuất tích hợp:
    - Cơ chế quản lý bộ nhớ ảo PagedAttention.
    - Bộ nhớ đệm tiền tố tự động RadixAttention.
    - Tăng tốc bằng mô hình nháp Speculative Decoding.
    - Thu thập số liệu viễn trắc TTFT, TPOT và thông lượng.
    """

    def __init__(
        self,
        model_path: str = "meta-llama/Meta-Llama-3.1-70B-Instruct",
        speculative_draft_model: Optional[str] = "meta-llama/Llama-3.2-1B-Instruct",
        gpu_memory_utilization: float = 0.92,
        tensor_parallel_size: int = 4,
    ):
        print(f"[vLLM Khởi Tạo] Nạp AsyncLLMEngine: {model_path}")
        print(f"[vLLM Cấu Hình] Tensor Parallel: {tensor_parallel_size} | VRAM Tỷ Lệ: {gpu_memory_utilization}")

        self.engine_args = AsyncEngineArgs(
            model=model_path,
            speculative_model=speculative_draft_model,
            num_speculative_tokens=4 if speculative_draft_model else None,
            speculative_draft_tensor_parallel_size=1 if speculative_draft_model else None,
            tensor_parallel_size=tensor_parallel_size,
            gpu_memory_utilization=gpu_memory_utilization,
            max_num_seqs=256,              # Giới hạn số yêu cầu đồng thời trong batch liên tục
            max_model_len=16384,            # Ngưỡng trần cửa sổ ngữ cảnh
            enable_prefix_caching=True,     # Kích hoạt RadixAttention
            enforce_eager=False,            # Kích hoạt CUDA Graphs cho decode tốc độ cao
            disable_log_requests=True,
        )

        self.engine = AsyncLLMEngine.from_engine_args(self.engine_args)
        self.total_completed_requests = 0
        self.cumulative_tokens_generated = 0

    async def generate_stream(
        self,
        prompt: str,
        temperature: float = 0.2,
        top_p: float = 0.95,
        max_tokens: int = 1024,
    ) -> AsyncGenerator[Dict[str, Any], None]:
        """
        Stream các đoạn token sinh ra bất đồng bộ và ghi nhận chỉ số viễn trắc.
        """
        request_id = f"req-{random_uuid()}"
        sampling_params = SamplingParams(
            temperature=temperature,
            top_p=top_p,
            max_tokens=max_tokens,
        )

        start_time = time.perf_counter()
        first_token_time: Optional[float] = None
        previous_text_len = 0
        token_count = 0

        results_generator = self.engine.generate(prompt, sampling_params, request_id)

        try:
            async for request_output in results_generator:
                current_text = request_output.outputs[0].text
                new_text = current_text[previous_text_len:]
                previous_text_len = len(current_text)
                token_count = len(request_output.outputs[0].token_ids)

                if first_token_time is None and len(new_text) > 0:
                    first_token_time = time.perf_counter()

                yield {
                    "delta": new_text,
                    "is_finished": request_output.finished,
                    "request_id": request_id,
                }

        finally:
            end_time = time.perf_counter()
            total_duration = end_time - start_time
            ttft_ms = ((first_token_time - start_time) * 1000.0) if first_token_time else 0.0
            
            # Thời gian sinh mỗi token đầu ra (Time Per Output Token - TPOT)
            generation_time = end_time - (first_token_time or start_time)
            tpot_ms = (generation_time / max(1, token_count - 1)) * 1000.0 if token_count > 1 else 0.0
            throughput = token_count / total_duration if total_duration > 0 else 0.0

            self.total_completed_requests += 1
            self.cumulative_tokens_generated += token_count

            print(
                f"[Viễn Trắc vLLM] Yêu cầu: {request_id} | "
                f"TTFT: {ttft_ms:.1f}ms | TPOT: {tpot_ms:.1f}ms | "
                f"Thông lượng: {throughput:.1f} tok/s | Tokens: {token_count}"
            )


# Mô Phỏng Đánh Giá Hiệu Năng
async def run_benchmark():
    server = EnterpriseVLLMEngine(
        model_path="meta-llama/Meta-Llama-3.1-8B-Instruct",
        speculative_draft_model=None,
        gpu_memory_utilization=0.90,
        tensor_parallel_size=1,
    )

    shared_system_prompt = (
        "Bạn là chuyên gia kỹ thuật hạ tầng dữ liệu AI doanh nghiệp chuyên sâu về tính toán hiệu năng cao.\n"
        * 10  # Tạo tiền tố nhân tạo để kiểm thử RadixAttention
    )

    test_queries = [
        "Hãy giải thích cách PagedAttention phân chia bộ nhớ Key-Value thành các khối vật lý không liền kề.",
        "So sánh cơ chế RadixAttention với các giải pháp tiền tố tĩnh truyền thống.",
        "Tiêu chuẩn toán học nào được áp dụng để chấp nhận token trong speculative decoding?",
    ]

    print("\n--- Thực Thi Kiểm Thử Khởi Động & Tiền Tố RadixAttention ---")
    for idx, query in enumerate(test_queries):
        full_prompt = f"{shared_system_prompt}\nCâu hỏi: {query}\nPhản hồi:"
        print(f"\n[Gửi Yêu Cầu Số {idx + 1}]")
        
        async for chunk in server.generate_stream(full_prompt, max_tokens=256):
            if chunk["is_finished"]:
                print(f"-> Hoàn tất câu hỏi {idx + 1}.")


if __name__ == "__main__":
    print("Mã nguồn Enterprise vLLM Serving Engine đã sẵn sàng.")
    # Chạy trên máy chủ GPU thực tế: asyncio.run(run_benchmark())

Triển Khai Go 1.25+ Reverse Proxy & Cân Bằng Tải Cho Cụm vLLM

Trong cụm máy chủ nhiều GPU, các truy vấn cần được phân phối thông minh tới các bản sao vLLM. Bộ cân bằng tải viết bằng Go 1.25+ dưới đây theo dõi tải kết nối và định tuyến yêu cầu tới bản sao tối ưu nhất:

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sync"
	"sync/atomic"
	"time"
)

type BackendReplica struct {
	URL         *url.URL
	ActiveReqs  int64
	Proxy       *httputil.ReverseProxy
	IsHealthy   bool
	LastCheck   time.Time
	HealthMutex sync.RWMutex
}

type VLLMLoadBalancer struct {
	backends []*BackendReplica
}

func NewVLLMLoadBalancer(backendURLs []string) (*VLLMLoadBalancer, error) {
	var backends []*BackendReplica
	for _, rawURL := range backendURLs {
		parsed, err := url.Parse(rawURL)
		if err != nil {
			return nil, fmt.Errorf("địa chỉ backend không hợp lệ %s: %w", rawURL, err)
		}

		backend := &BackendReplica{
			URL:       parsed,
			Proxy:     httputil.NewSingleHostReverseProxy(parsed),
			IsHealthy: true,
			LastCheck: time.Now(),
		}
		backends = append(backends, backend)
	}

	lb := &VLLMLoadBalancer{backends: backends}
	go lb.startHealthChecker(5 * time.Second)
	return lb, nil
}

func (lb *VLLMLoadBalancer) startHealthChecker(interval time.Duration) {
	ticker := time.NewTicker(interval)
	for range ticker.C {
		for _, b := range lb.backends {
			go func(backend *BackendReplica) {
				healthURL := fmt.Sprintf("%s/health", backend.URL.String())
				client := http.Client{Timeout: 2 * time.Second}
				resp, err := client.Get(healthURL)
				
				backend.HealthMutex.Lock()
				defer backend.HealthMutex.Unlock()
				
				if err != nil || resp.StatusCode != http.StatusOK {
					backend.IsHealthy = false
				} else {
					backend.IsHealthy = true
				}
				backend.LastCheck = time.Now()
			}(b)
		}
	}
}

// SelectBestBackend áp dụng thuật toán Least-Connections trên các node đang khỏe mạnh.
func (lb *VLLMLoadBalancer) SelectBestBackend() (*BackendReplica, error) {
	var best *BackendReplica
	minActive := int64(1<<62 - 1)

	for _, b := range lb.backends {
		b.HealthMutex.RLock()
		healthy := b.IsHealthy
		b.HealthMutex.RUnlock()

		if !healthy {
			continue
		}

		active := atomic.LoadInt64(&b.ActiveReqs)
		if active < minActive {
			minActive = active
			best = b
		}
	}

	if best == nil {
		return nil, fmt.Errorf("không có bản sao backend vLLM nào khả dụng")
	}
	return best, nil
}

func (lb *VLLMLoadBalancer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	backend, err := lb.SelectBestBackend()
	if err != nil {
		http.Error(w, err.Error(), http.StatusServiceUnavailable)
		return
	}

	atomic.AddInt64(&backend.ActiveReqs, 1)
	defer atomic.AddInt64(&backend.ActiveReqs, -1)

	backend.Proxy.ServeHTTP(w, r)
}

func main() {
	backendNodes := []string{
		"http://10.0.1.10:8000",
		"http://10.0.1.11:8000",
	}

	lb, err := NewVLLMLoadBalancer(backendNodes)
	if err != nil {
		log.Fatalf("Khởi tạo Load Balancer thất bại: %v", err)
	}

	server := &http.Server{
		Addr:         ":8080",
		Handler:      lb,
		ReadTimeout:  120 * time.Second,
		WriteTimeout: 120 * time.Second,
	}

	log.Printf("vLLM Load Balancer Router đang lắng nghe trên cổng :8080 xuyên suốt %d node", len(backendNodes))
	// Chạy thực tế trong container: log.Fatal(server.ListenAndServe())
}

Ma Trận So Sánh Các Động Cơ Phục Vụ LLM

Tiêu Chí So SánhNaive Transformers (HF)HuggingFace TGITensorRT-LLM (NVIDIA)vLLM Engine (2027 SOTA)
Cơ Chế Cấp Phát KV CacheBộ đệm liền kề tĩnhPhân trang khối cơ bảnPhân trang khối theo blockBộ nhớ ảo PagedAttention
Lãng Phí Phân Mảnh VRAM60% - 80%15% - 25%< 5%< 4% lãng phí
Số Lượng Phiên (GPU H100)8 - 16 streams64 - 128 streams128 - 256 streams256 - 512 streams đồng thời
Bộ Nhớ Đệm Tiền Tố (Prefix)Không hỗ trợKhớp chuỗi chính xácTiền tố tĩnh cố địnhRadixAttention (Cây Radix Trie Động)
Speculative DecodingKhông hỗ trợMô hình nháp đơn giảnHỗ trợ Medusa & LookaheadĐa token Draft, Eagle, Medusa
Hỗ Trợ Định Lượng (Quant)FP16 / BF16AWQ, GPTQFP8, FP4, INT4-AWQFP8, FP4, AWQ, Marlin, GPTQ
Khả Năng Đổi Mô Hình ĐộngRất chậmTrung bìnhYêu cầu biên dịch lạiCao (Thay thế Adapter LoRA tức thì)

Đánh Đổi Khi Định Lượng (Quantization): FP8 / FP4 So Với AWQ / GPTQ

Khi mở rộng quy mô phục vụ, kỹ thuật định lượng (quantization) là bắt buộc để nạp vừa các mô hình lớn vào VRAM và nhân đôi tốc độ xử lý:

+-------------------------------------------------------------------------------+
|                       PHÂN LOẠI ĐỊNH LƯỢNG MÔ HÌNH HIỆN ĐẠI                   |
+-------------------------------------------------------------------------------+
| 1. Định Lượng Chỉ Trọng Số - Weight-Only (AWQ / GPTQ / Marlin - INT4/INT8)     |
|    - Cơ chế: Nén trọng số thành số nguyên 4-bit; giải nén thành FP16 trong SRAM |
|    - Lợi thế: Giảm 70% dung lượng lưu trữ mô hình; tối ưu cho batch nhỏ        |
|    - Điểm nghẽn: Phép tính số học vẫn phải thực thi trên Tensor Cores FP16    |
+-------------------------------------------------------------------------------+
| 2. Định Lượng Cả Trọng Số & Kích Hoạt - FP8 / FP4 (Hopper H100 / Blackwell)   |
|    - Cơ chế: Cả trọng số và activation tensor chạy trực tiếp trên phần cứng   |
|    - Lợi thế: Nhân đôi FLOPS phần cứng, cắt giảm 50% dung lượng KV Cache VRAM  |
|    - Yêu cầu: Cần hiệu chuẩn ma trận tỉ mỉ để tránh tràn số học với outliers  |
+-------------------------------------------------------------------------------+

Trên các cụm máy chủ Hopper (H100/H200) và Blackwell (B200) hiện đại, định lượng chuẩn FP8 (E4M3 / E5M2) là lựa chọn sản xuất hàng đầu, đem lại tốc độ thông lượng cao gấp 2.2 lần so với FP16 mà hầu như không làm suy giảm độ chính xác (< 0.1% perplexity).


Các Bất Biến Vận Hành Trong Môi Trường Sản Xuất

+-------------------------------------------------------------------------------+
|                      DANH MỤC BẤT BIẾN VẬN HÀNH PHỤC VỤ LLM                   |
+-------------------------------------------------------------------------------+
| [1] Ngưỡng VRAM Cấp Phát: Thiết lập gpu_memory_utilization từ 0.90 đến 0.94.  |
| [2] Kích Hoạt Radix Caching: Bắt buộc enable_prefix_caching=True cho đa phiên.|
| [3] Đồng Nhất Phỏng Đoán: Mô hình đích phải xác thực logits cùng nhiệt độ.    |
| [4] Trục Xuất Khi Quá Tải: Đẩy các khối KV Cache ưu tiên thấp sang RAM host. |
| [5] Giám Sát Viễn Trắc Liên Tục: Theo dõi chặt chẽ TTFT, TPOT và tải VRAM.    |
| [6] Tăng Tốc CUDA Graph: Bắt buộc cudagraph capture cho pha Decode tự hồi quy.|
+-------------------------------------------------------------------------------+

Câu Hỏi Thường Gặp (FAQ)

Làm thế nào PagedAttention loại bỏ hiện tượng phân mảnh bộ nhớ KV Cache trong vLLM?

PagedAttention phân chia việc cấp phát bộ nhớ KV Cache thành các khối vật lý có kích thước cố định (ví dụ 16 tokens mỗi khối) lưu trữ trên các trang VRAM không liền kề. Bảng phân trang ảo theo dõi các khối vật lý thuộc về từng chuỗi yêu cầu. Bộ nhớ được cấp phát theo nhu cầu thực tế từng token, loại bỏ sự lãng phí cấp phát trước và giảm tỷ lệ phân mảnh xuống dưới 4%.

Speculative Decoding là gì và nó giúp giảm độ trễ sinh từ như thế nào?

Speculative Decoding kết hợp một mô hình nháp nhỏ, tốc độ cao với một mô hình đích dung lượng lớn. Mô hình nháp dự đoán song song nhiều token ứng viên. Mô hình đích sau đó xác thực toàn bộ các ứng viên này chỉ trong một lượt forward pass duy nhất, chấp nhận các token đúng và sửa đổi token sai. Kỹ thuật này giúp tăng tốc từ 2x đến 3x mà không làm giảm độ chính xác toán học của mô hình.

RadixAttention tiền tố tối ưu hóa hệ thống phục vụ đa người dùng doanh nghiệp như thế nào?

RadixAttention duy trì một cây Radix chứa các khối KV Cache tương ứng với các token đã được xử lý trước đó. Khi nhiều người dùng chia sẻ cùng system prompt, ví dụ mẫu hoặc tài liệu ngữ cảnh, vLLM bỏ qua hoàn toàn việc tính toán lại, liên kết ngay các trang bộ nhớ vật lý đã có và giảm độ trễ phản hồi token đầu tiên (TTFT) tới 80%.

Sự khác biệt và đánh đổi kiến trúc giữa định lượng FP8/FP4 phần cứng và định lượng chỉ trọng số AWQ/GPTQ là gì?

AWQ và GPTQ là các phương pháp định lượng chỉ nén trọng số: trọng số được lưu ở dạng 4-bit và giải nén thành FP16 khi tính toán, giúp tiết kiệm băng thông bộ nhớ nhưng các phép tính vẫn chạy trên nhân FP16. Ngược lại, định lượng trực tiếp FP8/FP4 (trên GPU Hopper H100 và Blackwell B200) định lượng cả trọng số và các tensor kích hoạt, kích hoạt các nhân Tensor Core FP8 chuyên dụng. Điều này nhân đôi năng lực tính toán và giảm một nửa dung lượng VRAM của KV Cache.

Các Bước Tiếp Theo Trong Chuỗi Bài Viết