LLaMA3 在 HuggingFace Transformers 与 SGLang 下的实现对比

LLaMA、大预言模型、HuggingFace、SGLang

Posted by Gemini on December 9, 2025

本篇博客由谷歌的Gemini 3 pro生成(结合Antigravity代码工具)

随着 Llama 3 的发布,开源大模型的能力再次被推向了新的高度。对于开发者而言,如何高效地部署和使用 Llama 3 成为了一个关键问题。目前最主流的两种选择分别是 HuggingFace TransformersSGLang

HuggingFace Transformers 是大模型开发的”瑞士军刀”,而 SGLang 则是为高性能推理而生的”F1赛车”。本文将从性能、特性、易用性等多个维度,深度对比这两者在 Llama 3 推理场景下的表现。

设计定位对比

HuggingFace Transformers:

  • 定位:通用的 NLP/LLM 开发框架。
  • 核心目标:兼容性、易用性、研究友好。
  • 适用场景:模型微调(Fine-tuning)、算法研究、Demo 快速搭建、对吞吐量要求不高的离线推理。

SGLang:

  • 定位:高性能 LLM/VLM 推理和服务框架。
  • 核心目标:低延迟、高吞吐、复杂的推理控制流。
  • 适用场景:生产环境部署、高并发 API 服务、需要极致性能的 Agent 系统、结构化输出生成。

关键特性对比

特性 HuggingFace Transformers SGLang
推理后端 PyTorch 原生,支持简单的 KV Cache 高度优化的后端,集成 FlashInfer, RadixAttention
显存管理 相对基础,容易 OOM PagedAttention,显存利用率极高
KV Cache 基础支持 RadixAttention (核心杀手锏):自动复用 KV Cache,大幅加速多轮对话和 Agent 场景
批处理 (Batching) 静态 Batching 或简单的动态 Batching Continuous Batching (连续批处理):显著提高吞吐量
量化支持 Bitsandbytes (INT4/8), AutoGPTQ, AWQ (需额外库) 内置原生支持 (FP8, INT4, AWQ, GPTQ),开箱即用
结构化输出 较弱,需配合 outlines 等库 原生支持正则表达式约束,生成 JSON 等格式速度极快且零额外开销
多卡/分布式 Accelerate (主要是 Pipeline/Tensor Parallel) 高效的 Tensor Parallelism (TP) 和 Data Parallelism (DP),轻松扩展至 H100 集群

在 Llama 3 8B 和 70B 的测试中,SGLang 通常展现出显著的优势:

  1. 吞吐量 (Throughput):得益于 Continuous Batching 和 RadixAttention,SGLang 在高并发下的吞吐量通常是 HuggingFace 的 3-5 倍,甚至更高。
  2. 首字延迟 (TTFT):SGLang 的调度器经过极致优化(零开销 CPU 调度),首字延迟极低。
  3. 复杂交互加速:这是 SGLang 最强的点。如果你的应用是 “Few-shot Prompting” 或者 “Self-Consistency” (思维链),SGLang 可以自动复用前缀的 KV Cache (RadixAttention),使得第二次请求几乎不需要重新计算 Prompt 部分,速度提升可达 5-10 倍

注意:HuggingFace 也在通过集成 torch.compile 和 Flash Attention 2 提升性能,但在生产级 Serving 场景下,依然难以与 SGLang、vLLM 等专用引擎抗衡。

运行代码示例

HuggingFace Transformers (Llama 3 8B)

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

model_id = "meta-llama/Meta-Llama-3-8B-Instruct"

tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    torch_dtype=torch.bfloat16,
    device_map="auto",
)

messages = [
    {"role": "system", "content": "You are a pirate chatbot who always responds in pirate speak!"},
    {"role": "user", "content": "Who are you?"},
]

input_ids = tokenizer.apply_chat_template(
    messages,
    add_generation_prompt=True,
    return_tensors="pt"
).to(model.device)

outputs = model.generate(
    input_ids,
    max_new_tokens=256,
    eos_token_id=tokenizer.eos_token_id,
    do_sample=True,
    temperature=0.6,
    top_p=0.9,
)

response = tokenizer.decode(outputs[0][input_ids.shape[-1]:], skip_special_tokens=True)
print(response)

SGLang (SRT - SGLang Runtime)

SGLang 既可以作为服务启动,也可以像 Python 库一样使用。

命令行启动服务:

python -m sglang.launch_server --model-path meta-llama/Meta-Llama-3-8B-Instruct --port 30000

Python 客户端调用:

import sglang as sgl

@sgl.function
def pirate_chat(s):
    s += sgl.system("You are a pirate chatbot who always responds in pirate speak!")
    s += sgl.user("Who are you?")
    s += sgl.assistant(sgl.gen("answer", max_tokens=256, temperature=0.6))

# 连接到本地服务
state = pirate_chat.run(backend=sgl.RuntimeEndpoint("http://localhost:30000"))
print(state["answer"])

或者直接使用 sglang.Engine 在进程内运行 (类似 vLLM):

import sglang as sgl

engine = sgl.Engine(model_path="meta-llama/Meta-Llama-3-8B-Instruct")

@sgl.function
def task(s):
    s += "Question: What is the capital of France?\nAnswer:"
    s += sgl.gen("answer", stop="\n")

state = engine.run(task)
print(state["answer"])

核心算法改进

为了更直观地理解 SGLang 的性能来源,我们需要深入到算法实现层面。HuggingFace Transformers 更多是 Python 层的 “Look-before-you-leap” 抽象,而 SGLang 是 C++ Kernels 的 “Optimized Execution” 编排。

RadixAttention: KV Cache 的先进优化

在 HuggingFace Transformers 中,KV Cache 通常是请求级别的。如果你有两个请求共享很长的前缀(例如 System Prompt + Few-shot Examples),HF 默认会重复计算这部分的 KV Cache。虽然可以通过手动传递 past_key_values 优化,但工程复杂度极高。

SGLang 引入了 RadixAttention,将 KV Cache 管理为一棵基数树(Radix Tree)。

  • 机制:自动识别并维护 Prompt 的共享前缀。当新的请求进来时,SGLang 会在 Radix Tree 中查找最长匹配前缀,直接复用其 KV Cache,仅计算新增部分。
  • 收益:在多轮对话、Agent 推理链(Chain-of-Thought)等场景下,Prefill 阶段的计算量可减少 60%-90%。

关键代码实现 (SGLang RadixCache 核心逻辑简化):

class RadixCache:
    def __init__(self):
        self.root_node = TreeNode()
        self.evictable_size_ = 0

    def match_prefix(self, token_ids):
        # 核心逻辑:在树中寻找最长匹配前缀

        # 遍历 Radix Tree,匹配 token_ids

        # 如果命中,返回命中的节点和缓存的 KV indices

        node = self.root_node
        for token in token_ids:
            if token in node.children:
                node = node.children[token]
            else:
                break
        return node.cached_kv_indices

    def insert(self, new_token_ids, new_kv_indices):
        # 将新的推理结果插入树中,供下次复用

        # SGLang 会根据 LRU 策略定期驱逐冷数据

        pass

源码路径: sglang/python/sglang/srt/mem_cache/radix_cache.py

多头潜在注意力 (MLA): DeepSeek 系列的高效引擎

针对 DeepSeek-V2/V3 等模型引入的 Multi-Head Latent Attention (MLA) 机制,SGLang 进行了深度优化。相比于传统的 GQA,MLA 通过低秩投影大大压缩了 KV Cache 的显存占用,但也带来了更复杂的计算模式。

  • 机制:SGLang v0.3+ 针对 MLA 实现了包括权重吸收 (Weight Absorption)、以组为单位的解码 Kernel (Grouped Decoding Kernels) 以及 FP8 量化支持。
  • 收益:在 DeepSeek 系列模型上,由于 MLA 的优化,显存占用显著降低,使得单卡可以运行更大参数或更长 Context 的模型,同时保持极高的吞吐量。

关键代码实现 (MLA 权重吸收与投影逻辑):

class MultiHeadLatentAttention(nn.Module):
    def forward(self, q, k, v):
        # MLA 的核心在于将 KV 压缩到低维 Latent Space

        # SGLang 在 Kernel 层面做了极致优化,这里展示逻辑流


        # 1. 权重吸收 (Weight Absorption)

        # 将原始的 W_Q, W_K, W_V 融合,减少计算时的显存搬运

        q_latent = self.q_down_proj(q)
        kv_latent = self.kv_down_proj(k) # KV 联合压缩
        
        # 2. 在 Latent Space 进行 RoPE (旋转位置编码)

        # 避免了在完整 Head 维度上做 RoPE 的巨大开销

        q_rope = apply_rope(q_latent)
        k_rope = apply_rope(kv_latent)
        
        # 3. 投影回原始空间进行 Attention 或直接使用 FlashInfer 的 MLA Kernel

        # SGLang 调用优化的 Custom Op

        output = flashinfer.mla_decode(q_rope, k_rope, ...)
        return output

源码路径: sglang/python/sglang/srt/models/deepseek_v2.py

压缩有限状态机: 零开销结构化输出

对于 JSON 限制生成,HuggingFace 通常需要借助 outlinesguidance 等外挂库,这些库往往通过 Mask Logits 来实现,过程可能相当慢。

SGLang 实现了 Compressed Finite State Machine (FSM)

  • 机制:将正则表达式转化为 FSM,并直接下沉到 CUDA Kernel 级别进行 Token Masking。
  • 收益:相比于 Python 层面的 Logits Masking,几乎实现了零额外开销的结构化生成。

关键代码实现 (Regex 到 FSM 的编译与 Masking):

class RegexLogitsProcessor:
    def __init__(self, regex_string):
        # 1. 离线编译:将正则字符串转换为有限状态机 (FSM)

        self.fsm = regex_to_fsm(regex_string)
        # 2. 压缩:合并线性路径,减少状态跳转开销

        self.compressed_fsm = compress_fsm(self.fsm)

    def __call__(self, input_ids, scores):
        # 3. 运行时:直接在 GPU Kernel 中根据当前状态 Mask 掉非法 Token

        # SGLang 将 FSM 状态表下沉到了 CUDA

        # mask = sgl_kernel.fsm_mask(self.compressed_fsm, current_state)

        # scores += mask

        return scores

源码路径: outlines/outlines/generate/regex.py (SGLang 依赖 Outlines 实现)

核心工程改进

SGLang 不仅在算法层面创新,在系统工程层面也引入了类似 “操作系统” 的调度能力。

零开销批处理调度器 (Zero-Overhead Batch Scheduler)

传统的推理框架在 GPU 计算完当前 Batch 后,CPU 需要花费时间准备下一个 Batch 的元数据,这会导致 GPU 空转(Idle)。

  • 机制:SGLang 引入了类似 NanoFlow 的流水线机制,在 GPU 计算当前 Batch 的同时,CPU 异步准备下一个 Batch 的数据。
  • 收益:消灭了 GPU 的空闲气泡,在高并发场景下,GPU 利用率几乎可以打满 100%。

关键代码实现 (Pipeline 调度逻辑):

def run_scheduler_loop():
    while True:
        # SGLang 的 Loop 不止处理当前,还预取下一步
        
        # 1. 下发当前 Batch 到 GPU 执行 (Async)

        current_batch.execute_on_gpu()
        
        # 2. "Zero-Overhead" 的核心:

        # 在 GPU 忙碌时,CPU 立即开始计算**下一个** Batch 的调度计划

        # (包括 RadixCache 匹配、Token 预处理、显存分配)

        next_batch_plan = scheduler.schedule_next_batch()
        
        # 3. 准备好数据,等待 GPU 完成上一轮,无缝衔接

        # synchronize() # 仅在必要时同步

源码路径: sglang/python/sglang/srt/managers/scheduler.py

缓存感知负载均衡器 (Cache-Aware Load Balancer)

在多实例部署(Data Parallelism)场景下,传统的负载均衡器(如 Round-Robin)是无状态的,可能将需要相同前缀的请求分发到不同实例,导致 RadixAttention 失效。

  • 机制:SGLang 的 Router 能够感知每个 Worker 的 Radix Tree 状态。如果请求 A 需要前缀 P,Router 会将其分发到已经缓存了 P 的 Worker 上。
  • 收益:显著提高了分布式环境下的 KV Cache 命中率,减少了不必要的 Prefill 计算,大幅降低端到端延迟。

关键代码实现 (Router 分发逻辑):

class CacheAwareRouter:
    def dispatch(self, request):
        tree_cache_status = self.get_workers_cache_status()
        
        best_worker = None
        max_overlap = 0
        
        # 遍历所有 Worker,寻找拥有请求中最长共享前缀的那个

        for worker in self.workers:
            # Router 维护了近似的 Radix Tree 状态

            overlap_len = tree_cache_status[worker].match_len(request.prompt_ids)
            
            if overlap_len > max_overlap:
                max_overlap = overlap_len
                best_worker = worker
        
        # 将请求路由到命中率最高的 Worker

        return best_worker.send(request)

源码路径: sglang/sgl-router/py_src/sglang_router/launch_router.py