Microsoft.ML.OnnxRuntimeGenAI并行请求性能问题及优化咨询
问题分析与优化方案
一、单请求耗时随并行数增加的原因
- 模型资源竞争:你将
Model注册为单例,多个并行请求会共享同一个ONNX Runtime会话资源。虽然会话本身支持多线程,但模型计算(如ComputeLogits)的底层资源(CPU核心、内存带宽)会被多个请求分摊,导致每个请求的计算等待时间增加。 - 内存带宽瓶颈:并行请求会生成更多中间张量,内存读写操作增多,当内存带宽达到饱和时,每个请求的张量加载、计算速度都会下降,直接拉长单请求耗时。
- CPU核心利用率不足:从测试数据看,4个并行请求时CPU使用率仅~50%,说明当前的线程配置或模型并行策略没有充分利用CPU资源,多个请求的线程调度存在冲突,导致单请求能获取的计算资源减少。
二、基于Microsoft.ML.OnnxRuntimeGenAI的性能优化方案
1. 优化ONNX Runtime会话配置
给Model添加自定义的SessionOptions,调整线程数、执行模式和内存复用策略,提升多线程下的资源利用率:
// Program.cs中修改Model的创建逻辑 var sessionOptions = new SessionOptions(); // 根据CPU核心数设置内部并行线程数 sessionOptions.IntraOpNumThreads = Environment.ProcessorCount; // 启用并行执行模式 sessionOptions.ExecutionMode = ExecutionMode.ORT_PARALLEL; // 启用内存复用,减少内存开销和竞争 sessionOptions.EnableMemoryPattern = true; // 如果有GPU,添加对应的执行提供者(比如CUDA/DirectML) // sessionOptions.AppendExecutionProvider_CUDA(); builder.Services.AddSingleton(new Model(modelDirectory, sessionOptions));
2. 批量处理请求
将多个并行请求合并为批量输入,利用模型的批量计算能力,减少重复的模型加载和资源竞争。可以修改API支持批量请求:
// 修改TestController的Generate方法支持批量 [HttpPost("generate-batch")] public List<string> GenerateBatch([FromBody] List<Dto> dtos) { var tokenLists = dtos.Select(d => tokenizer.Encode(d.test)).ToList(); using var generatorParams = new GeneratorParams(model); generatorParams.SetSearchOption("max_length", 2048); generatorParams.SetInputSequences(tokenLists); var results = new List<string>(); using var generator = new Generator(model, generatorParams); while (!generator.IsDone()) { generator.ComputeLogits(); generator.GenerateNextToken(); } foreach (var seq in generator.GetAllSequences()) { results.Add(tokenizer.Decode(seq)); } return results; }
3. 优化生成循环逻辑
当前逐token解码的方式会带来额外开销,可以攒多个token后再一次性解码,减少Decode调用次数:
// 修改Generate方法中的循环 var resultTokens = new List<int>(); using var generator = new Generator(model, generatorParams); while (!generator.IsDone()) { generator.ComputeLogits(); generator.GenerateNextToken(); var outputTokens = generator.GetSequence(0); var newToken = outputTokens.Slice(outputTokens.Length - 1, 1); resultTokens.AddRange(newToken.ToArray()); } return tokenizer.Decode(resultTokens);
4. 使用量化模型
使用INT8量化后的Phi-3 ONNX模型,可以显著减少内存占用和计算量,提升多请求下的运行速度。你可以通过ONNX Runtime的量化工具对原始模型进行量化,或者直接下载官方提供的量化版本。
5. 启用GPU加速
如果有可用的GPU,通过添加GPU执行提供者(如CUDA、DirectML),可以将模型计算转移到GPU,利用GPU的并行处理能力,大幅降低多请求下的单请求耗时。只需要在SessionOptions中添加对应的执行提供者即可(如上述代码中的注释部分)。
测试数据回顾
| Number of parallel requests | Total time to complete all requests | Average request complete time | RAM using | Processor using |
|---|---|---|---|---|
| 1 | 00:01:50.30 | 00:01:50.18 | 4.3 Gb | < 20% |
| 2 | 00:03:07.82 | 00:03:07.64 | 5.4 Gb | ~ 25 % |
| 3 | 00:04:19.06 | 00:04:18.73 | 6.6 Gb | ~ 30% |
| 4 | 00:05:36.74 | 00:05:35.96 | 7.8 Gb | ~ 50% |
内容的提问来源于stack exchange,提问作者Vladyslav Lishchyna
相关产品推荐
相关产品推荐

