0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

LLMEngine下一层级的模块内如何实现各自功能接口

jf_pmFSk4VX 来源:GiantPandaCV 2023-08-21 09:45 次阅读

最近业余时间在看新番vLLM,在读源码过程中,对其显存管理原理有了清晰的认识。vLLM系统主要是基于python+cuda实现的,很多其他python项目实现都很混乱(各种重复代码、语意不明/模糊的抽象设计),但vLLM的系统设计却特别工整,为怕遗忘,特别开启本篇,top down的记录一下vLLM框架结构。

回到vLLM这个项目,vLLM针对GPT类模型推理过程中KVCache这个显存占用大头专门设计了block_table,将KVCache分段成多个block存储在GPU中。一方面,这种设计可以共用beam search多batch之间share prompt sequence(的KVCache),减少显存占用。另一方面,在gpu显存和cpu内存间调度这些block,可以在有限的gpu显存空间下同时推理更大batch的sequence,换句话说,就是尽可能拉满gpu显存使用率,提高吞吐。

本篇文章将会按top down的方式介绍整个系统。先总览整个框架包含的基本类型,包括类型之间的关系、各类职责。然后,针对系统入口LLMEngine,介绍各个类之间如何通过接口互相组织完成推理过程,加深各个类功能的抽象理解。更进一步的,分析LLMEngine下一层级的模块内如何实现各自功能接口。(后续也会抽时间专门开一篇介绍vLLM中用到的cuda ops源码,特别是PageAttention部分,敬请期待)

框架概览

271dae0c-3f2c-11ee-ac96-dac502259ad0.jpg

vLLM类关系图

整个框架核心的模块关系如上:

LLMEngine:是整个系统的入口,add_request负责输入prompt请求,step迭代推理,最终返回LLM生成的结果。其内部组合了一个Scheduler和一组Worker。

Scheduler:在每个推理步调度可处理的Sequence输入信息,其组合包含了一个BlockSpaceManager

BlockSpaceManager:维护gpu显存和cpu内存的使用情况,以及Sequence对应Cache的BlockTable信息。

Worker:在每个推理步执行LlamaForCausalLM推理,并返回采样后结果。除一个LLM模型外,其另一个核心组件是CacheEngine。

CacheEngine:负责执行相关gpu、cpu空间的换入、换出、拷贝等操作。

LLMEngine

275bb2ce-3f2c-11ee-ac96-dac502259ad0.jpg

LLMEngine实现细节

从图中可以看到,从上到下按先后顺序LLMEngine分别进行了__init__、add_request、step。

在构造LLMEngine时,LLMEngine就会调用Worker中的CacheEngine,初始化gpu、cpu空间,计算能容纳多少个block。每个block包含block_size个token对应的各层KVCache大小。在后续的组织中都会将Sequence对应的KVCache分成block_size大小的cache block,以方便管理对应block的空间。

add_request接口执行多次,接收多个待处理的prompt,将prompt处理成对应token的Sequence。每个输入prompt构造一个SequenceGroup, 其中包含了多个重复的Sequence为后续beam search做准备。SequenceGroup会最终保存到Scheduler中,以进行后续的调度。

step执行一个推理步。首先Scheduler会调度一组SequenceGroup和相关信息作为当前推理步的执行输入,除了输入外,还会包含当前SequenceGroup所需KVCache的换入换出信息。然后,Worker会将执行一次LLM推理(当然会先执行CacheEngine先准备KVCache)。Worker采样的输出结果会再次更新到Scheduler中的SequenceGroup内,以更新其内部的状态。最后,多次step循环,直到所有prompt对应的SequenceGroup都生成结束。

Scheduler & BlockSpaceManager

Scheduler

Scheduler中包含了三个队列:waitting、running、swapped。每当新增一个SequenceGroup时,添加至waitting队列中。

276dd332-3f2c-11ee-ac96-dac502259ad0.jpg

这三个队列之间的关系如下:

279b5c58-3f2c-11ee-ac96-dac502259ad0.jpg

waitting:等待计算KVCache的SequenceGroup(也就是prompt序列)

running:执行推理的SequenceGroup,会在当前step中作为输入,一共包含两类:

prompt:来自waitting,未计算KVCache的SequenceGroup

generate token:计算过KVCache的SequenceGroup,准备生成下一个token

swapped:KVCache暂时换出到cpu内存的SequenceGroup

在每次schedule执行时,会调度几个队列之间的SequenceGroup,维护队列间的状态,使得当前执行推理尽可能占满显存空间。详细逻辑如上图中的数字标号顺序所示,值得注意的是,通过调度能实现两种解决显存不足的策略,一个是换出到cpu中,另一个就是重计算了(只有在SequenceGroup内只有一个Sequence的情况才能使用)。

当SequenceGroup推理新增了token时,update接口会更新一遍SequenceGroup内的状态。如下图所示,SequenceGroup内包含一组beam search的seq,每次执行推理的时候,每个seq采样s次,那么久会生成n*s个生成的token,根据这里面token保留置信度topn个生成结果。下图所示的结果就是n=4的情况,可以看到topn保留的output里seq1和seq3都来自原始输入seq1(parent_seq=1),此时需要BlockSpaceManager将原始的seq3释放(因为保留的output里没有seq3的输出),然后从seq1拷贝/fork到seq3,再讲新token添加到各个seq中。

27d4af76-3f2c-11ee-ac96-dac502259ad0.jpg

BlockSpaceManager

BlockSpaceManager的功能是管理各个SequenceGroup对应KVCache存储信息。回顾LLMEngine提到过的,每个Sequence的KVCache序列会分成多个block_size长度的cache block,每个cache block的位置信息记录在BlocKspaceManager。如下图所示,BlockSpaceManager包含一个block_tables,其记录cache block到gpu显存或cpu内存物理地址的映射。

27e7fe00-3f2c-11ee-ac96-dac502259ad0.jpg

SequenceGroup刚加入Scheduler的时候并没有分配cache block空间,第一次进入running的时候需要向BlockSpaceManager申请可用的block空间。如下图所示,BlockSpaceManager分配block空间是以一个SequenceGroup作为一组输入,而且默认分配空间的时候,所有SequenceGroup内的token都是一样的(即是相同的prompt),因此会为所有Sequence都指向同一片cache block区域,该区域被引用数为Sequence数量。

当需要为一个Sequence新增token时,如下图所示,有两种情况:

当前cache block空间不足添加新token,则新增cache block。

当前空间足以添加新token,但last block与其他Sequence共用时(ref_count>1),如果新token还是添加到last block,那么会与共用last block的其他Sequence冲突,则需要释放掉last block(free不是真的释放,只是ref_count-1),分配一个新的last block。最后,返回信息标记原本last block内容需要拷贝到这个新的last block,即所谓的“copy-on-write”。

28209062-3f2c-11ee-ac96-dac502259ad0.jpg

最后就是BlockSpaceManager其他接口的实现图解了,详细可参加下图:

28547210-3f2c-11ee-ac96-dac502259ad0.jpg

实际上,BlockSpaceManager只负责维护cache block到gpu/cpu空间的索引,真正进行换入、换出、拷贝操作都需要通过Worker中CacheEngine进行。因此在Scheduler调度的时候,也会收集BlockSpaceManager返回结果,得到当前step所需KVCache的block_to_swap_in、block_to_swap_out、block_to_copy,以供后续CacheEngine操作内存空间。

Worker

Worker负责缓存更新执行和LLM推理执行。关于Worker的这个图比较长,因此这里截断成两张图来看。

2875684e-3f2c-11ee-ac96-dac502259ad0.jpg

如上图所示,Worker在执行时首先进行两个操作。

缓存更新:SchedulerOutputs包含了前面提到的当前所需swap in/swap out/copy的cache block信息,然后通过CacheEngine自定义的ops去执行缓存搬运操作,得到cuda stream的event,后续会在推理LLM各层的时候用到。

准备输入token序列__prepare_input:上图右侧的框内是预处理的过程,将SequenceGroupMetadata包含Scehduler调度得到running的所有SequenceGroup组合一个flatten的token序列,作为LLM的初始输入。Scheduler那节中提到过,running队列中当前执行的SequenceGroup有两类:一类未计算prompt(前缀)的KVCache,这部分需要完整的prompt token输入去计算各层的KVCache(全量推理)。另一类已经计算并缓存前缀的KVCache,因此只需要last token作为输入计算下一个generation token的分布(增量推理)。如上图所示,输入token序列的前半部分是多个prompt的token全量推理序列,后半部分是各个增量推理序列的last token。此外,全量推理的SequenceGroup中多个Sequence共享prompt,因此只需要任意一个Sequence的prompt作用输入就行。

289d68d0-3f2c-11ee-ac96-dac502259ad0.jpg

上图是Worker执行LLM模型的过程。由__prepare_input组装的flatten token在各层映射成flatten hidden state。除了线性层、激活层等token独立计算的层以外,attention层的计算涉及不同token的hidden state依赖。上图主要展开了Attention层的四个主要步骤:

prompt全量推理:prompt序列通过xformers的attention算子计算得到下个layer的hidden state。由于这里attention计算的输入是完整的tensor,不是KVCache中分散的cache block,所以可以用第三方的attention算子完成计算。

等待缓存事件:CacheEngine中发送了异步缓存操作,因此只有当前层序列的cache block完成缓存更新,才能进一步获取KVCache或者记录KVCache,这种异步的实现能通过overlap计算和缓存搬运,节省一部分缓存搬运时间。

记录当前KVCache:当前层输入的hidden state作为KVCache通过自定义算子记录到对应cache block内,这里记录所有有效token的hidden state,包括prompt和last token(last token是前几次step中新增的,所以也没有缓存hidden state到KVCache)。

generation token增量推理:vLLM的核心PageAttention即在此实现,这里作者通过一个自定义算子(好像是参考Faster Transformer实现),实现了基于BlockTable分散KVCache的增量attention计算。

最后LLM内的采样器进行采样,将beam_search结果(新token)返回给Worker输出。

碎碎念

至此,笔者基本完成想要表达的的vLLM top down系统架构,相关的框架drawio已上库(图画的都有点挫,文章里可能不方便看。。),希望这篇文章能帮助有意愿在vLLM上做贡献的小伙伴。针对vLLM作者设计的cache_ops、attention_ops的自定义实现,笔者也会利用业余时间学习,补一篇文章进行介绍。

审核编辑:彭菁

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 模块
    +关注

    关注

    7

    文章

    2485

    浏览量

    46538
  • 接口
    +关注

    关注

    33

    文章

    7640

    浏览量

    148512
  • 模型
    +关注

    关注

    1

    文章

    2704

    浏览量

    47696
  • python
    +关注

    关注

    51

    文章

    4677

    浏览量

    83473
  • GPT
    GPT
    +关注

    关注

    0

    文章

    302

    浏览量

    14869

原文标题:vLLM框架top down概览

文章出处:【微信号:GiantPandaCV,微信公众号:GiantPandaCV】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    簇嵌套簇的中控件属性如何操作

    请问各位大大,我想控制簇嵌套簇中的某个控件的可见属性应该怎样做呢?发现可以控制簇的下一层控件的属性,但是再下一层簇中的控件就不知怎控制了。如图所示,我想控制让其中个布尔控件不可见。怎做到?
    发表于 07-06 23:59

    单片机程序设计的十功力,你练到那一层了?

    简单模块化,如何合理的利用CPU的时间。我曾经写过这一层点简单教程。对付这一层应该是绰绰有余了。第三 并肩作战,时间,说爱你不容易。
    发表于 11-01 08:50

    控件快捷菜单选择事件,能不能选择不是最低一层的菜单?

    图中控件快捷菜单有3,第一层“3”,第二“3.2”,第三“3.2.1”“3.2.2”,通过事件结构里面的“快捷菜单选择”只能触发最后一层
    发表于 04-14 10:06

    单片机程序设计的十功力,你练到那一层了?

    一层 : 我来了处在这一层的典型是可以用C语言写简单的逻辑控制,如闪烁LED,简单数码管显示,简单外围模块驱动实验。般对单片机感兴趣,经常动手实践的人,半年左右,可以练到此地步(针
    发表于 03-22 11:46

    单片机程序设计的十功力,你练到那一层了?

    一层 : 我来了处在这一层的典型是可以用C语言写简单的逻辑控制,如闪烁LED,简单数码管显示,简单外围模块驱动实验。般对单片机感兴趣,经常动手实践的人,半年左右,可以练到此地步(针
    发表于 09-07 10:13

    EPON系统三接口设计

    软硬件平台驱动之上,三功能协议及管理之下,是三功能
    发表于 06-06 05:00

    如何添加一层机械

    请问如何添加一层机械?谢谢
    发表于 09-17 02:56

    文带你看懂HarmonyOS如何适配多种终端

    接口的同时,隐藏内部实现细节。对于中小规模系统,基于架构分层可以较好地解决系统架构解耦和跨团队协作问题,每一层提供定的业务功能,可以由不同
    发表于 10-12 14:37

    HFSS Frequency 设置问题

    HFSS 的drien solution frequency 设置的频率 和下一层的 sweep 里面设置的frequency start stop是什么关系?这两个分别设置的是什么频率?
    发表于 11-07 22:53

    AUTOSAR基础软件是由哪些部分组成的

    基础软件主要是用于提供基础软件服务,包括标准化的系统功能以及功能接口,并且由系列的基础服务软件组成,包括系统服务、内存服务、通信服务等。
    发表于 02-17 08:00

    基于RT-Thread内核的AUTOSAR在n32g上的实现方案

    服务、ECU抽象、MCU抽象,分层的目的是为了实现各层的复用和对下一层的隔离,这正如RT-Thread的分层思想那样,RT-Threa
    发表于 07-27 14:30

    用Verilog/SystemVerilog快速实现个加法树

    reduceBalancedTree提供了两个方法调用:Vec类型和Seq类型均可以调用该方法,该方法实现为:从源代码里可以看出,该方法最终的实现依赖两个参数:op: (T, T) => T 树的每一层级的操作,上面
    发表于 08-01 14:29

    STM32F1 LWIP开发手册

    了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层级结构,每一层都呼叫它的下一层所提供的协议来完成自己的需求。通俗而言:TCP负责发现传输的问题,
    发表于 09-27 08:23

    嵌入式多功能接口转换器的设计与实现

    本文研究如何在嵌入式开发平台上构建一台多功能接口转换器。使得不同接口的智能设备能通过该接口转换器实现数据统一由网络接口传送到计算机。并在AR
    发表于 07-01 17:04 24次下载

    什么是type-c全功能接口 Type-C充电接口和type-c全功能接口有什么不同

    Type-C全功能接口,也被称为USB-C全功能接口,是一种多功能的连接接口标准。它是由USB Implementers Forum(USB-IF)制定的一种标准,并在现代设备中被广泛
    的头像 发表于 08-03 14:32 2.5w次阅读