【Bug已解决】AddExternalInitializers copies device (GPU) OrtValues per session instead of using them in …
【Bug已解决】AddExternalInitializers copies device (GPU) OrtValues per session instead of using them in place 解决方案一、现象长什么样用AddExternalInitializers把一个已经常驻 GPU 的OrtValue作为外部初始化器加到计算图里常见于把预加载的大常量权重直接挂到图上时发现每个新建的 session 都会把这份 GPU 张量完整拷贝一份而不是原地复用in-place / 共享引用// 预加载一个大权重到 GPU得到 device OrtValue Ort::Value gpu_weight loadWeightToGpu(...); // 比如 1GB 的 embedding 表 // 把它作为外部初始化器加进图 // 期望session 直接引用 gpu_weight0 拷贝 // 实际每个 session 都 deep-copy 了一份 gpu_weight1GB x N session std::vectorOrtValue* initializers {gpu_weight}; // AddExternalInitializers 内部对 device OrtValue 做了拷贝最小信号GPU 显存随 session 数线性增长每个 session 多一份权重拷贝 预期所有 session 共享同一份 GPU 权重in-place注意这不是结果错而是显存浪费 拷贝开销——大权重本可跨 session 共享却被每份 session 复制。二、背景AddExternalInitializers的作用是把一个OrtValue通常是常量权重注册成图的初始化器这样图在构造时就“拥有”这个权重不用每次run()都从外部传入。当这个OrtValue已经在GPU 设备上时理想情况是 session直接持有它的引用或共享所有权多个 session 共享同一块 GPU 显存。但 ORT 的AddExternalInitializers实现里对 deviceGPUOrtValue走了“拷一份再挂到图上”的路径而不是“原地接管/共享引用”。原因通常是图的初始化器在内部用OrtValue的拷贝语义值语义默认AddExternalInitializers的契约是“把值加进图”实现者图省事直接拷贝。deviceOrtValue的拷贝涉及一次cudaMemcpy设备内或跨流既占显存又耗时。对于大模型几十 GB 权重每个 session 拷一份直接爆显存所以必须 in-place 共享。三、根因根因是AddExternalInitializers对 deviceGPUOrtValue走了深拷贝路径而不是 in-place 共享引用移动语义 / 共享所有权导致每个 session 都复制一份 GPU 权重默认值拷贝语义AddExternalInitializers内部把传入的OrtValue按值存入图的初始化器列表对 GPU 张量触发一次设备内拷贝。未用移动/共享没有判断“如果传入的是 device OrtValue就移动所有权或共享引用不拷贝”于是多 session 各自持有一份拷贝。显存线性增长N 个 session 就有 N 份权重拷贝大模型直接 OOM。不是结果错拷贝出的权重值一样推理正确只是浪费显存和拷贝时间。所以这不是数值错而是API 对 device OrtValue 的共享语义没实现导致本可共享的 GPU 权重被重复拷贝。四、最小可运行复现下面用 C 标准库模拟“AddExternalInitializers 拷贝 vs in-place 共享”的显存差异#include iostream #include vector #include memory // 模拟 device OrtValueGPU 张量记录被拷贝次数 struct DeviceOrtValue { size_t bytes; static int copy_count; DeviceOrtValue(size_t b) : bytes(b) {} DeviceOrtValue(const DeviceOrtValue o) : bytes(o.bytes) { copy_count; } }; int DeviceOrtValue::copy_count 0; // 有 bug 的实现拷贝 device OrtValue 进图 std::vectorDeviceOrtValue addExternalInitializers_copy(const DeviceOrtValue w) { std::vectorDeviceOrtValue graph; graph.push_back(w); // 拷贝 return graph; } int main() { DeviceOrtValue weight(1000000000); // 1GB // 建 4 个 session每个都 AddExternalInitializers for (int i 0; i 4; i) addExternalInitializers_copy(weight); std::cout GPU 权重拷贝次数: DeviceOrtValue::copy_count 每个 session 拷贝一次 - 浪费 4GB\n; // 修复应移动/共享引用拷贝次数 0 }跑出来copy_count 4即 4 个 session 各拷贝一份 1GB 权重共浪费 4GB。修复后应当用移动/共享引用copy_count 0。这复现了“device OrtValue 被重复拷贝”的机制。五、解决方案第一层最小直接修复最小修复让AddExternalInitializers对 deviceOrtValue走 in-place 共享移动所有权或共享引用不拷贝。对使用者临时规避是减少 session 数量全局单例 session让拷贝只发生一次// 全局只建一个持有外部初始化器的 session所有推理复用 // 这样 device OrtValue 只被拷贝或共享一次 static Ort::Session* g_session nullptr; Ort::Session getSession(Ort::Env env, Ort::SessionOptions so, const char* path) { if (!g_session) { // 把 GPU 权重 AddExternalInitializers 后建 session仅一次拷贝 g_session new Ort::Session(env, path, so); } return *g_session; }对 ORT 仓库侧修复是改AddExternalInitializers若传入的OrtValue位于设备内存使用std::move或共享OrtValue的智能指针引用计数接管而不是值拷贝多个 session 共享同一份 deviceOrtValue。这一层立刻消除重复拷贝。六、解决方案第二层结构性改进把“外部初始化器对 device OrtValue 必须 in-place 共享”收口成唯一的配置对象OrtAddExternalInitDeviceCopyPolicyAPI 实现读它from dataclasses import dataclass, field from typing import Tuple, Literal dataclass(frozenTrue) class OrtAddExternalInitDeviceCopyPolicy: AddExternalInitializers device OrtValue 共享语义的单一事实来源。 # device OrtValue 的处理方式in-place 共享不拷贝 device_value_policy: Literal[in_place_share, move, copy] in_place_share # 是否允许多 session 共享同一 device OrtValue allow_cross_session_share: bool True # 受影响 API affected_api: Tuple[str, ...] (AddExternalInitializers,) # 拷贝必须显式声明默认禁止隐式拷贝 forbid_implicit_copy: bool True def should_copy(self, is_device_value: bool) - bool: if not is_device_value: return False return self.device_value_policy copy def describe(self) - str: return device OrtValue 进 AddExternalInitializers 必须 in-place 共享禁止隐式拷贝 POLICY OrtAddExternalInitDeviceCopyPolicy() def plan_add(ort_value_is_device: bool, policy: OrtAddExternalInitDeviceCopyPolicy POLICY) - str: return copy if policy.should_copy(ort_value_is_device) else in_place_share所有 API 实现读同一份POLICYdevice OrtValue 默认 in-place 共享杜绝隐式拷贝。七、解决方案第三层断言 / CI 守护把“device OrtValue 不被隐式拷贝”做成断言。下面用 pytest 风格守护复用第四节逻辑import pytest def test_device_value_in_place(policy): assert policy.device_value_policy in_place_share assert policy.should_copy(True) is False # device 不拷贝 def test_cross_session_share_allowed(policy): assert policy.allow_cross_session_share is True def test_forbid_implicit_copy(policy): assert policy.forbid_implicit_copy is True def test_host_value_no_copy_either(policy): # 即便 host 值按策略也不拷贝直接用 assert policy.should_copy(False) is False这四组断言锁住(1) device 值 in-place 不拷贝(2) 允许跨 session 共享(3) 禁止隐式拷贝(4) host 值也不无故拷贝。CI 跑通即代表共享语义被守护。八、排查清单遇到 AddExternalInitializers 让 GPU 显存随 session 数增长确认是不是 device OrtValue传入的权重在 GPU 上、且每个 session 都多一份 - 锁定隐式拷贝。看 API 语义AddExternalInitializers是不是值拷贝没用移动/共享。查 session 数量是不是每请求一 session应改单例。临时规避全局单例 session只拷贝/共享一次。根本修复device OrtValue 走 in-place 共享移动/引用计数不拷贝。统一策略对象用OrtAddExternalInitDeviceCopyPolicy固化。CI 守护断言 device 值不拷贝、可跨 session 共享。九、小结AddExternalInitializers copies device (GPU) OrtValues per session instead of using them in place的根因是AddExternalInitializers对位于设备内存的OrtValue走了值拷贝路径而不是 in-place 共享引用移动语义/引用计数导致每个新建的 session 都把这份 GPU 权重完整拷贝一份显存随 session 数线性增长、还有拷贝开销。最小修复是让 API 对 device OrtValue 走 in-place 共享移动/共享所有权临时规避是全局单例 session 只共享一次结构性改进是用唯一的OrtAddExternalInitDeviceCopyPolicy固化“device 值必须 in-place 共享、禁止隐式拷贝”CI 用四组断言守护“device 不拷贝、可跨 session 共享、禁止隐式拷贝”。记住常驻 GPU 的大权重进图必须共享引用隐式拷贝会直接撑爆显存。

相关新闻

【信息科学与工程学】【通信工程】第一百五十九篇 SDN网络中的核心算法01

【信息科学与工程学】【通信工程】第一百五十九篇 SDN网络中的核心算法01

编号2 完整表格 字段 内容 编号​ 2 类型​ 算法 领域​ SD-WAN(软件定义广域网) 模块​ 流量工程(Traffic Engineering) – 带宽分配 依赖软件环境​ 编译器:GCC 11.2.0 (C++20) 操作系统:Linux Kernel 5.15 (x86_64) 虚拟化:KVM + Docker 运行库:GLPK (GN…

2026/9/17 10:45:19 阅读更多 →
【Bug已解决】[Web] Expose WebGPU EP buffer cache mode options in JS 解决方案

【Bug已解决】[Web] Expose WebGPU EP buffer cache mode options in JS 解决方案

【Bug已解决】[Web] Expose WebGPU EP buffer cache mode options in JS 解决方案 一、现象长什么样 在 Web 端(ONNX Runtime Web)用 WebGPU EP,想调“缓冲区缓存模式”相关的 session 选项(比如让中间张量在不同 run() 之间复用 …

2026/9/29 3:04:50 阅读更多 →
Spring Boot 3.x升级遇UnsupportedClassVersionError:Java版本不匹配的排查与修复

Spring Boot 3.x升级遇UnsupportedClassVersionError:Java版本不匹配的排查与修复

1. 问题现象与本质剖析 最近在升级一个老项目到 Spring Boot 3.x 时,编译过程一切顺利,但在启动应用时,控制台直接抛出了一个令人困惑的错误: java.lang.UnsupportedClassVersionError: class file has wrong version 61.0, shou…

2026/9/10 18:47:43 阅读更多 →

最新新闻

USSD应用接口协议全解析:从拨号到菜单的链路与落地实践

USSD应用接口协议全解析:从拨号到菜单的链路与落地实践

简介:这是一份中国移动通信企业标准《USSD应用接口协议》规范文档,属于USSD系列标准之一,面向移动通信网络规划、设备选型、工程设计及运行维护人员,用于明确900/1800MHz TDMA数字蜂窝移动通信网中非结构化补充业务数据的业务流程…

2026/9/30 7:43:26 阅读更多 →
ESXi 6.5安装避坑指南:镜像选择、引导盘与网络配置详解

ESXi 6.5安装避坑指南:镜像选择、引导盘与网络配置详解

简介:这是面向DELL服务器部署的VMware ESXi 6.5安装配置教程文档,尤其适合刚接触虚拟化、需要自行搭建实验环境的运维或系统管理员。资源为docx格式,仅1个文档文件,压缩包大小1.26MB,轻量易用。文档从制作U盘启动介质开…

2026/9/30 7:43:25 阅读更多 →
DeepSeek对话一键导出:油猴脚本实现原始数据自由

DeepSeek对话一键导出:油猴脚本实现原始数据自由

DeepSeek 网页版用多了,最让人难受的不是模型答得不好,而是好不容易调出来的一轮高质量对话,复制出来全是乱的。代码块换行丢、标题层级跟网页样式纠缠在一起、复制到 Markdown 编辑器里还要手动清理半天。更要命的是几十轮对话翻下来&#x…

2026/9/30 7:43:25 阅读更多 →
批量文件名大小写转换:4种跨平台实用方法

批量文件名大小写转换:4种跨平台实用方法

拍了一堆照片、下了一堆资料、拷了一堆项目文件,打开文件夹一看, DSC_0234.JPG 、 dsc_0234.jpg 、 ReadMe.txt 、 README.txt 混在一起,强迫症当场就犯了。更麻烦的是,有些程序只认固定大小写的文件名,名字里…

2026/9/30 7:43:25 阅读更多 →
OpenClaw部署安全指南:从权限最小化到提示注入防护

OpenClaw部署安全指南:从权限最小化到提示注入防护

1. OpenClaw部署前,先想清楚它到底会触碰什么 如果你和我一样,最近把OpenClaw这类AI智能体装到自己的终端或云服务器上,第一周大概率会有一种“终于有真数字助理”的快感:它读文档、整理文件、调API、写脚本、回消息,几…

2026/9/30 7:43:25 阅读更多 →
AI辅助本科毕业论文全流程:8个实战工具与避坑指南

AI辅助本科毕业论文全流程:8个实战工具与避坑指南

又到一年毕业季,后台被师弟师妹问爆了:AI写论文到底靠不靠谱?说实话,我刚带完一届本科生的毕业设计,见过太多人要么完全不敢用AI,要么直接甩给AI生成全文然后被导师约谈。这两类都不可取。我的真实观点是&a…

2026/9/30 7:42:25 阅读更多 →

日新闻

Base64 图片头部特征识别:从文件头到格式判断的完整指南

Base64 图片头部特征识别:从文件头到格式判断的完整指南

1. 项目概述:为什么说看懂 base64 图片头部是基本功这几年跟 base64 打交道的机会越来越多,后端接口返回图片、前端渲染验证码、小程序里存小图、还有一些老系统导出报表,动不动就给你一段长到怀疑人生的 base64 字符串。很多人拿到字符串就直…

2026/9/30 0:00:35 阅读更多 →
Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

简介:本资源是一份面向Java初学者与课程设计学生的公交站牌广告灯箱管理系统毕业设计文档,聚焦城市公共广告资源信息化管理痛点,提供从需求分析到技术实现的完整方案。文档采用标准学术论文结构,含摘要、英文摘要、目录及五章正文…

2026/9/30 0:00:35 阅读更多 →
用 Redis Lua 构建大模型 API 多租户原子配额治理体系

用 Redis Lua 构建大模型 API 多租户原子配额治理体系

我去年年底接了一个内部 AI 平台的治理需求,背景很直接:公司把 DeepSeek、MiniMax 这类大模型 API 统一封装成内部网关,开放给几个业务团队用。结果第一个月账单出来,额度直接超了 4 倍。仔细查日志,发现原因并不复杂—…

2026/9/30 0:00:35 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 8:16:59 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 16:41:41 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/29 8:24:48 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/29 19:29:29 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/29 5:58:00 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/29 3:55:56 阅读更多 →