用一个简单的Python字典将多模态推理性能提升超10%
Modal团队通过分析SGLang调度器的性能瓶颈,发现频繁的CUDA IPC池句柄重新打开操作导致主机开销过高。他们通过一个简单的Python字典缓存替换了重复操作,在Qwen2.5-VL-3B模型上实现了吞吐量提升16.2%、延迟降低超10%的效果。该优化已合并至SGLang v0.5.10版本。
多模态视觉语言模型(VLM)为AI赋予了视觉能力,但推理引擎的优化尚未完全跟上。Modal团队在为客户优化Qwen2.5-VL-3B模型的推理性能时,发现SGLang的吞吐量远低于GPU的理论上限。经过分析,他们找到了一个关键瓶颈——调度器中的主机开销。
调度器是SGLang中负责编排GPU工作的单线程组件,其循环速度直接影响整个推理效率。使用py-spy对调度器进行性能剖析后,他们发现process_input_requests函数消耗了约13%的CPU时间,而其中大部分又花在hash_feature函数上。进一步深入,25%的hash_feature时间(占调度器总时间3%)被用于reconstruct_on_target_device调用,最终落到torch.UntypedStorage._new_shared_cuda。
问题根源在于SGLang的多进程架构:分词器进程将输入转化为张量后,需要通过CUDA IPC共享给调度器进程。原先的实现中,调度器在每次迭代时都会为每个张量重新打开共享句柄,重复进行底层CUDA API的引用计数、PyTorch Storage对象创建等操作。这些“账本”工作在每次调度循环后就被丢弃,造成了大量不必要的开销。
Modal团队的解决方案出奇简单:用一个Python字典缓存每个内存池的句柄。由于CUDA内存池在进程生命周期内不会重新分配,因此无需考虑缓存失效问题。读写时仅需在极少见的写操作上加锁。实现后,再次使用py-spy分析发现,_new_shared_cuda热点完全消失,input处理时间减少了一半以上。
端到端基准测试结果令人振奋:在单张H100上运行Qwen2.5-VL-3B,请求吞吐量提升16.2%(从22.2 req/s到25.7 req/s),首Token延迟(TTFT)平均降低13.2%,每Token延迟(TPOT)平均降低17.2%。尾延迟同样显著改善,P99端到端延迟下降14.9%。有趣的是,解码延迟也下降了17%,尽管优化仅针对输入/预填充阶段。这得益于调度器的单线程特性——减少输入处理时间意味着更多时间可用于批处理和GPU派发。
该优化已通过PR #21418合并至SGLang v0.5.10,任何使用CUDA IPC传输的多模态模型都将受益。Modal团队强调,这一改进只是推理性能工程中“永不阻塞GPU”原则的具体体现。