一、背景
Deepseek-r1模型的爆火标志着本地部署大模型的需求日益增长。本文主要探讨如何优化本地部署大模型的性能,并结合我们的实践进行评测分析,文章最后我们将分享如何在本地高效部署满血版Deepseek-r1大模型。
在生产环境中,我们已部署专用的大模型推理集群,并对其性能进行了全面优化。对于大模型推理来说,性能优化主要聚焦于两个关键指标:吞吐量与响应时间(RT)。
1.吞吐量
传统上,我们用每秒请求数(QPS)来衡量吞吐量,即系统每秒能够处理多少请求。但对于大模型而言,还有一个重要指标——每秒Token数(token/s),它反映了系统每秒能处理的输入或输出Token数量。
2.响应时间(RT)
这是指系统处理每个请求所需的时间。对于支持流式输出的大模型,还需要关注另外一个指标——首个Token到达时间(TTFT: Time To First Token),即从开始处理请求到输出第一个Token所需的时间
接下来文章将介绍部署高性能大模型推理服务的方法与思路,这些方法主要围绕提升吞吐量与响应时间这两个关键指标展开。
二、高性能、易扩展的大模型推理框架是什么样的
尽管业界已有许多经典的大模型推理框架,但在深入了解这些框架之前,我们不妨先思考一下,如何设计一个既高性能又易于扩展的大模型推理框架。
大模型推理框架需要满足的基本条件
性能足够高——CPU与GPU分离设计
对于一款以Python为主的大模型GPU推理引擎,要实现较高的性能,关键在于CPU与GPU的分离设计。至少需要将系统拆分为两个进程:CPU进程和GPU进程。CPU进程主要负责与CPU相关的逻辑,例如序列化、调度、分发和Resize等;而GPU进程则专注于GPU推理逻辑,其底层通过直接调用CUDA等库来进行GPU运算。
目前,主流的大模型推理框架,如vllm与sglang,已经实现或正在实施CPU与GPU分离架构。
那么,CPU与GPU分离究竟解决了什么问题呢?
一直以来,Python 在运行时采用全局解释器锁(GIL)机制,这意味着在任意时刻只有一个线程能够执行 Python 字节码。也就是说,即使在多线程程序中,各线程也无法在 Python 层面实现真正的并行执行。这个设计主要是为了简化内存管理和对象引用计数,从而保证线程安全,但也带来了一些限制,特别是在以GPU计算为主的推理服务中更为明显。
在单一的 Python 进程中,如果同时存在多个 CPU 密集型任务(比如网络请求处理、数据预处理、请求验证等)和 GPU 任务,它们都必须在同一个 GIL 下运行。这样,CPU密集型任务就会与GPU任务竞争 GIL,导致 GPU kernel 启动调度不足,从而形成性能瓶颈。这种瓶颈表现为GPU利用率不高,在高并发场景下,GIL 的竞争会极大地影响系统的响应速度和吞吐量。
下表为我们曾经针对Python GIL锁做过的专项对比测试,在做了CPU与GPU分离设计后,GPU利用率大幅提高,QPS提升了7倍,RT缩减了50%。
下面是VLLM在0.6版本做的最大变更,即做CPU与GPU进程分离设计,带来了性能的大幅提升,吞吐提升了2.7倍。具体可以参考文章[1]vLLM v0.6.0: 2.7x Throughput Improvement and 5x Latency Reduction。
可扩展性足够好——各模块高内聚低耦合
为了实现高效且易于扩展的设计,我们应将系统按照功能拆分为多个模块,每个模块只负责其特定功能,确保模块内部的高内聚和模块间的低耦合。一个完整的大模型推理框架至少需要包含以下四个模块:
- 接入层:接入层负责处理各种请求。比如当收到OpenAI格式的请求时,接入层将其转化为内部可识别的原始请求(raw request),以便后续其他模块继续处理。
- 调度器:调度器负责管理和调度各个请求(Request)。当有多个并发请求时,调度器动态调整模型的输入和输出,以确保计算资源得到高效利用,同时满足调度限制,如GPU缓存、最大请求数和最大处理长度等。调度器通过管理请求的状态、缓存、优先级和资源使用,确保推理过程流畅进行。
- 模型推理:在接收到请求后,模型推理层调用相应模型的forward方法进行推理计算。其底层实际上调用CUDA等进行GPU推理。
- 显存管理:操作系统有物理内存管理机制,避免了频繁申请和释放内存带来的碎片问题。然而,CUDA计算中并没有显存管理机制,频繁的显存申请与释放同样会导致显存碎片问题。因此,显存管理成为推理引擎中不可或缺的模块,用于解决显存碎片问题。
大模型推理框架设计
综合上述内容,我们可以设计出一个高性能、可扩展的大模型推理框架。框架图如下:
从框架图中可以看出,系统首先被拆分为多个进程(多个CPU进程与GPU进程),进程间可通过管道等方式进行通信。此外,系统在逻辑上又被拆分为多个模块,其中接入层、调度器、模型推理和显存管理四个模块是必不可少的。
该架构也是当前vllm 1.0与sglang等经典推理框架的基础架构。感兴趣的同学可以通过查看相关代码,你会发现它们的设计思路大致与上面相同。比如下面的sglang推理引擎的代码,参考:[2]sglang 代码