AI ONNX Runtime 复用会话 怎样处理
我在把 ONNX 模型接进服务时,最先遇到的不是模型输出错误,而是每个请求都重新创建 InferenceSession:首请求很慢,进程内存也随着流量变得难以判断。更稳的默认模式是按模型和会话配置创建一次,后续请求复用它调用 session.run()。会话可以共享,并不意味着并发数可以无限放大;真正的上限还要看执行提供程序、输入缓冲区和显存预算。
官方地址:https://onnxruntime.ai/
把 InferenceSession 当成服务级依赖,把输入和输出当成请求级状态;先复用会话,再用信号量或队列控制同时运行的请求。
把会话提升为长期依赖
ONNX Runtime 的 Python API 将 InferenceSession 定义为加载并运行模型的主类。模型路径、SessionOptions 和 providers 都属于创建会话时的契约,不应该在每个 HTTP 请求里重复决定。我的经验是把它收进一个小型 runner:初始化阶段加载模型,请求阶段只接收本次输入。
import threading
import onnxruntime as ort
class ModelRunner:
def __init__(self, model_path: str, max_inflight: int = 2):
options = ort.SessionOptions()
# 图优化和线程配置在会话创建时确定,避免请求之间互相改配置。
options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
options.intra_op_num_threads = 2
self.session = ort.InferenceSession(
model_path,
sess_options=options,
providers=["CPUExecutionProvider"],
)
# 槽位只限制同时运行数,不复制模型,也不共享请求输入。
self.slots = threading.BoundedSemaphore(max_inflight)
def infer(self, input_name: str, input_value):
# 每次调用使用调用方准备好的独立输入,避免并发改写同一数组。
with self.slots:
return self.session.run(None, {input_name: input_value})
这里的重点不是把 ModelRunner 做成全局变量,而是让它的生命周期至少覆盖一批请求。实际服务可以通过依赖注入持有它。max_inflight 是资源预算,不是 ONNX Runtime 的“正确答案”:CPU 模型可以从较小值开始,GPU 模型则要把临时激活内存、复制开销和尾延迟一起测量。

模式成立的压力:初始化贵,运行要稳定
创建会话通常包含模型读取、图转换、算子准备和执行提供程序选择。把这些工作放进请求路径,会让首请求和常规请求混在一起,也容易让每个会话各自创建线程池。官方线程管理文档提供了 intra_op_num_threads、inter_op_num_threads、执行模式和图优化级别等选项;它们应围绕部署机器和模型形状做一次配置,而不是在高峰期临时修改。
执行提供程序也属于这个模式的边界。CPU、CUDA、TensorRT 或其他 provider 的优先级决定哪些节点由哪一侧执行;同一个模型换 provider,内存和并发行为可能随之变化。若要切换 provider,应在初始化或明确的重建流程里做,不能把 set_providers() 当成每个请求的开关,因为官方文档说明这个调用会重建底层会话。
我会把以下三项放在 runner 配置旁边记录:模型输入形状是否固定、provider 列表及其顺序、并发槽位的初始值。这样出现延迟变化时,至少能分辨是模型输入、执行后端还是请求排队造成的。
用信号量控制并发,而不是盲目复制会话
ONNX Runtime 的核心设计允许多个线程调用同一会话的 Run(),但这不是“每个 provider 都保证相同的并发体验”。共享会话可以减少重复加载和重复缓存,复制多个会话却可能增加线程池、权重或设备内存压力。更可控的做法是先共享一个会话,用信号量限制同时进入推理的请求数,再根据吞吐、P95 延迟和内存曲线决定是否扩容。
class BoundedRunner:
def __init__(self, session, max_inflight: int):
self.session = session
# 从1开始压测,再逐步增加;不要用无限队列掩盖设备内存压力。
self.slots = threading.BoundedSemaphore(max_inflight)
def run(self, feeds: dict):
# feeds 必须属于当前请求;调用期间不要由其他线程改写其中的数组。
with self.slots:
return self.session.run(None, feeds)
这段代码只表达资源边界,不代表任意硬件都适合并发值2。对于 CUDA 等设备后端,多个同时运行的请求可能增加临时 workspace 和输入输出拷贝;对于低并发服务,值1反而可能带来更稳定的尾延迟。若模型需要 I/O Binding,也要把绑定对象的所有权和生命周期按请求隔离,不能为了复用会话顺手复用一份正在变化的绑定状态。

反例:会话能复用,不等于所有状态都能共享
最常见的反例有三个。第一,把 InferenceSession(model) 写进请求处理器;它把初始化成本乘到请求数上。第二,把一个可变的 NumPy 输入数组放在全局变量里,多个请求同时做预处理;即便会话本身可以并发运行,输入也可能在推理期间被改写。第三,只看 CPU 的经验就套到 DirectML。ONNX Runtime 的 DirectML 文档明确要求关闭内存模式、使用顺序执行,并说明同一会话不能被多个线程同时调用;此时应使用单槽位、队列或不同会话的受控模型。
因此,代码里应共享的是已初始化的模型执行器,而不是正在被请求修改的字典、数组、OrtValue 或 I/O Binding。请求结束后只返回自己的结果;如果结果还要异步交给下游,先明确复制或所有权转移策略。
我的判断清单:什么时候单会话就够了
- 模型和 provider 固定,初始化成本明显高于一次推理:优先单会话长期复用。
- 多个请求共享同一会话:先加有界信号量,避免把设备并发交给无限线程。
- 输入数组、输出容器或绑定对象会被修改:按请求创建,不能只因为模型相同就共享。
- 切换 provider、SessionOptions 或模型文件:视为重建会话,不在请求中临时切换。
- 使用 DirectML 或遇到特定 GPU 后端异常:遵循该 provider 的限制,先把并发降到1再定位。
- 只有当单会话已经成为瓶颈,并且内存、线程和 provider 都允许时,才比较多会话方案。
官方资料可从以下入口继续核对:https://onnxruntime.ai/docs/api/python/api_summary.html、https://onnxruntime.ai/docs/performance/tune-performance/threading.html、https://onnxruntime.ai/docs/execution-providers/DirectML-ExecutionProvider.html。这几个页面分别对应会话 API、线程配置和特殊 provider 边界。
两个容易混淆的问题
复用会话会不会让模型权重自动复制很多份? 单纯重复调用 run() 不会按请求创建新的会话;但不同会话的内存占用不能假定完全共享,尤其是设备端缓存和临时工作区。是否拆成多会话,要用目标 provider 的实际内存曲线判断。
同一会话能不能直接放进线程池? 核心层允许并发调用 Run(),但 provider 可能有更窄的限制,且请求输入仍必须独立。先用有界并发做基线;如果是 DirectML,按官方限制串行调用同一会话。
AI Datasets 按列过滤 怎样处理
- 上一篇
- AI Datasets 按列过滤 怎样处理
- 下一篇
- AI vLLM 限制显存 怎样处理
-
- 科技周边 · 人工智能 | 20分钟前 |
- AI AI 网关按能力路由 怎样处理
- 467浏览 收藏
-
- 科技周边 · 人工智能 | 28分钟前 |
- AI 流式响应断线重连 怎样处理
- 470浏览 收藏
-
- 科技周边 · 人工智能 | 31分钟前 |
- AI RAG 元数据过滤 怎样处理
- 223浏览 收藏
-
- 科技周边 · 人工智能 | 32分钟前 | python · 人工智能 · 结构化输出 · AI Hugging Face 结构化输出 JSON Schema TGI Inference Providers grammar response_format
- AI JSON Schema 约束输出 怎样处理
- 339浏览 收藏
-
- 科技周边 · 人工智能 | 35分钟前 | 人工智能 · AI 可观测性 span OpenTelemetry trace GenAI语义约定
- AI OpenTelemetry 补 trace 怎样处理
- 219浏览 收藏
-
- 科技周边 · 人工智能 | 36分钟前 |
- AI vLLM 限制显存 怎样处理
- 476浏览 收藏
-
- 科技周边 · 人工智能 | 40分钟前 | python · 数据处理 · 人工智能 · AI Hugging Face Datasets select_columns remove_columns DatasetDict 数据集字段
- AI Datasets 按列过滤 怎样处理
- 373浏览 收藏
-
- 科技周边 · 人工智能 | 44分钟前 | python · 人工智能 · AI 文本分类 pipeline padding Transformers 批量推理 truncation batch_size
- AI Transformers 批量文本分类 怎样处理
- 379浏览 收藏
-
- 科技周边 · 人工智能 | 46分钟前 |
- AI 工具调用接回多轮对话 怎样处理
- 180浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 585次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 622次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 655次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 622次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 436次使用
-
- Go并发控制Channel使用场景分析
- 2022-12-23 459浏览
-
- Go并发控制WaitGroup的使用场景分析
- 2023-02-16 264浏览
-
- Golang 语言控制并发 Goroutine的方法
- 2023-01-07 285浏览
-
- Golang 实现分片读取http超大文件流和并发控制
- 2022-12-24 140浏览
-
- Go 并发控制context实现原理剖析(小结)
- 2023-01-01 470浏览

