第一视角下关于 ServerlessLLM 的故事(上)

引:滴滴打车

我第一次接触 Serverless 大概是在 2018 年。当时我还在摸索搭建个人 blog,在 AWS 上看到了相关产品。这里简单介绍一下 Serverless 的背景,我最常用的比喻是:

  • 第一种方案是买服务器自己部署,就像买车。可以完全掌控机器配置和环境,但缺点是需要花费大量额外成本,如机器维护、硬件成本、机房租金、电费、空调费等。
  • 第二种方案是租服务器,类似租车。虽然不用为硬件和场地操心,但这种模式会带来巨大的浪费。租来的机器不能保证每时每刻都在 100% 使用。最常见的情况是大部分公司业务分昼夜:白天用户多,晚上用户少。晚上租了很多机器,开销非常大。此外,还有服务器网络安全等一系列头疼的问题。
  • 第三种方案是买 API,类似公交车或地铁。只购买所需的业务类型,常见例子有 OCR、数据库和图像识别等,这些功能的维护成本较高,部署也比较麻烦。

随着具体业务的增加,Serverless 模式应运而生。固定的 API 功能僵化,企业也不想为了一个业务功能耗费太多服务器成本。这时,许多企业选择类似「滴滴打车」或「Uber」的计费模式,购买 Serverless 产品。这种方案有很多名称,有些地方叫 Cloud Function 或 Micro-Service,也有人称为 SaaS (Software as a Service) 或 FaaS (Function as a Service)。不过我更习惯统称它们为 Serverless。

这种模式的好处是定制化按需计费。但很多人忽略了一个问题:微服务实际上并不「微」。一年前我读过一篇有趣的文章:十年了,为什么 Serverless 不成功

这篇文章讨论了很多现实业务中 Serverless 不好用的具体例子和场景。但总的来说:我们的划分单位出了问题。到底多小的业务适合用 Serverless 这个问题被模糊了。举个例子,如果一个初创企业的产品数据用了一个 FaaS 的数据库,用户管理用了 Clerk 这种 SaaS 产品,那么一个简单的查询就涉及两套产品的对接。这样的开发模式表面上降低了成本,用各种微服务构建了一个强大的产品,但实际上,背后错综复杂的组合逻辑让微服务的维护格外沉重。

那为什么现在 Serverless 又开始流行了呢?因为异构计算和 AI 业务的出现,为我们提供了一种自然的划分方案。要用 GPU 的业务就是一个独立的单元。这个逻辑简单粗暴,但非常合理。毕竟带 GPU 的服务器在价格、运维成本上显然不是普通服务器能比的。

到了 2023 年,很多企业想尝试将大语言模型落地。然而大部分企业面临的困境是:我们的业务已经稳定运营,不确定用上 AI 模型后效果会不会好,成本会不会增加。大部分企业不具备量化评估这些方案的能力。于是,我刚拿到博士录取,我的导师安排我和师兄一起研究 Serverless + LLM 这种让企业更轻松使用大语言模型的部署方案。

Serverless + LLM 的加号,有点难做

为什么 GPU + Serverless 难做?我直接引用一下我一篇 blog 里面做出的分析:

传统的 Serverless 业务中的一个重要环节是 cold boot,也就是常说的冷启动时间。简单来说:当一个应用场景中的用户数量增长超过现有机器的承载能力后,我们需要启动新的机器,配置上当前的业务程序去承载更多的用户。(当然,更准确的说法不是启动新的机器而是新的容器)

在经典的场景中:

Cold boot = 启动新容器的时间 + 容器和已有服务集群连接时间

但是,在 AI 场景中,我们的应用负载有了非常大的区别: - 大文件:模型本身在这种环境下是一个必须的超大文件,这部分下载需要耗费大量的时间 - 新硬件:我们的容器中包含了 GPU 硬件,但是运行大模型的时候,我们真正用的并不是机器内存,而是 GPU 上的显存 - 启动复杂:传统应用启动之后,我们应用可以直接运行;但是作为 AI 应用,我们需要将模型载入 GPU

换而言之,现在 AI 时代的大模型应用:

Cold boot = 启动新容器的时间 + 模型下载时间 + 模型载入内存时间 + 模型载入 GPU 时间 + 容器和已有服务集群连接时间

为了给出一个更加量化的具体感受,我们测试了一个比较流行的 Serverless GPU 方案:Ray Serve + PyTorch。 实验环境采用了:10 Gbps network, A5000 with PCIe 4.0, NVMe SSD。

Model Download Load \(1^\text{st}\) token End-to-end
LLaMA-2-7B
10.8 s 4.8 s 0.8 s 20.1 s
LLaMA-2-13B 21.0 s
9.5 s
0.9 s
34.5 s
LLaMA-2-70B
111.9 s
48.0 s
8.3 s
173.7 s

不难看出,绝大部分时间的开销都在模型的下载和加载上。现有的解决方案大致有三种:

  • 多开(Oversubscription):提前启动足够多的 Serverless 服务等待客户,但这种方式价格明显不合适,而且会导致资源浪费。
  • 使用 DRAM 作为缓存:空间有限,虽然之前我们认为这是最好的办法,但随着 grok 这种 600G 的模型出现,即使是 2TB 的超大内存,也只能存下几份模型副本。
  • 增加高速外部存储服务:同样太贵,不适合长期使用。

对企业来说,部署模型最重要的永远是降本增效。增效的问题主要在模型本身,我们不是特别关心,所以如何降本就非常有讲究了。我们认为我们的机会在于利用 GPU Serverless 中被忽视的本地存储空间。GPU serverless 服务器厂商提供的服务只关心 GPU 本身,例如 banana 和 runpod 公司的服务,甚至只会暴露 GPU metrics 给客户。所以我们认为这些服务器有以下特征:

  • 有 TB 级别的 DRAM
  • 有几十 TB 级别的空闲 SSD

我们整篇工作其实都是在探讨如何更好地利用这些空间进行调度。主要有三点贡献:

  • 开发了一个可以快速拉起模型到 GPU 的模型格式
  • 发明了一种让模型推理可以快速迁移的方案
  • 在上面的基础上,我们设计了一种调度模式,让 Serverless 场景下 end-to-end 的延迟最低

模型格式

为什么我们要设计新的模型格式,是 torch 和 safetensors 不够好吗?

确实,在训练时,我们采用的是 Persist many, load few 的模式,因为我们需要将模型锁住,并不断更新梯度。然而在推理场景中,我们只关心加载的速度,属于 Persist once, load many 的模式。

为此,我们采用了解耦设计,将模型的初始化和 checkpoint loading 独立开:

这种设计的好处是,我们可以专注于各种优化手段以加速 checkpoint loading,从而追求极致的模型加载速度。

我当初加入项目时,就是负责研究这一块的 CUDA kernel 设计。最终,大家通过各种方案的组合,榨干了所有可能的带宽:

结果上来看,确实达到了非常理想的效果:

某种意义上,这项贡献可以独立于整个项目之外作为独立项目,在我们的试验环境下,模型加载速度提升了 5-8 倍。如果有更多的 PCIe 支持,我们甚至可以达到更高的加载性能。

和最近的 prefill/decoding 做区分的思路一样,不同负载的任务就应该使用差异化的框架和方案。compute-bound 和 memory-bound 的任务确实应该使用不同的计算范式。我们在模型加载中的思考也一样,训练和推理就是不一样的任务,所以,用一套模型格式可能不是那么聪明的选择。

Live migration

这部分工作在会场上引起了最大的争议。今年,阿里的一项工作与我们的思想非常类似,但采用了完全不同的策略。

首先介绍一下,什么是 Live Migration?

这里用一个例子直接回答这个问题:

我们现在有两台服务器,第一台机器上,Model B 在 SSD 里;第二台机器上,Model B 在 DRAM 里,但第二台机器的 GPU 正在推理 Model A。现在我们希望找到一台机器去运行 Model B 的推理任务,那么哪一台更合适?

如果我们直接找空闲 GPU,即图中的方案 (a),我们需要等待模型从 SSD 中加载,这个时间开销巨大。如果我们采用抢占式方案执行,如图(b),我们需要重新对 Model A 进行推理,打断带来的开销过高。

基于对 LLM 应用特性的观察,我们最终采用了 Live Migration 策略。简单来说,就是迁移 token,而不是暴力迁移 KV-Cache 或完全从头重推。

基于实验设备,我们做了一个简单的评估:

  • 迁移 token:带宽和网络开销可以忽略不计。而且我们可以将已经处理完的 token 和原先的 prompt 合并成一个新的长 prompt,这样可以充分利用 GPU 设备的计算红利。
  • 迁移 KV-Cache:会导致网络严重拥塞,而且每一步实际上都是在重新执行 decoding。

两种方案无非就是计算和通信的取舍。在会场上,阿里的老师认为,不论什么场景都应该迁移 KV Cache。如果在阿里云上,我确实认为没什么问题。阿里云的基建非常强,任何两台机器之间都有 64GB/s 级别的网络通信,这时候迁移 KV Cache 的成本是非常可控的。然而,我们的学校集群是临时借来的,最高只有 10Gb/s 的共享带宽,这也是导致我们得出不同结论的主要原因。如果有企业希望部署 Serverless 的 LLM 业务,希望这个重要的信息能对大家有所帮助。

最后用一个动图来演示一下三种方案:

  • 方案 (a):直接找空闲 GPU,需要等待模型从 SSD 中加载,时间开销巨大。
  • 方案 (b):采用抢占式方案执行,需要重新对 Model A 进行推理,打断带来的开销过高。
  • Live Migration:迁移 token,不暴力迁移 KV-Cache 或完全从头重推,利用 GPU 设备的计算红利,避免网络拥塞。

startup-time-optimized model scheduling

最后一项贡献其实就是基于前两项贡献的顺水推舟了。我们刚刚说了 Live Migration 好,但是也是在网速受限,且我们重新 prefill 的代价不太高的前提下。所以我们到底要不要做 live migration,实际上是由一个简单的线性模型来决策的。

我们在实验的过程中发现,live migration 的开销近似等于 prefill 的开销,而这部分和 token length 是线性相关的。

所以我们就可以简单地用一个算式来对我们的调度进行决策。

小结

自从 LLM 兴起之后,越来越多的企业和机构开始尝试 Serverless + LLM 的组合。学界有 Ion Stoica 带领团队开发的 Ray,以及衍生出来的 KubeRay 和 Ray Serve;工业界也有 Banana.dev 和 Runpods 等。这一派「万物经发,勃勃生机」的景象让人兴奋。

但是在这篇繁荣生态之下我们确实一直忽视了 cold boot 的问题,也是借着这篇文章的机会,我们认真分析了模型训练和模型推理两种场景中对 checkpoint 格式的不同需求,定制化设计了合适的解决方案。所以 Serverless + LLM 这个问题中,我们的花了接近一年的时间掩藏中间的加号,让 ServerlessLLM 变得丝滑易用。这个缝合过程,可能还是比大家想的稍微复杂一点的。

OSDI 2024 是我们的开始

OSDI 2024 其实只是我们整个项目的开始,坦白来说我们对我们自己的工作只能说 95 分的满意,还有很多细节没有做好。而且之后还有非常多的发展空间:

网络层面

正如我们今年在会场上和阿里的争论一样,我们两个在这个问题上都没有做好。实际上,有些场景是可以走 migrate KV 而有些场景确实是可以 migrate token 的。而且我们还没有使用 RDMA、CXL、NVLink 等复杂的网络通信技术。这些都会影响我们 load 模型的方式,也会影响我们的决策。

应用层面

这篇文章中我们只研究了 LLM 推理这种简单的场景。实际上,对于部署整个应用而言,还有很多类似的「可以迁移的应用」没有进行研究和探索。比如,各种微调模型和多模态模型。由于这些应用的特质,性能分析和我们的 LLM 推理场景有所不同。例如,对于微调模型,我们可能只需关注 Lora 层,模型切换更轻便;而多模态模型的 token 更长,可能不再适合进行 token 迁移。不过具体情况我们还没有时间进行进一步分析和测试。

存储层面

我们的文章里提到了我们用上 local SSD 达到一个比较理想的效果。那么这么做其实引入了另一个问题:我们在初始状态下,每个机器 SSD 预存什么模型会比较合适?机器闲置的时候,我们应该怎么提前预备 SSD 里面的模型内容以降低下一个 replica 的启动时间?这个其实是一个非常接近生产的问题,要获得一切用户的行为习惯和真实的使用数据,我们才方便进行下一步思考和研究。这里非常欢迎各位工业界的朋友给我们提供宝贵的数据和意见。

资源利用层面

我们在实验的时候还做了一个有待商榷的设置:模型是独占 GPU 的。这个设计解决起来不难,但是还是需要一些额外的代码成本去进行实现的。这部分的工作我们暂时还没有完全处理完成,如果有兴趣的朋友可以加入我们的开源项目帮我们一起补充这些。

结语

其实,我们的项目近期还是收到很多方面的质疑的。

学界的质疑主要是 migrate 的具体内容,到底是 token 还是 KV Cache。这个争议我们在上文给出了一个比较完整的回复。

而来自业界的质疑主要是传统的云计算厂商。大部分人会有一个先入为主的观念,就是 K8s 和 vGPU 已经足够完善了,为什么我们还需要一套新的 ServerlessLLM?

如果是两年前,我确实很难给出回应。但是现在随着模型的增加,和异构设备的独立性不断变强的今天,我可以自信地给出我的见解。当下我们的硬件设计出现了明显的割裂:内存和内存之间,我们有 CXL 和 RDMA 等高速网络;GPU 和 GPU 之间,我们也有高速的 NVLink 和 NVSwitch。唯独 GPU 和内存之间,这最重要也最容易被人忽视的部分成为了系统中的瓶颈。在更强的 PCIE 设备出现之前,我们认为,我们的 ServerlessLLM 会将是这个硬件过渡期中不可替代的一部分。而未来,等这一部分硬件设施也被追平之后,我们的 ServerlessLLM 框架也可以在多种选择中更合理的进行决策,让模型在最合适的硬件上,用最高效的方式启动。

最后,我们的项目开源代码在 https://github.com/ServerlessLLM/ServerlessLLM ,欢迎大家来玩。


第一视角下关于 ServerlessLLM 的故事(上)
http://blog.chivier.site/2024-07-18/2024/第一视角下关于-ServerlessLLM-的故事(上)/
Author
Chivier Humber
Posted on
July 18, 2024
Licensed under