共享内存 IPC 缓存:加速 LLM 推理系统中的数据传输

阅读时间 7 分钟
Donglu Wang (Cohere)

注意: 本文最初发布于 Cohere 博客

隆重推出共享内存 IPC 缓存——这是 由 Cohere 为 vLLM 项目贡献 的一种高性能缓存机制。它通过绕过冗余的进程间通信并将大型多模态输入保留在共享内存中,大幅降低了数据传输开销,从而实现了在大规模环境下更快、更高效的 LLM 推理。

现代 LLM 推理通常涉及多个协作进程,这些进程通过进程间通信(IPC)进行交互。随着并行规模的扩大和输入内容的丰富(例如多模态数据),IPC 开销往往会迅速演变为主要的性能瓶颈。

借助共享内存 IPC 缓存,我们可以显著减少单节点内进程间冗余的数据传输。我们的基准测试显示:

  • 首次请求:预填充(Prefill)吞吐量提高了 11.5%,首字延迟(TTFT)降低了 10.5%
  • 缓存请求(KV 和图像输入均被复用):预填充吞吐量提高了 69.9%,首字延迟(TTFT)降低了 40.5%。这些性能提升主要源于消除了进程间冗余的 IPC 传输。

此外,这些收益会随输入大小和张量并行(TP)规模的增加而扩展:更大的输入和更宽的 TP 配置涉及更繁重的 IPC 流量,这使得共享内存缓存对于大型多模态工作负载而言更具影响力。

LLM 推理中的进程间通信

在典型的多进程 LLM 推理架构中,通常包含三个主要组件:前端(front-end),负责处理和预处理用户请求;协调器(coordinator),负责管理调度和编排;以及推理 工作进程(worker),负责运行模型计算。

下图展示了在一个使用四块 GPU 的 LLM 推理系统中进程如何协同工作。前端将输入数据发送给协调器,协调器随后将其路由到四个工作进程(每个 GPU 一个)以执行推理。


图 1. 使用四块 GPU 的 LLM 推理系统中进程协同工作的概览

每个阶段通常在单独的进程中运行,以实现可扩展性和异步执行。因此,数据必须通过 IPC 在这些进程之间流动。对于较小的输入,这种开销可以忽略不计,但随着输入的增长,IPC 时间可能成为严重的性能瓶颈。

问题:重复的大规模数据传输

多模态输入(如图像、音频或长上下文序列)可能会非常庞大。例如,在 CohereLabs/command-a-vision-07-2025 模型中,一个 1024×3072 像素的最大尺寸输入图像,以 int8 数组表示时约为 9 MB。该模型还可以接收多张图像作为输入,因此每个请求的总大小很容易达到数十兆字节。

通过 IPC 在进程间传输此类大数据并非“免费”。在多轮对话或批处理中,相同的输入可能会被多次传输,进一步加剧了开销。

现有解决方案:镜像缓存

vLLM 此前已使用镜像缓存(mirrored caching)来减少冗余 IPC 传输。在这种方法中,发送方和接收方维护相同的缓存副本,并遵循相同的插入顺序和逐出策略。当发送方检测到特定输入的缓存命中时,它会假设接收方的缓存处于相同状态,从而跳过 IPC 传输。

然而,这种方法有一个关键限制:它依赖于严格的输入顺序,要求发送方和接收方必须以完全相同的顺序处理输入。例如,在典型的“前端-协调器-工作进程”架构中,如果将镜像缓存放置在工作进程上,协调器可能会根据其调度策略重新排序输入,导致缓存不同步,进而可能导致行为错误。

因此,在 vLLM 中,镜像缓存仅应用于前端与协调器之间的通信。对于协调器与工作进程之间的路径,当只有一个工作进程时,vLLM 会将其置于与协调器相同的进程中,消除了额外 IPC 的需求。然而,当涉及多个工作进程时,vLLM 会退回到基于套接字(socket)的 IPC,这会带来序列化、传输和反序列化的额外开销。

新方法:共享内存 IPC 缓存

为了克服传统 IPC 缓存的局限性,我们引入了共享内存 IPC 缓存。现在,发送方和接收方可以直接访问单个共享缓存,消除了对顺序的假设,并避免了冗余的数据拷贝。

共享内存对象存储

我们实现了一个共享内存对象存储(Shared Memory Object Store)数据结构来实现这种缓存,允许一个写入者实例和多个读取者实例高效地共享同一内存缓冲区。

设计

  • 写入者(Writer):将输入对象插入到共享环形缓冲区中,更新地址索引,并将地址广播给所有相关读取者。
  • 读取者(Reader):使用提供的地址直接从共享内存中访问对象。

下图展示了使用共享内存对象存储的 IPC 缓存机制。发送进程维护一个写入者实例,而每个接收进程都有一个对应的读取者实例。


图 2. 使用共享内存对象存储的 IPC 缓存示意图

发送键值对(key-object)时,发送方首先通过 is_cached(key) 检查该键是否已被缓存。如果已缓存,写入者使用 get_cached(key) 获取缓冲区地址;否则,它使用 put(key, object) 将对象存入共享内存并获得缓冲区地址。随后,发送方通过默认的 IPC 将此地址广播给所有接收方。

在接收方一侧,接收到地址后,使用 get(address) 从共享内存中获取对象。为简便起见,此处省略了序列化和反序列化步骤。

逐出与安全

当空间不足时,写入者会从环形缓冲区的头部进行逐出。读取者计数器(共享)写入者计数器(本地)协调工作,以防止在数据仍在使用时发生过早逐出。仅当满足条件 writer_counter × n_readers == reader_counter 时,条目才会被逐出。

优势

  • 无顺序假设:进程可以以任何顺序消费输入。
  • 单一共享缓存:无论读取者数量多少,共享内存的使用量保持不变。
  • 高效并发访问:多个读取者可以同时读取相同的输入,且同步开销最小,无需额外拷贝。

将共享内存对象存储应用于我们之前提到的“前端-协调器-工作进程”架构,我们将写入者置于前端进程中,并在每个工作进程中放置一个专用读取者。这使我们能够绕过中间的 IPC,特别是针对大型输入数据。


图 3. 由共享内存对象存储支持的 LLM 推理系统中的进程协同概览

vLLM 基准测试结果

我们通过一次 PR 将共享内存 IPC 缓存实现到 vLLM 中用于处理多模态输入。为了评估其影响,我们进行了以下基准测试:

以下是测试结果:

首次请求

指标基准共享内存 IPC 缓存差异
预填充吞吐量581.34 tok/s648.22 tok/s+11.5%
平均首字延迟 (TTFT)3898.98 ms3491.15 ms−10.5%

速度提升源于在前端进行一次写入,并允许工作进程并发读取,从而消除了冗余传输和 IPC 排队延迟。

缓存请求

指标基准共享内存 IPC 缓存差异
预填充吞吐量2894.03 tok/s4917.57 tok/s+69.9%
平均首字延迟 (TTFT)790.18 ms470.60 ms−40.5%

在此场景下,KV 和图像输入均被缓存,降低 IPC 开销所带来的效益尤为显著。

立即开始

共享内存 IPC 缓存加速了 LLM 系统中的数据移动,使其更精简、更具扩展性——特别是在处理具有大量多模态输入或多个并发 GPU 工作进程的工作负载时。除了 LLM 推理之外,它还可以在任何通过 IPC 缓存减少冗余数据传输的场景中提升性能,使其成为处理多种应用的通用工具。

此功能现已在 vLLM 主分支中可用。要为多模态缓存启用此功能,请设置 mm_processor_cache_type = "shm"。更多信息请参阅 vLLM 用户指南

致谢

特别感谢 Cohere 的 Bharat Venkitesh 和 vLLM 社区成员:Cyrus Leung(在代码审查和集成方面提供了宝贵反馈);Nick HillRoger Wang(在早期阶段验证了概念);以及 Kero Liang(报告并协助修复了一个 bug)。