快速开始#
我们以轻量级模型 Qwen/Qwen2.5-1.5B-Instruct 开始,演示vLLM和SGLang环境下CT-HPKV的使用。在启动推理引擎服务之前,我们需要对环境做一些简单配置:
准备工作#
KV Cache存储路径#
CT-HPKV支持将KV Cache数据卸载到内存(DRAM)和高性能存储(SSD/HPFS)中,存储侧KV Cache文件的读写使用标准的POSIX接口。因 此,如果我们需要(默认开启)将KV Cache卸载到存储系统,需要显式指定KV Cache文件的存储路径。在CT-HPKV中,我们通过设置环境变 量或配置文件来达到该目的:
环境变量
# 假设将KV Cache文件存储到"/mnt/hpkv/storage"目录
(base) hpkv@ctyunos:~$ export HPKV_KV_STORAGE_PATH="/mnt/hpkv/storage"
重要
若未显式设置HPKV_KV_STORAGE_PATH,默认路径为"~/.cache/hpkv/storage";
确保当前用户对HPKV_KV_STORAGE_PATH指定的路径有读写权限;
HPKV_KV_STORAGE_PATH指定的路径需要是高速存储设备,如SSD、HPFS等,不建议NAS、HDD等低速存储;
配置文件
如果我们的推理引擎业务模式相对固定,或者部署工程师更喜欢配置文件的形式,我们可以将所有的配置信息写入配置文件中,然后在启动
HPKV Server时,让程序自行读取其中的配置项。CT-HPKV配置文件采用 .ini 格式,以键值对的形式进行保存。以配置KV Cache
文件存储路径为例,我们可以在 .ini 文件中添加如下内容:
# 假设在"~/config.ini"中添加如下配置
HPKV_KV_STORAGE_PATH=~/.cache/hpkv/storage
CT-HPKV应用默认会读取 ~/.config/hpkv/config.ini 配置文件的内容,如果配置文件不存在或者配置项不存在,会再尝试读取
环境变量的值;如果环境变量也没有,则使用默认值。因此,我们可以将上述编写的 config.ini 文件移动到 ~/.config/hpkv 路
径下,或在启动CT-HPKV应用时,通过环境变量显式指定配置文件路径:
# 显式指定配置文件路径
(base) hpkv@ctyunos:~$ export VLLM_ENABLE_HPKV=1 \
export HPKV_CONFIG_FILE_PATH="~/config.ini" \
vllm serve ...
文件系统类型#
CT-HPKV在设计时,为了提高内存性能,不允许操作系统将内存池的内存交换到磁盘空间。我们采用将共享内存文件存放于ramfs文件系统路径下, 并将内存池注册为锁页内存(Pin Memory)的方式提升性能。因此,我们在使用CT-HPKV前,需要选择一个空的目录,将其文件系统类型挂载为 ramfs类型:
# 文件系统类型挂载(假设挂载到"/mnt/hpkv")
(base) hpkv@ctyunos:~$ mount -t ramfs nodev /mnt/hpkv
# 确认文件系统类型挂载成功
(base) hpkv@ctyunos:~$ mount | grep ramfs
ramfs on /mnt/hpkv type ramfs (rw,nosuid,nodev,noexec,relatime,mode=700)
重要
mount挂载命令通常需要管理员权限;确保当前用户对挂载的路径有读写权限;
如果操作系统内核版本为5.15+, 需要挂载为
tempfs类型;启动推理引擎时,需要使用
export HPKV_SHARE_MEM_FILE_PATH="/mnt/hpkv"方式指定该路径;
至此,所有准备工作就绪。
推理服务启动#
vLLM启动#
# 配置vLLM启用CT-HPKV
(base) hpkv@ctyunos:~$ export VLLM_ENABLE_HPKV=1
# 配置使用的内存池大小 (8G大小)
(base) hpkv@ctyunos:~$ export HPKV_KV_MEM_POOL_SIZE=8g
# 配置共享内存文件路径
(base) hpkv@ctyunos:~$ export HPKV_SHARE_MEM_FILE_PATH="/mnt/hpkv"
# 配置KV Cache文件存储路径
(base) hpkv@ctyunos:~$ export HPKV_KV_STORAGE_PATH="/mnt/hpkv/storage"
# 启动vLLM
(base) hpkv@ctyunos:~$ vllm serve /mnt/data/models/Qwen/Qwen2.5-1.5B-Instruct \
--host 0.0.0.0 --port 8505 --trust-remote-code \
--served-model-name qwen2.5-1.5b --block-size 32 \
--max-num-batched-tokens 8192 \
--gpu-memory-utilization 0.8 \
--enable-prefix-caching \
--kv-transfer-config '{"kv_connector":"HpkvConnectorV1", "kv_role":"kv_both"}' \
--disable-log-requests
重要
由于vLLM V1性能远超V0,且在较新的vLLM版本中已将V0废弃,因此CT-HPKV只适配了vLLM V1。
此时,我们尝试发送一个较长的提示词请求,查看 /mnt/hpkv/storage 路径下的变化,会发现有对应的KV Cache文件生成。
重要
CT-HPKV Server中,有一项名为 VLLM_HPKV_TOKEN_THRESHOLD 的配置,其含义为:在启用CT-HPKV功能时,如果
CT-HPKV命中的词元数量大于等于该值,本次推理请求才从CT-HPKV中加载对应的KV Cache数据到显存,供本次推理复用。如果
命中的词元数量小于该值,则考虑使用AI进行Prefill重计算,而不采用CT-HPKV中缓存的KV Cache数据。该项配置着重考虑计算
与内存加载的平衡。CPU与AI芯片之间通常通过PCIe协议互联,以常见的PCIe Gen 4.0 x16(单向单宽32GB/s)和Gen5.0 x16
(单向带宽64GB/s)为例,数据从内存加载到显存需要一定的时间,如果加载时间甚至超过GPU计算对应词元的时间,则采用计算的
方式更划算。该阈值默认值为1204。用户可根据模型种类、PCIe带宽、实际场景等情况自行调整。
重要
Ascend NPU环境下, vLLM启动方式与上述一致。
SGLang启动#
NVIDIA GPU#
# 配置使用的内存池大小 (8G大小)
(base) hpkv@ctyunos:~$ export HPKV_KV_MEM_POOL_SIZE=8g
# 配置共享内存文件路径
(base) hpkv@ctyunos:~$ export HPKV_SHARE_MEM_FILE_PATH="/mnt/hpkv"
# 配置KV Cache文件存储路径
(base) hpkv@ctyunos:~$ export HPKV_KV_STORAGE_PATH="/mnt/hpkv/storage"
# 对应的命令行调用方式
(base) hpkv@ctyunos:~$ sglang serve --model-path --enable-ct-hpkv \
/mnt/data/models/Qwen/Qwen2.5-1.5B-Instruct \
--host 0.0.0.0 --port 8000 --page-size 64 \
--served-model-name qwen2.5-1.5b
Ascend NPU#
# Ascend相关配置
(base) hpkv@ctyunos:~$ export SGLANG_SET_CPU_AFFINITY=1
(base) hpkv@ctyunos:~$ export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
(base) hpkv@ctyunos:~$ export STREAMS_PER_DEVICE=32
# 配置使用的内存池大小 (8G大小)
(base) hpkv@ctyunos:~$ export HPKV_KV_MEM_POOL_SIZE=8g
# 配置共享内存文件路径
(base) hpkv@ctyunos:~$ export HPKV_SHARE_MEM_FILE_PATH="/mnt/hpkv"
# 配置KV Cache文件存储路径
(base) hpkv@ctyunos:~$ export HPKV_KV_STORAGE_PATH="/mnt/hpkv/storage"
# 对应的命令行调用方式
(base) hpkv@ctyunos:~$ sglang serve --model-path --enable-ct-hpkv \
/mnt/data/models/Qwen/Qwen2.5-1.5B-Instruct \
--host 0.0.0.0 --port 8000 --page-size 64 \
--served-model-name qwen2.5-1.5b \
--device npu --attention-backend ascend
内存命中#
默认情况下,一个请求推理结束后,对应Prefill和Decode阶段词元的KV Cache信息会保存在显存Prefix Cache、内存和存储中。
如果一个新的推理请求进来,KV Cache匹配的优先级是显存 > 内存 > 存储。因此,如果我们想查看对应层级的KV Cache命中
时的推理,我们需要将上一级的KV Cache数据清除。假设我想查看内存KV Cache命中的结果,我需要将显存中的KV Cache数据清除。
恰好,vLLM在服务的端点中提供了一个名为 reset_prefix_cache 的接口,我们通过提交Post请求,显式清除显存中的所有
KV Cache数据。在SGLang中提供了 flush_cache 接口实现同样的功能。不同的是,SGLang的 flush_cache 接口
在默认情况下就开启,vLLM则需要启动调试模式,reset_prefix_cache 接口才可见,如下:
# vLLM 开启调试模式
(base) hpkv@ctyunos:~$ export VLLM_SERVER_DEV_MODE=1
# 其余启动配置不变
# 配置vLLM启用CT-HPKV
(base) hpkv@ctyunos:~$ export VLLM_ENABLE_HPKV=1
# 配置使用的内存池大小 (8G大小)
(base) hpkv@ctyunos:~$ export HPKV_KV_MEM_POOL_SIZE=8g
# 配置共享内存文件路径
(base) hpkv@ctyunos:~$ export HPKV_SHARE_MEM_FILE_PATH="/mnt/hpkv"
# 配置KV Cache文件存储路径
(base) hpkv@ctyunos:~$ export HPKV_KV_STORAGE_PATH="/mnt/hpkv/storage"
# 启动vLLM
(base) hpkv@ctyunos:~$ vllm serve /mnt/data/models/Qwen/Qwen2.5-1.5B-Instruct \
--host 0.0.0.0 --port 8505 --trust-remote-code \
--served-model-name qwen2.5-1.5b --block-size 32 \
--max-num-batched-tokens 8192 \
--gpu-memory-utilization 0.8 \
--enable-prefix-caching \
--kv-transfer-config '{"kv_connector":"HpkvConnectorV1", "kv_role":"kv_both"}' \
--disable-log-requests
此时,我们可以先发送一个长请求,待推理完成后确保该请求对应词元的KV Cache信息已经卸载到CT-HPKV,然后分别使用如下请求清除 vLLM和SGLang显存中的KV Cache数据:
# vLLM 清除显存KV Cache
(base) hpkv@ctyunos:~$ curl --location --request POST 'http://localhost:8000/reset_prefix_cache'
# SGLang 清除显存KV Cache
(base) hpkv@ctyunos:~$ curl --location --request POST 'http://localhost:8000/flush_cache'
此时,显存中的KV Cache数据已经清除,重复发送一个上一个长请求, 推理引擎将尝试从内存中加载对应的KV Cache数据到显存并执行推理。
存储命中#
要测试存储命中,则需要保证显存和内存中同时不存在发送请求对应词元的KV Cache数据。显存KV Cache数据可以调用接口清除,内存侧没有 相应的清除接口,我们可以采用重启推理引擎的方式来实现(二次启动的KV Cache存储路径要与之前保持一致)。