文件最后提交记录最后更新时间
fix: 修复Session GC对default allocator所有权管理缺陷;Python Tensor.to_host 去 padding Co-authored-by: du-hua1024<duhua2@huawei.com> # message auto-generated for no-merge-commit merge: !2297 merge master into develop fix: 修复Session GC对default allocator所有权管理缺陷;Python Tensor.to_host 去 padding Created-by: du-hua1024 Commit-by: du-hua1024 Merged-by: cann-robot Description: # Pull Request ## 描述 1、修复 Python Session 析构(GC)时 default allocator 清理的所有权管理问题。 run_graph_with_stream_async() 在某个 stream 未注册 external allocator 时,会自动注册 Python default allocator。旧逻辑仅在当前 session 的 _default_allocator_streams 中记录 stream,析构时按 stream 直接 unregister,无法判断当前 stream 上的 allocator 是否仍然是该 session 当初自动注册的 default allocator,可能误删后续由其他 session 注册的 allocator。 ### 需要覆盖的关键场景 1. **同一 session 覆盖** session1 自动注册 default_A 后,又通过 register_external_allocator() 注册 custom allocator。析构时不能再按 default 清理该 stream。 2. **跨 session custom 覆盖** session1 自动注册 default_A,随后 session2 注册 custom_Bsession1 析构时不能 unregister custom_B。 3. **跨 session default 重新归属** session1 注册 default_Asession2 注册 custom_Bsession3 再次触发自动注册 default_C。此时 session1 析构时不能因为当前 stream 仍是 default allocator 就误删 session3default_C。 4. **Python wrapper 外部改动兜底** 如果 C++ 侧或其他 wrapper 路径绕过 Python 状态表修改了 stream allocator,析构前还需要通过 C wrapper 查询当前 allocator 是否仍为 Python default allocator,避免仅依赖 Python 侧记录。 5. **并发窗口** HasDefaultAllocator -> unregister 属于 check-then-act 操作。Python wrapper 内通过同一把锁串行化 default 注册、custom 注册、显式注销和析构清理,避免判断后被其他 Python session 插入注册操作。 ### 设计方案 - 引入 _stream_to_default_allocator_owner:维护 stream -> weakref(session),记录当前 stream 的 Python default allocator 归属 session。 - register_external_allocator() 成功后清理当前 stream 的 default owner 记录,表示该 stream 已被 custom allocator 覆盖。 - _ensure_default_allocator() 成功注册 default allocator 后,记录当前 session 为该 stream 的 default owner。 - Session.__del__() 提取 _unregister_default_allocators(),析构时仅在以下条件同时满足时才 unregister: - 当前 session 仍是该 stream 的 default owner; - C++ 当前 allocator 仍是 Python default allocator。 - 新增 _default_allocator_lock,保护 Python wrapper 内 allocator 注册/注销和 owner 状态更新。 - 新增 GeApiWrapper_HasDefaultAllocator C wrapper API,通过 PyCallbackAllocator::is_default_allocator 区分 Python default allocator 和 Python custom allocator。 - register / unregister 相关路径仅在 C 侧返回成功(ret == 0)后同步更新 Python 侧状态。 2、Python Tensor.to_host 去 padding ### 背景 Python 异步执行接口使用 default allocator 创建输出 Tensor 时,runtime 侧 device buffer 可能按 allocator 规则进行对齐,Tensor::GetSize() 表示实际 storage size,可能大于 shape 对应的逻辑数据大小。例如 [2, 3] 的 FP32 输出逻辑上只有 6 个元素,但 storage size 对齐后可能被 Python 侧按 16 个元素解析,导致 Length of list 16 does not match shape 6。 之前由于 TensorValueUtils::ConvertTensorValue是按 shape大小取的值,所以问题没有暴露出来,当前TensorValueUtils::ConvertTensorValue修改为按 Tensor data size 算元素数,所以后面 Tensor._unflatten 就发现shape和data个数对不上了。 ### 修改方案 - GeApiWrapper_Tensor_ToHost 不再把 device tensor 的 storage size 直接作为 host data size。 - 通过 tensor desc 的 shape、format 和 dtype 调用 TensorUtils::CalcTensorMemSize 计算 host 可见的逻辑数据大小,只拷贝有效数据。 - Tensor::GetSize() 仅作为 device storage 上限校验:当 storage size 小于逻辑大小时返回失败,避免越界拷贝。 - logical size 为 0 时清空 host data 并设置 placement 为 host;只有 logical size 大于 0 时才检查 device pointer 并执行 D2H 拷贝。 ### 附带清理 本 PR 还保留了 examples/offline_compile_run/cpp 中两处魔数清理: - kArgIndexAfterCommand - kInferOutputFloatsPerLine 该清理不影响核心 bug 修复逻辑,属于同 PR 附带的代码规范改进。 ## 变更类型 - [x] 🐛 Bug 修复 - [ ] ✨ 新功能 - [ ] 💄 代码风格更新(格式化,局部变量) - [ ] ♻️ 重构(既不修复错误也不增加功能的代码变动) - [ ] 📦 构建过程或辅助工具的变动 - [ ] 📝 文档内容更新 ## 关联的Issue ## 如何测试 新增/更新 Python session allocator 相关单元测试,覆盖: 1. custom allocator 覆盖 default allocator 后,Python default owner 状态被清理; 2. 析构时只清理当前 session 仍持有的 default allocator; 3. 当前 stream owner 已变更为其他 session 时,旧 session 析构不 unregister; 4. C++/wrapper 绕过 Python owner 状态导致当前 allocator 不再是 default 时,析构不 unregister; 5. C 侧 unregister 失败时,不提前清理 Python 侧状态。 已执行基础检查: ```bash python3 -m py_compile api/python/ge/ge/session/session.py tests/ge/ut/ge/graph/pyge_tests/session_test.py git diff --check ``` ## 核对清单 - [x] 我的代码遵循了项目的代码风格 - [x] 我已对代码进行了自测 - [x] 我已更新了相关的文档 - [x] 我在标题中使用了合适的类型标签(如:feat:, fix:) - [x] 我已经详细阅读了贡献指南(CONTRIBUTING.md),并遵守了其中的所有规定,包括但不限于commit message的格式、无效commit的合并等 ## 其他信息 See merge request: cann/ge!22971 个月前
feat: 新增离线图编译执行 C++ 样例并修复 allocator 回调释放问题 Co-authored-by: du-hua1024<duhua2@huawei.com> # message auto-generated for no-merge-commit merge: !1955 merge tmp2 into develop feat: 新增离线图编译执行 C++ 样例并修复 allocator 回调释放问题 Created-by: du-hua1024 Commit-by: du-hua1024 Merged-by: cann-robot Description: # Pull Request ## 描述 本 PR 包含三类变更: ### 1. 新增 GE C++ 离线图编译执行样例(examples/offline_compile_run/cpp/) - 覆盖单模型(Add 图)和 Bundle 模型(Add + Mul 图)两种场景 - 提供完整的 CMake 构建脚本(CMakeLists.txt)和一键执行脚本(run_sample.sh),支持 build_modelrun_infersample_and_run 等多个 target - 核心 API 流程:aclgrphBuildInitializeaclgrphBuildModel/aclgrphBundleBuildModelaclgrphSaveModel/aclgrphBundleSaveModelaclgrphBuildFinalize,推理通过 aclmdlLoadFromFile/aclmdlBundleLoadFromFile 执行 - 补充 README,与 Python 样例文档结构对齐 ### 2. 编码规范整改 - **ge_api_c_wrapper/c_offline_compile.cc + ge_api_c_wrapper_utils.h**:为离线编译 C Wrapper 接口的指针参数补充 const 修饰(char**const char**Graph*const Graph*int*const int*),同时修正函数声明的参数对齐格式,提升接口语义准确性 - **ir_proto_codec.cc**:将 GET_ATTR_JSON_FUNC(Str, std::string) 宏展开为显式函数,增加 AttrUtils::GetStr 返回空指针时的 null 检查,避免潜在的空指针解引用 - **examples/custom_es_api/run_sample.sh**:添加 set -e,确保脚本在任意命令失败时立即退出,增强健壮性 ### 3. 修复 allocator 回调对象提前释放的问题(_allocator_callback_adapter.py) - 原代码在 _on_allocator_destroy 中直接 pop 丢弃回调对象,未触发回调自身的析构逻辑 - 修复后先取出回调对象再显式调用清理,确保 C++ 侧 ~PyCallbackAllocator 触发时 Python 资源正常释放 ## 变更类型 - [ ] 🐛 Bug 修复 - [x] ✨ 新功能 - [ ] 💄 代码风格更新(格式化,局部变量) - [ ] ♻️ 重构(既不修复错误也不增加功能的代码变动) - [ ] 📦 构建过程或辅助工具的变动 - [x] 📝 文档内容更新 ## 关联的Issue 暂无 ## 如何测试 1. 安装 CANN toolkit 和 ops 包,执行 source /usr/local/Ascend/cann/set_env.sh 2. 进入 examples/offline_compile_run/cpp/ 目录 3. 运行 bash run_sample.sh -t sample_and_run,验证单模型编译与推理成功 4. 运行 bash run_sample.sh -t sample_and_run_bundle,验证 Bundle 编译与推理成功 5. 验证 add_sample.ombundle_sample.om 文件生成 6. 验证 allocator 回调修复:在有自定义 allocator 的场景下,析构时不再出现资源泄漏 ## 核对清单 - [x] 我的代码遵循了项目的代码风格 - [x] 我已对代码进行了自测 - [x] 我已更新了相关的文档 - [x] 我在标题中使用了合适的类型标签(如:feat:, fix:) - [x] 我已经详细阅读了贡献指南(CONTRIBUTING.md),并遵守了其中的所有规定,包括但不限于commit message的格式、无效commit的合并等 ## 其他信息 - C++ 样例与 Python 样例生成的 om 文件同名(add_sample.ombundle_sample.om),请勿在同一目录下混用 - run_sample.sh 使用 CMake 增量编译,多次执行不会清空构建目录 See merge request: cann/ge!19551 个月前
docs: 修正 example README 中 quick_install 链接路径 Co-authored-by: du-hua1024<duhua2@huawei.com> # message auto-generated for no-merge-commit merge: !2423 merge master into develop docs: 修正 example README 中 quick_install 链接路径 Created-by: du-hua1024 Commit-by: du-hua1024 Merged-by: cann-robot Description: # Pull Request ## 描述 本次 PR 修正 examples/es/operator_overload_async/python/README.md 中安装指导链接的相对路径。 README 位于 examples/es/operator_overload_async/python/ 目录下,原文使用 ../../../docs/quick_install.md 无法正确跳转到仓库根目录下的 docs/quick_install.md。 本次将链接统一修正为 ../../../../docs/quick_install.md,确保读者可以从样例文档直接打开安装指导。 ## 变更类型 - [ ] 🐛 Bug 修复 - [ ] ✨ 新功能 - [ ] 💄 代码风格更新(格式化,局部变量) - [ ] ♻️ 重构(既不修复错误也不增加功能的代码变动) - [ ] 📦 构建过程或辅助工具的变动 - [x] 📝 文档内容更新 ## 关联的Issue 无 ## 如何测试 1. 打开 examples/es/operator_overload_async/python/README.md。 2. 检查“3.1、准备cann包”中的两个“安装指导”链接,确认相对路径均为 ../../../../docs/quick_install.md。 3. 在 Markdown 预览中验证链接可正确跳转到仓库根目录下的 docs/quick_install.md。 ## 核对清单 - [x] 我的代码遵循了项目的代码风格 - [x] 我已对代码进行了自测 - [x] 我已更新了相关的文档 - [x] 我在标题中使用了合适的类型标签(如:feat:, fix:) - [x] 我已经详细阅读了贡献指南(CONTRIBUTING.md),并遵守了其中的所有规定,包括但不限于commit message的格式、无效commit的合并等 ## 其他信息 本次变更仅涉及文档链接修正,不涉及功能代码逻辑。 See merge request: cann/ge!24231 个月前
fix: 修复Session GC对default allocator所有权管理缺陷;Python Tensor.to_host 去 padding Co-authored-by: du-hua1024<duhua2@huawei.com> # message auto-generated for no-merge-commit merge: !2297 merge master into develop fix: 修复Session GC对default allocator所有权管理缺陷;Python Tensor.to_host 去 padding Created-by: du-hua1024 Commit-by: du-hua1024 Merged-by: cann-robot Description: # Pull Request ## 描述 1、修复 Python Session 析构(GC)时 default allocator 清理的所有权管理问题。 run_graph_with_stream_async() 在某个 stream 未注册 external allocator 时,会自动注册 Python default allocator。旧逻辑仅在当前 session 的 _default_allocator_streams 中记录 stream,析构时按 stream 直接 unregister,无法判断当前 stream 上的 allocator 是否仍然是该 session 当初自动注册的 default allocator,可能误删后续由其他 session 注册的 allocator。 ### 需要覆盖的关键场景 1. **同一 session 覆盖** session1 自动注册 default_A 后,又通过 register_external_allocator() 注册 custom allocator。析构时不能再按 default 清理该 stream。 2. **跨 session custom 覆盖** session1 自动注册 default_A,随后 session2 注册 custom_Bsession1 析构时不能 unregister custom_B。 3. **跨 session default 重新归属** session1 注册 default_Asession2 注册 custom_Bsession3 再次触发自动注册 default_C。此时 session1 析构时不能因为当前 stream 仍是 default allocator 就误删 session3default_C。 4. **Python wrapper 外部改动兜底** 如果 C++ 侧或其他 wrapper 路径绕过 Python 状态表修改了 stream allocator,析构前还需要通过 C wrapper 查询当前 allocator 是否仍为 Python default allocator,避免仅依赖 Python 侧记录。 5. **并发窗口** HasDefaultAllocator -> unregister 属于 check-then-act 操作。Python wrapper 内通过同一把锁串行化 default 注册、custom 注册、显式注销和析构清理,避免判断后被其他 Python session 插入注册操作。 ### 设计方案 - 引入 _stream_to_default_allocator_owner:维护 stream -> weakref(session),记录当前 stream 的 Python default allocator 归属 session。 - register_external_allocator() 成功后清理当前 stream 的 default owner 记录,表示该 stream 已被 custom allocator 覆盖。 - _ensure_default_allocator() 成功注册 default allocator 后,记录当前 session 为该 stream 的 default owner。 - Session.__del__() 提取 _unregister_default_allocators(),析构时仅在以下条件同时满足时才 unregister: - 当前 session 仍是该 stream 的 default owner; - C++ 当前 allocator 仍是 Python default allocator。 - 新增 _default_allocator_lock,保护 Python wrapper 内 allocator 注册/注销和 owner 状态更新。 - 新增 GeApiWrapper_HasDefaultAllocator C wrapper API,通过 PyCallbackAllocator::is_default_allocator 区分 Python default allocator 和 Python custom allocator。 - register / unregister 相关路径仅在 C 侧返回成功(ret == 0)后同步更新 Python 侧状态。 2、Python Tensor.to_host 去 padding ### 背景 Python 异步执行接口使用 default allocator 创建输出 Tensor 时,runtime 侧 device buffer 可能按 allocator 规则进行对齐,Tensor::GetSize() 表示实际 storage size,可能大于 shape 对应的逻辑数据大小。例如 [2, 3] 的 FP32 输出逻辑上只有 6 个元素,但 storage size 对齐后可能被 Python 侧按 16 个元素解析,导致 Length of list 16 does not match shape 6。 之前由于 TensorValueUtils::ConvertTensorValue是按 shape大小取的值,所以问题没有暴露出来,当前TensorValueUtils::ConvertTensorValue修改为按 Tensor data size 算元素数,所以后面 Tensor._unflatten 就发现shape和data个数对不上了。 ### 修改方案 - GeApiWrapper_Tensor_ToHost 不再把 device tensor 的 storage size 直接作为 host data size。 - 通过 tensor desc 的 shape、format 和 dtype 调用 TensorUtils::CalcTensorMemSize 计算 host 可见的逻辑数据大小,只拷贝有效数据。 - Tensor::GetSize() 仅作为 device storage 上限校验:当 storage size 小于逻辑大小时返回失败,避免越界拷贝。 - logical size 为 0 时清空 host data 并设置 placement 为 host;只有 logical size 大于 0 时才检查 device pointer 并执行 D2H 拷贝。 ### 附带清理 本 PR 还保留了 examples/offline_compile_run/cpp 中两处魔数清理: - kArgIndexAfterCommand - kInferOutputFloatsPerLine 该清理不影响核心 bug 修复逻辑,属于同 PR 附带的代码规范改进。 ## 变更类型 - [x] 🐛 Bug 修复 - [ ] ✨ 新功能 - [ ] 💄 代码风格更新(格式化,局部变量) - [ ] ♻️ 重构(既不修复错误也不增加功能的代码变动) - [ ] 📦 构建过程或辅助工具的变动 - [ ] 📝 文档内容更新 ## 关联的Issue ## 如何测试 新增/更新 Python session allocator 相关单元测试,覆盖: 1. custom allocator 覆盖 default allocator 后,Python default owner 状态被清理; 2. 析构时只清理当前 session 仍持有的 default allocator; 3. 当前 stream owner 已变更为其他 session 时,旧 session 析构不 unregister; 4. C++/wrapper 绕过 Python owner 状态导致当前 allocator 不再是 default 时,析构不 unregister; 5. C 侧 unregister 失败时,不提前清理 Python 侧状态。 已执行基础检查: ```bash python3 -m py_compile api/python/ge/ge/session/session.py tests/ge/ut/ge/graph/pyge_tests/session_test.py git diff --check ``` ## 核对清单 - [x] 我的代码遵循了项目的代码风格 - [x] 我已对代码进行了自测 - [x] 我已更新了相关的文档 - [x] 我在标题中使用了合适的类型标签(如:feat:, fix:) - [x] 我已经详细阅读了贡献指南(CONTRIBUTING.md),并遵守了其中的所有规定,包括但不限于commit message的格式、无效commit的合并等 ## 其他信息 See merge request: cann/ge!22971 个月前
feat: 新增离线图编译执行 C++ 样例并修复 allocator 回调释放问题 Co-authored-by: du-hua1024<duhua2@huawei.com> # message auto-generated for no-merge-commit merge: !1955 merge tmp2 into develop feat: 新增离线图编译执行 C++ 样例并修复 allocator 回调释放问题 Created-by: du-hua1024 Commit-by: du-hua1024 Merged-by: cann-robot Description: # Pull Request ## 描述 本 PR 包含三类变更: ### 1. 新增 GE C++ 离线图编译执行样例(examples/offline_compile_run/cpp/) - 覆盖单模型(Add 图)和 Bundle 模型(Add + Mul 图)两种场景 - 提供完整的 CMake 构建脚本(CMakeLists.txt)和一键执行脚本(run_sample.sh),支持 build_modelrun_infersample_and_run 等多个 target - 核心 API 流程:aclgrphBuildInitializeaclgrphBuildModel/aclgrphBundleBuildModelaclgrphSaveModel/aclgrphBundleSaveModelaclgrphBuildFinalize,推理通过 aclmdlLoadFromFile/aclmdlBundleLoadFromFile 执行 - 补充 README,与 Python 样例文档结构对齐 ### 2. 编码规范整改 - **ge_api_c_wrapper/c_offline_compile.cc + ge_api_c_wrapper_utils.h**:为离线编译 C Wrapper 接口的指针参数补充 const 修饰(char**const char**Graph*const Graph*int*const int*),同时修正函数声明的参数对齐格式,提升接口语义准确性 - **ir_proto_codec.cc**:将 GET_ATTR_JSON_FUNC(Str, std::string) 宏展开为显式函数,增加 AttrUtils::GetStr 返回空指针时的 null 检查,避免潜在的空指针解引用 - **examples/custom_es_api/run_sample.sh**:添加 set -e,确保脚本在任意命令失败时立即退出,增强健壮性 ### 3. 修复 allocator 回调对象提前释放的问题(_allocator_callback_adapter.py) - 原代码在 _on_allocator_destroy 中直接 pop 丢弃回调对象,未触发回调自身的析构逻辑 - 修复后先取出回调对象再显式调用清理,确保 C++ 侧 ~PyCallbackAllocator 触发时 Python 资源正常释放 ## 变更类型 - [ ] 🐛 Bug 修复 - [x] ✨ 新功能 - [ ] 💄 代码风格更新(格式化,局部变量) - [ ] ♻️ 重构(既不修复错误也不增加功能的代码变动) - [ ] 📦 构建过程或辅助工具的变动 - [x] 📝 文档内容更新 ## 关联的Issue 暂无 ## 如何测试 1. 安装 CANN toolkit 和 ops 包,执行 source /usr/local/Ascend/cann/set_env.sh 2. 进入 examples/offline_compile_run/cpp/ 目录 3. 运行 bash run_sample.sh -t sample_and_run,验证单模型编译与推理成功 4. 运行 bash run_sample.sh -t sample_and_run_bundle,验证 Bundle 编译与推理成功 5. 验证 add_sample.ombundle_sample.om 文件生成 6. 验证 allocator 回调修复:在有自定义 allocator 的场景下,析构时不再出现资源泄漏 ## 核对清单 - [x] 我的代码遵循了项目的代码风格 - [x] 我已对代码进行了自测 - [x] 我已更新了相关的文档 - [x] 我在标题中使用了合适的类型标签(如:feat:, fix:) - [x] 我已经详细阅读了贡献指南(CONTRIBUTING.md),并遵守了其中的所有规定,包括但不限于commit message的格式、无效commit的合并等 ## 其他信息 - C++ 样例与 Python 样例生成的 om 文件同名(add_sample.ombundle_sample.om),请勿在同一目录下混用 - run_sample.sh 使用 CMake 增量编译,多次执行不会清空构建目录 See merge request: cann/ge!19551 个月前
README.md

样例使用指导

1、功能描述

本样例演示离线图编译执行的流程,关于编译图为离线模型更多信息,请参考 生成离线模型

2、目录结构

cpp/
├── CMakeLists.txt           // CMake 构建文件
├── main.cpp                 // 程序主入口
├── run_sample.sh            // 执行脚本
├── README.md                // README 文件
└── src/
    ├── CMakeLists.txt            // CMake 构建文件
    ├── common.h / common.cpp     // 公共逻辑文件
    ├── single_model/             // 单模型编译与推理
    └── bundle_model/             // Bundle 编译与推理

3、使用方法

3.1、准备cann包

  • 通过 环境准备 中“方式三:手动安装软件包 > 场景1:体验master版本能力或基于master版本进行开发”,正确安装toolkitops
  • 设置环境变量 (假设包安装在/usr/local/Ascend/)
source /usr/local/Ascend/cann/set_env.sh 

3.2、图编译和图执行

执行单模型样例:

bash run_sample.sh -t sample_and_run

该命令会:

  1. 编译 C++ 可执行程序
  2. 构建 Add 图,离线编译并生成 add_sample.om
  3. 加载、执行该离线模型

执行 bundle 样例:

bash run_sample.sh -t sample_and_run_bundle

该命令会:

  1. 编译 C++ 可执行程序
  2. Add 图与 Mul 图打成 Bundle,离线编译并生成 bundle_sample.om
  3. 加载 Bundle,并分别执行两个子模型

离线编译在无卡场景下如需指定目标芯片版本,可增加 --soc-version

bash run_sample.sh -s Ascend910B1 -t sample_and_run
bash run_sample.sh -s Ascend910B1 -t sample_and_run_bundle

也可以拆分为"只做图编译"和"只做图执行"两个阶段:

bash run_sample.sh -t build_model
bash run_sample.sh -t run_infer

bash run_sample.sh -t build_bundle_model
bash run_sample.sh -t run_bundle_infer

run_infer / run_bundle_infer 要求 om 模型已存在

执行成功后会看到:

[Success] sample 执行成功

输出文件说明

执行成功后会在当前目录生成以下文件:

  • add_sample.om - 单模型离线文件
  • bundle_sample.om - Bundle 离线模型文件

3.3、日志打印

可执行程序执行过程中如果需要日志打印来辅助定位,可以在 bash run_sample.sh 之前设置如下环境变量来让日志打印到屏幕

export ASCEND_SLOG_PRINT_TO_STDOUT=1 #日志打印到屏幕
export ASCEND_GLOBAL_LOG_LEVEL=0 #日志级别为debug级别

4、核心流程介绍

4.1、单模型离线编译执行

  • 使用 aclgrphBuildInitialize 初始化编译环境
  • 构建 Graph 并通过 aclgrphBuildModel 生成离线模型
  • 使用 aclgrphSaveModel 保存 om 文件
  • 通过 aclmdlLoadFromFileaclmdlExecute 执行离线模型

4.2、Bundle 离线编译执行

  • 使用多个 Graph 组织 Bundle
  • 通过 aclgrphBundleBuildModel 一次性构建 Bundle 模型
  • 使用 aclgrphBundleSaveModel 保存 bundle_sample.om
  • 通过 aclmdlBundleLoadFromFile 加载 Bundle,并逐个执行子模型