ACL API 的 stream 为空行为机制说明

1. 涉及的 15 个 ACL 接口

本文分析的 15 个 ACL 接口均包含 aclrtStream stream 入参。调用者可在语法上向该参数传入 NULL(C)或 nullptr(C++);在 ACL Runtime 通用语义中,NULL 表示使用当前 Context 的默认 Stream,但具体接口及产品型号是否支持仍以对应 API 约束为准。各接口的函数签名及声明所在的头文件如下表所示。

序号 接口名称 函数签名 对应的 .h 文件路径
1 aclopExecute(已废弃) aclError aclopExecute(const char *opType, int numInputs, const aclTensorDesc *const inputDesc[], const aclDataBuffer *const inputs[], int numOutputs, const aclTensorDesc *const outputDesc[], aclDataBuffer *const outputs[], const aclopAttr *attr, aclrtStream stream) ge/inc/external/acl/acl_op.h:265-268
2 aclopExecuteV2 aclError aclopExecuteV2(const char *opType, int numInputs, aclTensorDesc *inputDesc[], aclDataBuffer *inputs[], int numOutputs, aclTensorDesc *outputDesc[], aclDataBuffer *outputs[], aclopAttr *attr, aclrtStream stream) ge/inc/external/acl/acl_op.h:298-300
3 aclopExecWithHandle aclError aclopExecWithHandle(aclopHandle *handle, int numInputs, const aclDataBuffer *const inputs[], int numOutputs, aclDataBuffer *const outputs[], aclrtStream stream) ge/inc/external/acl/acl_op.h:352-354
4 aclopCast aclError aclopCast(const aclTensorDesc *srcDesc, const aclDataBuffer *srcBuffer, const aclTensorDesc *dstDesc, aclDataBuffer *dstBuffer, uint8_t truncate, aclrtStream stream) ge/inc/external/acl/acl_op.h:370-372
5 aclopCompileAndExecute aclError aclopCompileAndExecute(const char *opType, int numInputs, const aclTensorDesc *const inputDesc[], const aclDataBuffer *const inputs[], int numOutputs, const aclTensorDesc *const outputDesc[], aclDataBuffer *const outputs[], const aclopAttr *attr, aclopEngineType engineType, aclopCompileType compileFlag, const char *opPath, aclrtStream stream) ge/inc/external/acl/acl_op_compiler.h:91-94
6 aclopCompileAndExecuteV2 aclError aclopCompileAndExecuteV2(const char *opType, int numInputs, aclTensorDesc *inputDesc[], aclDataBuffer *inputs[], int numOutputs, aclTensorDesc *outputDesc[], aclDataBuffer *outputs[], aclopAttr *attr, aclopEngineType engineType, aclopCompileType compileFlag, const char *opPath, aclrtStream stream) ge/inc/external/acl/acl_op_compiler.h:117-122
7 aclblasGemvEx aclError aclblasGemvEx(aclTransType transA, int m, int n, const void *alpha, const void *a, int lda, aclDataType dataTypeA, const void *x, int incx, aclDataType dataTypeX, const void *beta, void *y, int incy, aclDataType dataTypeY, aclComputeType type, aclrtStream stream) ge/inc/external/acl/ops/acl_cblas.h:53-56
8 aclblasHgemv aclError aclblasHgemv(aclTransType transA, int m, int n, const aclFloat16 *alpha, const aclFloat16 *a, int lda, const aclFloat16 *x, int incx, const aclFloat16 *beta, aclFloat16 *y, int incy, aclComputeType type, aclrtStream stream) ge/inc/external/acl/ops/acl_cblas.h:100-103
9 aclblasS8gemv aclError aclblasS8gemv(aclTransType transA, int m, int n, const int32_t *alpha, const int8_t *a, int lda, const int8_t *x, int incx, const int32_t *beta, int32_t *y, int incy, aclComputeType type, aclrtStream stream) ge/inc/external/acl/ops/acl_cblas.h:144-146
10 aclblasGemmEx aclError aclblasGemmEx(aclTransType transA, aclTransType transB, aclTransType transC, int m, int n, int k, const void *alpha, const void *matrixA, int lda, aclDataType dataTypeA, const void *matrixB, int ldb, aclDataType dataTypeB, const void *beta, void *matrixC, int ldc, aclDataType dataTypeC, aclComputeType type, aclrtStream stream) ge/inc/external/acl/ops/acl_cblas.h:193-197
11 aclblasHgemm aclError aclblasHgemm(aclTransType transA, aclTransType transB, aclTransType transC, int m, int n, int k, const aclFloat16 *alpha, const aclFloat16 *matrixA, int lda, const aclFloat16 *matrixB, int ldb, const aclFloat16 *beta, aclFloat16 *matrixC, int ldc, aclComputeType type, aclrtStream stream) ge/inc/external/acl/ops/acl_cblas.h:250-253
12 aclblasS8gemm aclError aclblasS8gemm(aclTransType transA, aclTransType transB, aclTransType transC, int m, int n, int k, const int32_t *alpha, const int8_t *matrixA, int lda, const int8_t *matrixB, int ldb, const int32_t *beta, int32_t *matrixC, int ldc, aclComputeType type, aclrtStream stream) ge/inc/external/acl/ops/acl_cblas.h:301-304
13 aclmdlExecuteV2 aclError aclmdlExecuteV2(uint32_t modelId, const aclmdlDataset *input, aclmdlDataset *output, aclrtStream stream, const aclmdlExecConfigHandle *handle) ge/inc/external/acl/acl_mdl.h:850-851
14 aclmdlExecuteAsync aclError aclmdlExecuteAsync(uint32_t modelId, const aclmdlDataset *input, aclmdlDataset *output, aclrtStream stream) ge/inc/external/acl/acl_mdl.h:883-884
15 aclmdlExecuteAsyncV2 aclError aclmdlExecuteAsyncV2(uint32_t modelId, const aclmdlDataset *input, aclmdlDataset *output, aclrtStream stream, const aclmdlExecConfigHandle *handle) ge/inc/external/acl/acl_mdl.h:866-867

2. 各类接口的 stream 为 nullptr 行为机制分析

2.1 单算子执行类(aclopExecute / aclopExecuteV2 / aclopExecWithHandle / aclopCast)

全局前提:以下分析均假设当前线程已设置有效 Context。stream=nullptr 时,3 条分支中多处调用 aclrtCtxGetCurrentDefaultStream 获取默认 Stream,若当前线程没有有效 Context,该调用失败并直接返回错误。此外,aclopExecuteaclopExecuteV2aclopExecWithHandle 官方均说明多线程场景不支持使用默认 Stream(即 stream=nullptr),否则可能任务执行异常。

本类 4 个接口的 stream 参数首先汇聚到 acl::OpExecutor::ExecuteAsync 的两个重载,而非同一个函数。其中 aclopExecuteaclopExecuteV2aclopCast 三个接口汇聚到 AclOp 重载 [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:410],aclopExecWithHandle 走 OpHandle 重载 [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:515]。汇聚关系如下图所示:

flowchart TD
    A1["aclopExecute(已废弃)<br/>[ge/api/acl/acl_op_executor/single_op/acl_op_executor.cpp:78]"]
    A2["aclopExecuteV2<br/>[ge/api/acl/acl_op_executor/single_op/acl_op_executor.cpp:84]"]
    A3["aclopExecWithHandle<br/>[ge/api/acl/acl_op_executor/single_op/acl_op_executor.cpp:99]"]
    A4["aclopCast<br/>[ge/api/acl/acl_op_executor/single_op/acl_op_executor.cpp:104]"]

    B1["aclopExecuteImpl<br/>[ge/api/acl/acl_op_executor/single_op/op.cpp:186]"]
    B2["aclopExecuteV2Impl<br/>[ge/api/acl/acl_op_executor/single_op/op.cpp:218]"]
    B3["aclopExecWithHandleImpl<br/>[ge/api/acl/acl_op_executor/single_op/op.cpp:150]"]
    B4["aclopCastImpl<br/>[ge/api/acl/acl_op_executor/single_op/builtin/cast_op.cpp:25]"]

    C1["OpExecutor::ExecuteAsync(AclOp)<br/>[ge/api/acl/acl_op_executor/single_op/op_executor.cpp:410]"]
    C2["OpExecutor::ExecuteAsync(OpHandle)<br/>[ge/api/acl/acl_op_executor/single_op/op_executor.cpp:515]"]

    A1 --> B1
    A2 --> B2
    A3 --> B3
    A4 --> B4
    B1 --> C1
    B2 --> C1
    B4 --> B2
    B3 --> C2

2.1.1 ExecuteAsync(AclOp) 重载的 stream 为 nullptr 行为

该重载 [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:410] 内部按条件分为 3 条分支,stream=nullptr 在每条分支中的处理方式不同。伪代码如下:

OpExecutor::ExecuteAsync(aclOp, inputs, outputs, stream=nullptr):

    // 分支 A:动态内核选择器路径(尝试性,失败则 fall through)
    if OpKernelSelector::HasSelectFunc(aclOp.opType):
        streamExecutor = Executors::GetOrCreate(context, stream=nullptr)
        ret = streamExecutor->ExecuteAsync(aclOp, inputs, outputs)
        // 结论:默认 Stream 仅用作 cache key,rtKernelLaunch 最终仍收到 nullptr
        if ret == ACL_SUCCESS: return ACL_SUCCESS

    // 分支 B:RT2 路径(RuntimeV2 开启且为动态算子)
    if IsRuntimeV2Enable() and isDynamic:
        return DoExecuteAsync(opModel.executor, aclOp, stream=nullptr, isExactModel)
        // 结论:stream=nullptr 没有被真正替换,arg.stream 保持 nullptr

    // 分支 C:RT1 路径(默认或静态模型)
    return DoExecuteRT1(aclOp, inputs, outputs, stream=nullptr, ...)
    // 结论:stream=nullptr 没有被替换,rtKernelLaunchWithFlagV2 收到 nullptr

3 条分支对 stream=nullptr 的处理需要分别分析:

2.1.1.1 分支 A:动态内核选择器路径

OpKernelSelector::HasSelectFunc(aclOp.opType) 为 true 时进入此分支。stream=nullptr 的完整调用栈如下:

OpExecutor::ExecuteAsync(aclOp, inputs, outputs, stream=nullptr)
[ge/api/acl/acl_op_executor/single_op/op_executor.cpp:410]
│  stream=nullptr 作为入参传入
│
└─ Executors::GetOrCreate(context, stream=nullptr)
   [ge/api/acl/acl_op_executor/single_op/executor/stream_executor.cpp:163]
   │  stream==nullptr 时调用 aclrtCtxGetCurrentDefaultStream 获取默认 Stream
   │  默认 Stream 仅用作 cache key 查找/插入 [stream_executor.cpp:165-174, 177, 193]
   │  new StreamExecutor(resMgr, stream) 传入的仍是 nullptr [stream_executor.cpp:187]
   │  即 streamExecutor->stream_ 保持 nullptr
   │
└─ streamExecutor->ExecuteAsync(aclOp, inputs, outputs)
   [ge/api/acl/acl_op_executor/single_op/executor/stream_executor.cpp:47]
   │  签名不含 stream 参数,内部使用 stream_(nullptr)
   │
   ├─ OpKernelSelector::GetOpKernelDesc(aclOp, desc)
   │  [ge/api/acl/acl_op_executor/single_op/executor/stream_executor.cpp:50]
   │  获取 kernel 描述,不涉及 stream
   │
   └─ ExecuteAsync(*desc, numInputs, inputs, numOutputs, outputs)
      [ge/api/acl/acl_op_executor/single_op/executor/stream_executor.cpp:58]
      → [ge/api/acl/acl_op_executor/single_op/executor/stream_executor.cpp:62]
      │
      └─ TbeOpTask::ExecuteAsync(numInputs, inputs, numOutputs, outputs, stream_=nullptr)
         [ge/api/acl/acl_op_executor/single_op/executor/stream_executor.cpp:69]
         → [ge/api/acl/acl_op_executor/single_op/executor/op_task.cpp:28]
         │  stream_ 为 nullptr,透传给 rtKernelLaunch
         │
         └─ rtKernelLaunch(stubFunc, blockDim, args, argSize, nullptr, stream=nullptr)
            [ge/api/acl/acl_op_executor/single_op/executor/op_task.cpp:45]
            最终 Runtime API 收到的 stream 为 nullptr

结论:分支 A 中 stream=nullptr 没有被替换为默认 Stream。Executors::GetOrCreate 虽然调用 aclrtCtxGetCurrentDefaultStream 获取默认 Stream,但仅用于 cache key 查找/插入,StreamExecutor 内部成员 stream_ 保持 nullptr。最终 rtKernelLaunch 收到的 stream 仍是 nullptr,由 Runtime 侧按默认 Stream 语义解释。

2.1.1.2 分支 B:RT2 路径

IsRuntimeV2Enable() 为 true 且 isDynamic 为 true 时进入此分支。stream=nullptr 的完整调用栈如下:

OpExecutor::ExecuteAsync(aclOp, inputs, outputs, stream=nullptr)
[ge/api/acl/acl_op_executor/single_op/op_executor.cpp:410]
│  stream=nullptr 作为入参传入
│
└─ DoExecuteAsync(opModel.executor, aclOp, stream=nullptr, isExactModel)
   [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:444](调用点)→ [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:335](定义点)
   │
   ├─ UpdateExecuteArgs(arg, stream=nullptr)
   │  [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:350]
   │  → [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:29]
   │  arg.stream = stream = nullptr
   │  即 arg.stream 保持 nullptr
   │
   ├─ GetKeyByStreamOrDefaultStream(stream=nullptr)
   │  [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:351]
   │  → [ge/api/acl/acl_op_executor/single_op/acl_op_resource_manager.cpp:63]
   │  stream==nullptr 时调用 aclrtCtxGetCurrentDefaultStream 获取默认 Stream
   │  返回 currentStream = 默认 Stream
   │  currentStream 仅用作下一步 CreateRT2Executor 的 cache key
   │
   ├─ CreateRT2Executor(streamExecutor, currentStream=默认Stream, arg, executor)
   │  [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:354]
   │  → [ge/api/acl/acl_op_executor/single_op/acl_op_resource_manager.cpp:594]
   │  currentStream(默认 Stream)仅用于 cache key 查找/创建 executor
   │
   └─ executor->Execute(arg, ...)
      [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:357]
      → [ge/runtime/v2/core/model_v2_executor.cc:227]
      │  arg.stream 仍为 nullptr
      │
      └─ SpecifyArgsInputs(arg, input_num, graph_executor)
         [ge/runtime/v2/core/model_v2_executor.cc:238]
         → [ge/runtime/v2/core/model_v2_executor.cc:137]
         │
         └─ OccupyStreamResource(arg, ...)
            [ge/runtime/v2/core/model_v2_executor.cc:142]
            → [ge/runtime/v2/core/model_v2_executor.cc:96]
            │  第 123 行:streams->MutableData()[0] = arg.stream
            │  即 streams[0] = nullptr,nullptr 被写入模型执行的第 0 号 stream
            │
         └─ graph_executor.SpecifyInput(streams, input_num)
            [ge/runtime/v2/core/model_v2_executor.cc:143]
            │  streams 数组(含 nullptr)传入 graph_executor
            │
       └─ graph_executor.Execute()
          [ge/runtime/v2/core/model_v2_executor.cc:249]
          → [ge/runtime/v2/core/model_v2_executor.cc:312]
          │  execute_func_(execution_data_)
          │  execute_func_ 由 GraphExecutorBuilder 通过 SetExecuteFunc 设置
          │  [ge/runtime/v2/core/builder/graph_executor_builder.cc:119]
          │  执行器类型由 SelectExecutor 选择,不固定为 SequentialExecute
          │  可选:Sequential / Topological / MultiThreadTopological / PriorityTopological
          │
          └─ execute_func_(execution_data)(以 SequentialExecute 为例)
             [ge/runtime/v2/core/executor/sequential/executor/sequential_executor.c:17]
             │  遍历 execution_data->nodes,对每个 node 调用 node->func(&(node->context))
             │  node->func 是通过 REGISTER_KERNEL 注册的 kernel 函数
             │  stream 从 streams[0](nullptr)经 kernel context 传入各 kernel
             │
             ├─ AICore/DSA/STARS kernel(如 AiCoreLaunchKernelWithHandle 等)
             │  [ge/runtime/v2/engine/aicore/launch_kernel/ai_core_launch_kernel.cc:635/667/711/733/767/797]
             │  [ge/runtime/v2/engine/dsacore/kernel/dsa_core_launch_kernel.cc:127]
             │  [ge/runtime/v2/engine/dsacore/kernel/stars_launch_kernel.cc:49]
             │  FE_ASSERT_NOTNULL(stream) 断言失败 → return ErrorResult()
             │  即 GE 层拦截报错,不会到达 Runtime API
             │
             └─ AICPU kernel(如 AicpuLaunchTfKernel / AicpuLaunchCCKernel 等)
                [ge/runtime/v2/engine/aicpu/kernel/aicpu_launch_kernel.cc:264/332]
                │  无 stream 非空检查(aicpu_ext_info_handle.cc:455 注释 "// stream can be nullptr")
                │  stream=nullptr 直接透传给 Runtime API:
                 │  rtAicpuKernelLaunchExWithArgs / aclrtLaunchKernelWithHostArgs(aicpu_launch_kernel.cc)
                │  是否报错取决于 Runtime 层处理

结论:分支 B 中 stream=nullptr 没有被真正替换为默认 Stream。GetKeyByStreamOrDefaultStream 获取的默认 Stream 仅用作 CreateRT2Executor 的 cache key;arg.stream 保持 nullptr,经 OccupyStreamResource 第 123 行写入 streams[0],透传给 graph_executorgraph_executor.Execute() 通过 execute_func_ 遍历执行各 node(执行器类型不固定),stream=nullptr 的最终行为取决于 kernel 类型:

  • AICore/DSA/STARS kernel:GE 层 FE_ASSERT_NOTNULL(stream) 断言失败,直接返回错误,不会到达 Runtime API。
  • AICPU kernel:无 stream 非空检查,nullptr 直接透传给 Runtime API(rtAicpuKernelLaunchExWithArgs / aclrtLaunchKernelWithHostArgs,aicpu_launch_kernel.cc),是否报错取决于 Runtime 层处理。
2.1.1.3 分支 C:RT1 路径

当不满足分支 B 条件(IsRuntimeV2Enable() 为 false 或 isDynamic 为 false)时进入此分支。stream=nullptr 的完整调用栈如下(以静态算子路径为例):

OpExecutor::ExecuteAsync(aclOp, inputs, outputs, stream=nullptr)
[ge/api/acl/acl_op_executor/single_op/op_executor.cpp:410]
│  stream=nullptr 作为入参传入
│
└─ DoExecuteRT1(aclOp, inputs, outputs, stream=nullptr, ...)
   [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:441/446](调用点)→ [op_executor.cpp:484](定义点)
   │
   ├─ LoadSingleOp(modelInfo, stream=nullptr, &singleOp)
   │  [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:501]
   │  → [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:369]
   │  │
   │  ├─ GetAllocators(stream=nullptr)
   │  │  [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:371]
   │  │  → [ge/api/acl/acl_model/model/acl_resource_manager.cpp:170]
   │  │  stream==nullptr 时调用 GetKeyByStreamOrDefaultStream 获取默认 Stream
   │  │  默认 Stream 仅用作 cache key [acl_resource_manager.cpp:172-175]
   │  │
   │  └─ GeExecutor::LoadSingleOpV2(modelName, modelData, stream=nullptr, singleOp, modelId)
   │     [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:383]
   │     → [ge/runtime/v1/executor/ge_executor.cc:1274]
   │     │
   │     └─ SingleOpManager::GetOpFromModel(modelName, modelData, stream=nullptr, singleOp, modelId)
   │        [ge/runtime/v1/single_op/single_op_manager.cc:22]
   │        │
   │        ├─ GetResourceId(stream=nullptr, resource_id)
   │        │  [ge/runtime/v1/single_op/single_op_manager.cc:32]
   │        │  → [ge/runtime/v1/single_op/single_op_manager.cc:142]
   │        │  stream==nullptr 时调用 aclrtCtxGetCurrentDefaultStream 获取默认 Stream
   │        │  默认 Stream 仅用作 resource_id(cache key)[single_op_manager.cc:144-149]
   │        │
   │        └─ GetResource(resource_id, stream=nullptr)
   │           [ge/runtime/v1/single_op/single_op_manager.cc:33]
   │           → [ge/runtime/v1/single_op/single_op_manager.cc:66]
   │           第 78 行:res->SetStream(stream=nullptr)
   │           注意:SetStream 接收的是原始 nullptr,非 GetResourceId 替换后的 curCtxDefaultStream
   │           即 StreamResource 内部 stream_ 保持 nullptr
   │
   └─ DoExecuteAsync(singleOp, aclOp, inputs, outputs, isExactModel)
      [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:509]
      → [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:151]
      │  签名不含 stream 参数,内部使用 singleOp 绑定的 stream_(nullptr)
      │
      └─ GeExecutor::ExecuteAsync(singleOp, inputVec, outputVec)
         [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:188]
         → [ge/runtime/v1/executor/ge_executor.cc:1297]
         │
         └─ singleOp->ExecuteAsync(inputs, outputs)
            [ge/runtime/v1/single_op/single_op.cc:224]
            │
            └─ impl_->ExecuteAsync(inputs, outputs)
               [ge/runtime/v1/single_op/single_op.cc:226]
               → [ge/runtime/v1/single_op/single_op.cc:356]
               │  SingleOpImpl 构造时 stream_(stream=nullptr) [single_op.cc:256]
               │
               └─ task->LaunchKernel(stream_=nullptr)
                  [ge/runtime/v1/single_op/single_op.cc:396]
                  → [ge/runtime/v1/single_op/task/op_task.cc:383](TbeOpTask::LaunchKernel)
                  │
                  └─ DoLaunchKernel(stream=nullptr)
                     [ge/runtime/v1/single_op/task/op_task.cc:402]
                     → [ge/runtime/v1/single_op/task/op_task.cc:730]
                     │
                     └─ DoLaunchKernelWithArgsEx(stream=nullptr)
                        [ge/runtime/v1/single_op/task/op_task.cc:732]
                        → [ge/runtime/v1/single_op/task/op_task.cc:738]
                        │  第 742 行:rtKernelLaunchWithFlagV2(stubFunc, blockDim, args, smDesc, stream=nullptr, 0, cfg)
                        │  或第 745 行:rtKernelLaunchWithHandleV2(handle, tilingKey, blockDim, args, smDesc, stream=nullptr, cfg)
                        │  最终 Runtime API 收到的 stream 为 nullptr

结论(静态 TBE 路径):分支 C 中 stream=nullptr 没有被真正替换为默认 Stream。GetResourceIdGetAllocators 获取的默认 Stream 仅用作 cache key;StreamResource 内部 stream_ 保持 nullptr,经 SingleOpImpl::ExecuteAsync 第 396 行透传给 TbeOpTask::LaunchKernel,最终 rtKernelLaunchWithFlagV2 / rtKernelLaunchWithHandleV2 收到的 stream 仍是 nullptr,由 Runtime 侧按默认 Stream 语义解释。

注意:以上调用栈仅验证了静态 TBE Task 路径。RT1 路径还包括 DynamicSingleOp、AiCpuTask、MemcpyAsyncTask、MixL2OpTask 等其他 Task 类型,各 Task 的 LaunchKernel 实现不同,stream=nullptr 的行为需分别验证。

2.1.1.4 总体结论

3 条分支对 stream=nullptr 的处理方式对比如下:

分支 触发条件 默认 Stream 用途(cache key → value) 最终到达 stream 实际值
A(动态内核选择器) HasSelectFunc 为 true 默认 Stream → StreamExecutor*Executors::GetOrCreate rtKernelLaunch(Runtime API) nullptr
B(RT2)- AICore/DSA/STARS IsRuntimeV2EnableisDynamic 默认 Stream → gert::ModelV2Executor*CreateRT2Executor GE 层 FE_ASSERT_NOTNULL(stream) 报错 nullptr(写入 streams[0]
B(RT2)- AICPU 同上 同上 rtAicpuKernelLaunchExWithArgs / aclrtLaunchKernelWithHostArgs(Runtime API) nullptr(透传)
C(RT1,静态 TBE) 默认或静态模型 默认 Stream → StreamResource*GetResource,作为 resource_id) rtKernelLaunchWithFlagV2 / rtKernelLaunchWithHandleV2(Runtime API) nullptr

共同结论

  1. 3 条分支均未在 GE 侧将 nullptr 替换为默认 Stream。所有分支中获取的默认 Stream 仅用作 cache key / resource_id,用于查找或创建 executor / StreamResource,不回写到实际执行路径的 stream 变量。
  2. stream=nullptr 的最终行为取决于分支和 kernel 类型
    • 分支 A:nullptr 透传到 rtKernelLaunch,由 Runtime 侧按默认 Stream 语义解释。
    • 分支 B(AICore/DSA/STARS):GE 层 FE_ASSERT_NOTNULL(stream) 拦截报错,不到达 Runtime。
    • 分支 B(AICPU):nullptr 透传到 Runtime API,是否报错取决于 Runtime 层处理。
    • 分支 C(静态 TBE):nullptr 透传到 rtKernelLaunchWithFlagV2 / rtKernelLaunchWithHandleV2,由 Runtime 侧按默认 Stream 语义解释。
  3. 分支 A 是尝试性路径,只有 streamExecutor->ExecuteAsync 返回失败才 fall through 到分支 B 或 C;Context 获取失败(aclrtGetCurrentContext)、executor 创建失败(GetOrCreate 返回 nullptr)会直接返回错误。因此实际生效的路径取决于算子类型和 RuntimeV2 开关:动态算子走 B,静态算子或未开启 RuntimeV2 时走 C,注册了动态内核选择器的算子优先尝试 A。

2.1.2 ExecuteAsync(OpHandle) 重载的 stream 为 nullptr 行为

该重载 [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:515] 内部代码含 3 条分支(含 isDynamic 动态分支 [op_executor.cpp:526]),但 aclopExecWithHandle API 不产生动态 shape 算子,故 isDynamic 分支不可达,实际仅 2 条分支生效。伪代码如下:

OpExecutor::ExecuteAsync(opHandle, inputs, outputs, stream=nullptr):

    // 分支 A':动态内核选择器路径(kernelDesc != nullptr)
    if opHandle.kernelDesc != nullptr:
        streamExecutor = Executors::GetOrCreate(context, stream=nullptr)
        return streamExecutor->ExecuteAsync(*kernelDesc, inputs, outputs)
        // 结论:与 2.1.1.1 分支 A 调用栈相同,rtKernelLaunch 最终收到 nullptr

    // 分支 C':RT1 路径(aclopExecWithHandle 不支持动态 shape 算子,仅静态路径)
    // stream=nullptr 用作 cachedOperators 的 cache key
    ret = LoadSingleOp(opHandle.opModel, stream=nullptr, &singleOp)
    return DoExecuteAsync(singleOp, opHandle.aclOp, inputs, outputs)
    // 结论:与 2.1.1.3 分支 C 调用栈相同,rtKernelLaunchWithFlagV2 最终收到 nullptr
2.1.2.1 分支 A':动态内核选择器路径

opHandle.kernelDesc != nullptr 时进入此分支。调用栈如下:

OpExecutor::ExecuteAsync(opHandle, inputs, outputs, stream=nullptr)
[ge/api/acl/acl_op_executor/single_op/op_executor.cpp:515]
│
└─ Executors::GetOrCreate(context, stream=nullptr)
   [ge/api/acl/acl_op_executor/single_op/executor/stream_executor.cpp:163]
   │  与 2.1.1.1 分支 A 完全相同:默认 Stream 仅用作 cache key,stream_ 保持 nullptr
   │
└─ streamExecutor->ExecuteAsync(*kernelDesc, numInputs, inputs, numOutputs, outputs)
   [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:523]
   → [ge/api/acl/acl_op_executor/single_op/executor/stream_executor.cpp:62]
   │  此处调用的是 OpKernelDesc 重载(2.1.1.1 分支 A 调用的是 AclOp 重载)
   │  但内部逻辑相同:第 69 行 task.ExecuteAsync(..., stream_=nullptr)
   │
   └─ TbeOpTask::ExecuteAsync(..., stream_=nullptr)
      → [ge/api/acl/acl_op_executor/single_op/executor/op_task.cpp:28]
      │
      └─ rtKernelLaunch(stubFunc, blockDim, args, argSize, nullptr, stream=nullptr)
         [ge/api/acl/acl_op_executor/single_op/executor/op_task.cpp:45]

结论:与 2.1.1.1 分支 A 结论相同。GetOrCreate 中默认 Stream 仅用作 cache key,stream_ 保持 nullptr,最终 rtKernelLaunch 收到 nullptr,由 Runtime 侧按默认 Stream 语义解释。

2.1.2.2 分支 C':RT1 路径

opHandle.kernelDesc == nullptr 时进入此分支。由于 aclopExecWithHandle 接口不支持动态 shape 算子(参见官方文档),此分支仅走静态路径。

stream=nullptr 的处理与 2.1.1.3 分支 C 相同,差异在于 OpHandle 重载有自己的 cache 机制:第 563 行 cachedOperators.find(stream=nullptr) 用 nullptr 作 cache key 查找;未命中时调用 LoadSingleOp [op_executor.cpp:567],与 2.1.1.3 分支 C 的 LoadSingleOp [op_executor.cpp:369] 是同一个函数。

后续 DoExecuteAsync(singleOp, ...) 调用栈与 2.1.1.3 分支 C 完全相同(SingleOpImpl::ExecuteAsynctask->LaunchKernel(stream_=nullptr)rtKernelLaunchWithFlagV2 / rtKernelLaunchWithHandleV2),此处不再重复。

结论(静态 TBE 子路径):与 2.1.1.3 分支 C 结论相同。GetResourceIdGetAllocators 获取的默认 Stream 仅用作 cache key;StreamResource 内部 stream_ 保持 nullptr,最终 rtKernelLaunchWithFlagV2 / rtKernelLaunchWithHandleV2 收到 nullptr,由 Runtime 侧按默认 Stream 语义解释。

注意:以上仅验证了静态 TBE 子路径。RT1 静态路径还包括 AiCpuTaskMemcpyAsyncTaskMixL2OpTask 等其他 Task 类型,各 Task 的 LaunchKernel 实现不同,stream=nullptr 的行为需分别验证。

注意:OpHandle 重载的 cache 机制额外使用 stream 作 key(cachedOperators),stream=nullptr 时以 nullptr 为 key 查找/插入。这与 SingleOpManager::GetResource 中用默认 Stream 作 resource_id 的机制是两套独立的缓存。

2.1.2.3 总体结论
分支 触发条件 与 AclOp 重载的关系 最终到达 stream 实际值
A'(动态内核选择器) kernelDesc != nullptr 调用栈同 2.1.1.1 分支 A(ExecuteAsync 重载不同但内部逻辑相同) rtKernelLaunch(Runtime API) nullptr
C'(RT1,静态 TBE) kernelDesc == nullptr 调用栈同 2.1.1.3 分支 C rtKernelLaunchWithFlagV2 / rtKernelLaunchWithHandleV2(Runtime API) nullptr

与 AclOp 重载的关键差异

  1. OpHandle 重载没有 RT2 路径(分支 B),因此不存在 AICore FE_ASSERT_NOTNULL(stream) 报错的情况。
  2. OpHandle 重载额外使用 stream 作 cache key(cachedOperators),stream=nullptr 时以 nullptr 为 key。
  3. 两条分支的 stream=nullptr 最终都透传到 Runtime API,由 Runtime 侧按默认 Stream 语义解释。

2.2 编译并执行类(aclopCompileAndExecute / aclopCompileAndExecuteV2)

本类 2 个接口在编译完成后,执行阶段汇聚到 2.1.1 节已分析过的 OpExecutor::ExecuteAsync(AclOp) 重载 [ge/api/acl/acl_op_executor/single_op/op_executor.cpp:410]。汇聚关系如下图所示:

flowchart TD
    A1["aclopCompileAndExecute(入口函数体内联,无独立 Impl)<br/>[ge/api/acl/acl_op_compiler/single_op/op_compiler.cpp:191]"]
    A2["aclopCompileAndExecuteV2(入口函数体内联,无独立 Impl)<br/>[ge/api/acl/acl_op_compiler/single_op/op_compiler.cpp:224]"]

    C["OpCompileProcessor::OpCompile(编译阶段,不涉及 stream)<br/>[ge/api/acl/acl_op_compiler/single_op/op_compiler.cpp:213/244]"]

    D["OpExecutor::ExecuteAsync(AclOp)<br/>[ge/api/acl/acl_op_executor/single_op/op_executor.cpp:410]"]

    A1 --> C
    A2 --> C
    C --> D

两个接口的调用链如下:

接口 定义 编译阶段 汇聚点(执行阶段)
aclopCompileAndExecute op_compiler.cpp:191 OpCompileProcessor::OpCompile [op_compiler.cpp:213] OpExecutor::ExecuteAsync(aclOp, inputs, outputs, stream) [op_compiler.cpp:221] → AclOp 重载 [op_executor.cpp:410]
aclopCompileAndExecuteV2 op_compiler.cpp:224 OpCompileProcessor::OpCompile [op_compiler.cpp:244] OpExecutor::ExecuteAsync(aclOp, inputs, outputs, stream) [op_compiler.cpp:252] → AclOp 重载 [op_executor.cpp:410]

关键结论

  1. 编译阶段不涉及 streamConstructAclOp [op_compiler.cpp:75] 不接收 stream 参数,AclOp 结构体中也不存储 stream。stream 仅在编译完成后的 ExecuteAsync 阶段作为独立参数传入。
  2. 两个接口汇聚到 2.1.1 节的 AclOp 重载 [op_executor.cpp:410],stream 透传路径与 2.1.1 节完全相同。
  3. V1 与 V2 的唯一差异在于 exeucteType 字段:V1 为 ACL_OP_EXECUTE,V2 为 ACL_OP_EXECUTE_REFRESH_OUTPUT_ORI_SHAPE。该差异影响 PrepareRt2Execute 中的 trust_shape_on_out_tensor 标志 [op_executor.cpp:465],但不影响 stream 的处理路径。

stream 为 nullptr 的行为完全复用 2.1.1 节的结论。两个接口在编译完成后进入 OpExecutor::ExecuteAsync(AclOp) 重载,后续按 3 条分支(A/B/C)处理 stream=nullptr,行为与 2.1.1.1 / 2.1.1.2 / 2.1.1.3 完全一致,此处不再重复。

2.3 BLAS 类(aclblasGemvEx / aclblasHgemv / aclblasS8gemv / aclblasGemmEx / aclblasHgemm / aclblasS8gemm)

本类 6 个接口通过 MatMulTemplate::ExecuteAsync 统一转调 aclopExecuteV2,此后调用链与 2.1 节完全一致。汇聚关系如下图所示:

flowchart TD
    A1["aclblasGemvEx<br/>[ge/api/acl/acl_cblas/single_op/blas/gemv_ops.cpp:15]"]
    A2["aclblasHgemv<br/>[ge/api/acl/acl_cblas/single_op/blas/gemv_ops.cpp:32]"]
    A3["aclblasS8gemv<br/>[ge/api/acl/acl_cblas/single_op/blas/gemv_ops.cpp:53]"]
    A4["aclblasGemmEx<br/>[ge/api/acl/acl_cblas/single_op/blas/gemm_ops.cpp:137]"]
    A5["aclblasHgemm<br/>[ge/api/acl/acl_cblas/single_op/blas/gemm_ops.cpp:252]"]
    A6["aclblasS8gemm<br/>[ge/api/acl/acl_cblas/single_op/blas/gemm_ops.cpp:261]"]

    B["aclblasGemmEx(汇聚点 1)<br/>[ge/api/acl/acl_cblas/single_op/blas/gemm_ops.cpp:137]"]
    C["MatMulTemplate::ExecuteAsync<br/>[ge/api/acl/acl_cblas/single_op/blas/gemm_ops.cpp:91]"]
    D["aclopExecuteV2(汇聚点 2,与 2.1 节汇合)<br/>[ge/api/acl/acl_op_executor/single_op/acl_op_executor.cpp:84]"]

    A1 --> B
    A2 --> A1
    A3 --> A1
    A4 --> B
    A5 --> B
    A6 --> B
    B --> C
    C --> D

6 个接口的调用链如下:

接口 定义 中间转调 汇聚点
aclblasGemvEx gemv_ops.cpp:15 → aclblasGemmEx [gemv_ops.cpp:20] aclopExecuteV2 [gemm_ops.cpp:111]
aclblasHgemv gemv_ops.cpp:32 → aclblasGemvEx [gemv_ops.cpp:37] → aclblasGemmEx aclopExecuteV2 [gemm_ops.cpp:111]
aclblasS8gemv gemv_ops.cpp:53 → aclblasGemvEx [gemv_ops.cpp:58] → aclblasGemmEx aclopExecuteV2 [gemm_ops.cpp:111]
aclblasGemmEx gemm_ops.cpp:137 → MatMulTemplate::ExecuteAsync [gemm_ops.cpp:194] aclopExecuteV2 [gemm_ops.cpp:111]
aclblasHgemm gemm_ops.cpp:252 → aclblasGemmEx [gemm_ops.cpp:257] aclopExecuteV2 [gemm_ops.cpp:111]
aclblasS8gemm gemm_ops.cpp:261 → aclblasGemmEx [gemm_ops.cpp:266] aclopExecuteV2 [gemm_ops.cpp:111]

关键结论

  1. 6 个接口呈扇形收敛:3 个 gemv 接口先转调 aclblasGemvEx,再由 aclblasGemvEx 和 2 个 gemm 接口(aclblasHgemm/aclblasS8gemm)转调 aclblasGemmEx,最终全部经 MatMulTemplate::ExecuteAsync 汇聚到 aclopExecuteV2 [gemm_ops.cpp:111]。
  2. stream 参数全程透传:从用户入口到 OpExecutor::ExecuteAsync,stream 在每一跳都原样传递,未被替换或丢弃。
  3. 与 2.1 节完全汇合:6 个 BLAS 接口在 aclopExecuteV2 处与 2.1 节的 aclopExecuteV2 接口完全汇合,后续走同一条 aclopExecuteV2ImplOpExecutor::ExecuteAsync(AclOp) 路径。

stream 为 nullptr 的行为完全复用 2.1 节的结论。6 个 BLAS 接口在 aclopExecuteV2 处与 2.1 节汇合,后续按 3 条分支(A/B/C)处理 stream=nullptr,行为与 2.1.1.1 / 2.1.1.2 / 2.1.1.3 完全一致,此处不再重复。

2.4 模型执行类(aclmdlExecuteV2 / aclmdlExecuteAsync / aclmdlExecuteAsyncV2)

2.4.1 分流结构总览

本类 3 个接口分属两条完全独立的管线,无共同汇聚点。分流逻辑用伪代码表示如下:

// ─── 标准 C++ 管线(api/acl/)───
aclmdlExecuteV2(modelId, input, output, stream, handle)      // 同步 [acl_model.cpp:329]
aclmdlExecuteAsync(modelId, input, output, stream)            // 异步 [acl_model.cpp:340]

  两者都先执行:
    isOm2 = AclIsOm2ModelById(modelId)                        // [acl_model_router.cpp:33]

  if isOm2:                                                   // OM2 路径
      → Om2ModelExecuteCommon(modelId, input, output, isAsync, stream)  // [model_om2.cpp:354]
          if isAsync:  executor->RunAsync(stream, ...)        // stream 透传
          else:         executor->Run(...)                    // stream 未传入

  else if IsRuntimeV2Enable && GetExecutor(modelId) != null:  // RuntimeV2 路径
      → RuntimeV2ModelExecute(modelId, input, output, isAsync, stream)  // [model.cpp:562]
          if isAsync:  executor->Execute(arg.stream=stream, ...)  // stream 参与执行
          else:         executor->ExecuteSync(...)            // stream 仅用于 profiling

  else:                                                       // RT1 路径
      → ModelExecute(modelId, input, output, isAsync, stream) // [model.cpp:710]
          → GeExecutor::ExecModel(modelId, stream, ...)       // stream 透传
          if !isAsync && stream != nullptr:                   // 同步路径额外做流同步
              aclrtSynchronizeStreamWithTimeout(stream, ...)  // [model.cpp:1715]


// ─── ACL_C 管线(api/acl_c/)───
aclmdlExecuteAsyncV2(modelId, input, output, stream, handle)  // 异步 [model.c:173]

    → ModelExecute(modelId, input, output, sync=false, stream, handle)  // [model.c:116]
        ├─ SetExecHandle(handle, stream, &execDesc)           // [model.c:88]
        │    workspace 优先取 handle->workPtr,其次 rtStreamGetWorkspace(stream)
        │    两者都无 → 返回 ACL_ERROR_INVALID_PARAM
        │    execDesc.stream = stream
        └─ ExecModel(modelId, &execDesc, ...)                 // [ge_executor.c:122]
            → rtStreamGetSqid(stream, &sqId)                  // stream 转为 sqId
            → rtNanoModelExecute(&rtMdlExec)                  // 最终 Runtime API,只收 sqId

2.4.2 标准 C++ 管线(aclmdlExecuteV2 / aclmdlExecuteAsync)

2.4.2.1 OM2 路径

AclIsOm2ModelById 返回 isOm2=true 时进入此路径。aclmdlExecuteV2aclmdlExecuteAsync 都经 Om2ModelExecuteCommon [ge/api/acl/acl_model/model/model_om2.cpp:354] 汇聚,按 isAsync 参数分流到同步或异步执行。

同步路径(aclmdlExecuteV2)
aclmdlExecuteV2(modelId, input, output, stream, handle)
[ge/api/acl/acl_model/model/acl_model.cpp:329]
│
└─ aclmdlExecuteV2ImplOm2(modelId, input, output, stream, handle)
   [ge/api/acl/acl_model/model/model_om2.cpp:1219]
   │  设置 streamSyncTimeout / eventSyncTimeout 到 Om2ThreadLocalContext
   │  [model_om2.cpp:1224-1231]
   │
   └─ Om2ModelExecuteCommon(modelId, input, output, isAsync=false, stream)
      [ge/api/acl/acl_model/model/model_om2.cpp:1233] → [model_om2.cpp:354]
      │  准备 input/output tensors(不涉及 stream)
      │
      └─ executor->Run(inputVec, outputVec)
         [ge/api/acl/acl_model/model/model_om2.cpp:401]
         → [ge/runtime/om2/om2_model_executor.cc:864](Impl::Run)
         │  Run() 签名不含 stream 参数
         │
         └─ run_func(&model_handle, inputs.size(), inputs.data(), outputs.size(), outputs.data(), timeout)
            [ge/runtime/om2/om2_model_executor.cc:870]
            │  run_func 是动态加载的 Om2ModelRun(来自编译产物 SO)
            │  RunFunc 签名:(*)(Om2ModelHandle*, int, void**, int, void**, int32_t streamSyncTimeout)
            │  无 stream 参数,timeout 来自 Om2ThreadLocalContext
            │  SO 内部使用自己管理的 stream 执行和同步

stream 处理:用户传入的 stream 在整条同步路径中未被使用Om2ModelExecuteCommon 接收 stream 但 isAsync=false 时调用 executor->Run() 不传入 stream;Impl::Run 调用 run_funcOm2ModelRun)也不传入 stream,只传入 streamSyncTimeout。SO 内部使用自己管理的 stream 执行 kernel 并做同步,与用户传入的 stream 无关。

异步路径(aclmdlExecuteAsync)
aclmdlExecuteAsync(modelId, input, output, stream)
[ge/api/acl/acl_model/model/acl_model.cpp:340]
│
└─ aclmdlExecuteAsyncImplOm2(modelId, input, output, stream)
   [ge/api/acl/acl_model/model/model_om2.cpp:1236]
   │
   └─ Om2ModelExecuteCommon(modelId, input, output, isAsync=true, stream)
      [ge/api/acl/acl_model/model/model_om2.cpp:1241] → [model_om2.cpp:354]
      │  准备 input/output tensors(不涉及 stream)
      │
      └─ executor->RunAsync(stream, inputVec, outputVec)
         [ge/api/acl/acl_model/model/model_om2.cpp:399]
         → [ge/runtime/om2/om2_model_executor.cc:876](Impl::RunAsync)
         │  stream 透传给 run_async_func
         │
         └─ run_async_func(&model_handle, stream, inputs.size(), inputs.data(), outputs.size(), outputs.data())
            [ge/runtime/om2/om2_model_executor.cc:880]
            │  run_async_func 是动态加载的 Om2ModelRunAsync(来自编译产物 SO)
            │  RunAsyncFunc 签名:(*)(Om2ModelHandle*, rtStream_t, int, void**, int, void**)
            │  stream 直接传入 SO

stream 处理:用户传入的 stream 在异步路径中全程透传Om2ModelExecuteCommonisAsync=true 时调用 executor->RunAsync(stream, ...)Impl::RunAsync 将 stream 直接传给 run_async_funcOm2ModelRunAsync),由 SO 内部使用用户 stream 异步执行。

2.4.2.2 RuntimeV2 路径

isOm2=falseIsRuntimeV2Enable(true) 为 true 且 GetExecutor(modelId) != nullptr 时进入此路径。aclmdlExecuteV2aclmdlExecuteAsync 都经 RuntimeV2ModelExecute [ge/api/acl/acl_model/model/model.cpp:562] 汇聚,按 isAsync 参数分流到同步或异步执行。

同步路径(aclmdlExecuteV2)
aclmdlExecuteV2(modelId, input, output, stream, handle)
[ge/api/acl/acl_model/model/acl_model.cpp:329]
│
└─ aclmdlExecuteV2Impl(modelId, input, output, stream, handle)
   [ge/api/acl/acl_model/model/model.cpp:1694]
   │  设置 streamSyncTimeout / eventSyncTimeout 到 ge::GetContext()
   │  [model.cpp:1701-1706]
   │
   └─ RuntimeV2ModelExecute(modelId, input, output, isAsync=false, stream)
      [ge/api/acl/acl_model/model/model.cpp:1711] → [model.cpp:562]
      │  准备 input/output tensors(不涉及 stream)
      │
      ├─ CANN_PROFILING_STEP_TRACE(modelId, index_id, kStartTag, stream)
      │  [model.cpp:652]  ← stream 用于 profiling 标记
      │
      ├─ executor->ExecuteSync(inputVec, ..., outputVec, ...)
      │  [model.cpp:659]
      │  → [ge/runtime/v2/core/model_v2_executor.cc:252]
      │  │  ExecuteSync() 签名不含 stream 参数
      │  │
      │  ├─ 若 default_stream_ == nullptr:
      │  │  rtStreamCreateWithFlags(&default_stream_, ..., RT_STREAM_FAST_LAUNCH | RT_STREAM_FAST_SYNC)
      │  │  [model_v2_executor.cc:253-256]  ← 创建 executor 内部 stream
      │  │
      │  ├─ Execute({default_stream_}, inputs, ..., outputs, ...)
      │  │  [model_v2_executor.cc:258]
      │  │  → [model_v2_executor.cc:227]
      │  │  │  arg.stream = default_stream_(非用户 stream)
      │  │  │  → SpecifyArgsInputs → OccupyStreamResource
      │  │  │    [model_v2_executor.cc:123] streams[0] = arg.stream = default_stream_
      │  │  │  → graph_executor.Execute() → execute_func_ → REGISTER_KERNEL kernel
      │  │  │    模型在 default_stream_ 上执行
      │  │
      │  └─ DoRtStreamSyncWithTimeout(default_stream_)
      │     [model_v2_executor.cc:262]  ← 同步内部 default_stream_
      │
      └─ CANN_PROFILING_STEP_TRACE(modelId, index_id, kEndTag, stream)
         [model.cpp:661]  ← stream 用于 profiling 标记

stream 处理:用户传入的 stream 不参与模型执行和同步RuntimeV2ModelExecuteisAsync=false 时调用 executor->ExecuteSync(),该函数签名不含 stream。ExecuteSync 内部使用 executor 成员变量 default_stream_(首次调用时通过 rtStreamCreateWithFlags 创建,带 RT_STREAM_FAST_LAUNCH | RT_STREAM_FAST_SYNC 标志)执行模型并同步。用户 stream 仅用于 CANN_PROFILING_STEP_TRACE 的 profiling 标记 [model.cpp:652/661]。

异步路径(aclmdlExecuteAsync)
aclmdlExecuteAsync(modelId, input, output, stream)
[ge/api/acl/acl_model/model/acl_model.cpp:340]
│
└─ aclmdlExecuteAsyncImpl(modelId, input, output, stream)
   [ge/api/acl/acl_model/model/model.cpp:1730]
   │
   └─ RuntimeV2ModelExecute(modelId, input, output, isAsync=true, stream)
      [ge/api/acl/acl_model/model/model.cpp:1738] → [model.cpp:562]
      │  准备 input/output tensors(不涉及 stream)
      │
      ├─ CANN_PROFILING_STEP_TRACE(modelId, index_id, kStartTag, stream)
      │  [model.cpp:652]  ← stream 用于 profiling 标记
      │
      ├─ arg.stream = stream
      │  [model.cpp:655]  ← 用户 stream 写入 arg
      │
      ├─ arg.external_allocator = GetAllocators(arg.stream, false)
      │  [model.cpp:656]  ← stream 用于获取 allocator
      │  arg.stream=nullptr 时 GetAllocators 内部用默认 Stream 作 cache key
      │
      ├─ executor->Execute(arg, inputVec, ..., outputVec, ...)
      │  [model.cpp:657]
      │  → [ge/runtime/v2/core/model_v2_executor.cc:227]
      │  │  arg.stream = 用户 stream(可能为 nullptr)
      │  │  → SpecifyArgsInputs → OccupyStreamResource
      │  │    [model_v2_executor.cc:123] streams[0] = arg.stream
      │  │  → graph_executor.Execute() → execute_func_ → REGISTER_KERNEL kernel
      │  │    模型在 arg.stream(用户 stream)上执行
      │  │    若 arg.stream=nullptr,AICore kernel 的 FE_ASSERT_NOTNULL(stream) 会报错
      │  │    (与 2.1.1.2 分支 B 结论一致)
      │
      └─ CANN_PROFILING_STEP_TRACE(modelId, index_id, kEndTag, stream)
         [model.cpp:661]  ← stream 用于 profiling 标记

stream 处理:用户传入的 stream 全程参与执行RuntimeV2ModelExecuteisAsync=true 时将 stream 写入 arg.stream [model.cpp:655],并用于 GetAllocators 获取 allocator [model.cpp:656]。executor->Execute(arg, ...) 内部经 OccupyStreamResourcearg.stream 写入 streams[0] [model_v2_executor.cc:123],透传给 graph_executor,最终由 REGISTER_KERNEL 注册的 kernel 使用。stream=nullptr 时的行为与 2.1.1.2 分支 B 完全一致(AICore/DSA/STARS kernel 报错,AICPU kernel 透传)。

2.4.2.3 RT1 路径

isOm2=false 且不满足 RuntimeV2 条件(IsRuntimeV2Enable 为 false 或 GetExecutor(modelId) == nullptr)时进入此路径。aclmdlExecuteV2aclmdlExecuteAsync 都经 ModelExecute [ge/api/acl/acl_model/model/model.cpp:710] 汇聚,按 basync 参数区分同步/异步。两条路径都调用 GeExecutor::ExecModel,stream 始终透传,最终到达 DavinciModel::NnExecute,由 InitModelStream 根据 stream 是否为 nullptr 决定使用用户 stream 还是创建内部 forbidden stream。

同步路径(aclmdlExecuteV2)
aclmdlExecuteV2(modelId, input, output, stream, handle)
[ge/api/acl/acl_model/model/acl_model.cpp:329]
│
└─ aclmdlExecuteV2Impl(modelId, input, output, stream, handle)
   [ge/api/acl/acl_model/model/model.cpp:1694]
   │
   ├─ ModelExecute(modelId, input, output, basync=false, stream)
   │  [ge/api/acl/acl_model/model/model.cpp:1713] → [model.cpp:710]
   │  │
   │  └─ executor.ExecModel(modelId, stream, inputData, ..., basync=false)
   │     [ge/api/acl/acl_model/model/model.cpp:807]
   │     → [ge/runtime/v1/executor/ge_executor.cc:1170]
   │     │
   │     └─ GraphLoader::ExecuteModel(model_id, stream, async_mode=false, ...)
   │        [ge/runtime/v1/executor/ge_executor.cc:1201]
   │        → [ge/runtime/v1/graph/load/graph_loader.cc:181]
   │        │
   │        └─ ModelManager::ExecuteModel(model_id, stream, async_mode=false, ...)
   │           [ge/runtime/v1/graph/load/model_manager/model_manager.cc:1761]
   │           │
   │           └─ davinci_model->NnExecute(stream, async_mode=false, ...)
   │              [ge/runtime/v1/graph/load/model_manager/model_manager.cc:1775]
   │              → [ge/runtime/v1/graph/load/model_manager/davinci_model.cc:7702]
   │              │
   │              ├─ InitModelStream(stream)
   │              │  [davinci_model.cc:7711] → [davinci_model.cc:7625]
   │              │  同步模式 + stream==nullptr 时:
   │              │    创建内部 forbidden stream(RT_STREAM_FORBIDDEN_DEFAULT)
   │              │    rt_model_stream_ = 内部 stream,is_forbidden_stream_=true,is_inner_model_stream_=true
   │              │    [davinci_model.cc:7654-7664]
   │              │  同步模式 + stream!=nullptr 时:
   │              │    rt_model_stream_ = stream(用户 stream),is_inner_model_stream_=false
   │              │    [davinci_model.cc:7644-7651]
   │              │
   │              └─ if is_forbidden_stream_ && is_inner_model_stream_:
   │                    aclmdlRIExecute(rt_model_handle_, streamSyncTimeout)
   │                    [davinci_model.cc:7748]  ← 同步接口,内部做流同步
   │                 else:
   │                    aclmdlRIExecuteAsync(rt_model_handle_, rt_model_stream_)
   │                    [davinci_model.cc:7750]  ← 异步接口,但同步模式下 stream 非空
   │
   └─ if stream != nullptr:
      aclrtSynchronizeStreamWithTimeout(stream, handle->streamSyncTimeout)
      [ge/api/acl/acl_model/model/model.cpp:1714-1720]
      │  仅当用户 stream 非空时执行显式流同步
      │  stream=nullptr 时跳过(此时模型在内部 forbidden stream 上执行,
      │  aclmdlRIExecute 内部已完成同步)

stream 处理:同步路径中 stream=nullptr 时,InitModelStream 创建内部 forbidden stream(RT_STREAM_FORBIDDEN_DEFAULT)[davinci_model.cc:7654-7664],模型在该内部 stream 上执行,通过 aclmdlRIExecute(同步接口)完成执行和同步 [davinci_model.cc:7748]。stream 非空时,rt_model_stream_ 设为用户 stream,通过 aclmdlRIExecuteAsync(异步接口)执行 [davinci_model.cc:7750],返回后 aclmdlExecuteV2Impl 额外调用 aclrtSynchronizeStreamWithTimeout 同步用户 stream [model.cpp:1714-1720]。

异步路径(aclmdlExecuteAsync)
aclmdlExecuteAsync(modelId, input, output, stream)
[ge/api/acl/acl_model/model/acl_model.cpp:340]
│
└─ aclmdlExecuteAsyncImpl(modelId, input, output, stream)
   [ge/api/acl/acl_model/model/model.cpp:1730]
   │
   └─ ModelExecute(modelId, input, output, basync=true, stream)
      [ge/api/acl/acl_model/model/model.cpp:1740] → [model.cpp:710]
      │
      └─ executor.ExecModel(modelId, stream, inputData, ..., basync=true)
         [ge/api/acl/acl_model/model/model.cpp:807]
         → [ge/runtime/v1/executor/ge_executor.cc:1170]
         │
         └─ GraphLoader::ExecuteModel(model_id, stream, async_mode=true, ...)
            [ge/runtime/v1/executor/ge_executor.cc:1201]
            → [ge/runtime/v1/graph/load/graph_loader.cc:181]
            │
            └─ ModelManager::ExecuteModel(model_id, stream, async_mode=true, ...)
               [ge/runtime/v1/graph/load/model_manager/model_manager.cc:1761]
               │
               └─ davinci_model->NnExecute(stream, async_mode=true, ...)
                  [ge/runtime/v1/graph/load/model_manager/model_manager.cc:1775]
                  → [ge/runtime/v1/graph/load/model_manager/davinci_model.cc:7702]
                  │
                  ├─ InitModelStream(stream)
                  │  [davinci_model.cc:7711] → [davinci_model.cc:7625]
                  │  异步模式:
                  │    销毁内部 stream(若存在),rt_model_stream_ = stream(用户 stream)
                  │    is_inner_model_stream_ = false
                  │    [davinci_model.cc:7632-7640]
                  │    stream=nullptr 时 rt_model_stream_ = nullptr
                  │
                  └─ aclmdlRIExecuteAsync(rt_model_handle_, rt_model_stream_)
                     [davinci_model.cc:7750]
                     │  rt_model_stream_ = 用户 stream(可能为 nullptr)
                     │  → aclmdlRIExecuteAsyncImpl [runtime/src/acl/aclrt_impl/model_ri.cpp:25]
                     │  → rtModelExecute(modelRI, stream, 0U) [model_ri.cpp:30]
                     │  → ApiImpl::ModelExecute → mdl->Execute(stm)
                     │  stream=nullptr 由 Runtime 侧按默认 Stream 语义解释

stream 处理:异步路径中 InitModelStream 直接将 rt_model_stream_ 设为用户 stream [davinci_model.cc:7638],stream=nullptr 时 rt_model_stream_ 为 nullptr。随后 aclmdlRIExecuteAsync(rt_model_handle_, rt_model_stream_) 将 stream 透传给 Runtime API rtModelExecute [model_ri.cpp:30],由 Runtime 侧按默认 Stream 语义解释。返回后不做额外流同步,调用者需自行同步。

2.4.3 ACL_C 管线(aclmdlExecuteAsyncV2)

aclmdlExecuteAsyncV2 走独立的 ACL_C 管线,与标准 C++ 管线无交汇。该接口在 SetExecHandle 阶段对 workspace 有强依赖,stream=nullptr 且 handle 未提供 workspace 时直接返回 ACL_ERROR_INVALID_PARAM

2.4.3.1 完整调用栈
aclmdlExecuteAsyncV2(modelId, input, output, stream, handle)
[ge/api/acl_c/acl_model/model/model.c:173]
│  stream 作为参数传入
│
└─ ModelExecute(modelId, input, output, sync=false, stream, handle)
   [ge/api/acl_c/acl_model/model/model.c:116]
   │
   ├─ SetExecHandle(handle, stream, &execDesc)
   │  [ge/api/acl_c/acl_model/model/model.c:121] → [model.c:88]
   │  │  workspace 获取逻辑(优先级从高到低):
   │  │  1. handle != NULL && handle->workPtr != NULL → workPtr = handle->workPtr [model.c:91-95]
   │  │  2. workPtr == NULL && stream != NULL → rtStreamGetWorkspace(stream, &workPtr, &workSize) [model.c:97-98]
   │  │  3. workPtr == NULL → 返回 ACL_ERROR_INVALID_PARAM [model.c:100-103]
   │  │
   │  │  stream=nullptr 时:
   │  │    跳过步骤 2(stream==NULL 条件不满足)
   │  │    若 handle 未提供 workPtr → 步骤 3 报错,直接返回 ACL_ERROR_INVALID_PARAM
   │  │    若 handle 提供了 workPtr → 继续执行
   │  │
   │  │  execDesc.stream = stream  [model.c:104]  ← stream 直接赋值透传
   │  │  execDesc.workPtr = workPtr  [model.c:105]
   │  │  execDesc.workSize = workSize  [model.c:106]
   │
   └─ ExecModel(modelId, &execDesc, sync=false, input, output)
      [ge/api/acl_c/acl_model/model/model.c:127]
      → [ge/runtime/c/executor/ge_executor.c:122]
      │
      └─ ModelExecuteInner(modelId, execDesc, sync, input, output)
         [ge/runtime/c/executor/ge_executor.c:127]
         → [ge/runtime/c/executor/model_executor.c:116]
         │  获取 GeModelDesc
         │
         └─ ModelExecute(execDesc, sync, input, output, mdlDesc)
            [ge/runtime/c/executor/model_executor.c:122](调用点)→ [model_executor.c:72](定义点)
            │
            ├─ rtStreamGetSqid(execDesc->stream, &sqId)
            │  [model_executor.c:75]
            │  ★ 关键转换:stream 被转换为 sqId(uint32_t)
            │  stream=nullptr 时:rtStreamGetSqid(nullptr, &sqId) 由 Runtime 侧处理
            │  (Runtime 可能返回错误,或按默认 Stream 解释,取决于 Runtime 实现)
            │
            ├─ 构造 rtMdlExecute_t:
            │  [model_executor.c:93-106]
            │  rtMdlExec.sqid = sqId          ← 只有 sqId,没有 stream 指针
            │  rtMdlExec.workPtr = execDesc->workPtr
            │  rtMdlExec.sync = sync
            │  ...(其他字段)
            │
            └─ rtNanoModelExecute(&rtMdlExec)
               [model_executor.c:108]
               │  最终 Runtime API,只接收 rtMdlExecute_t(含 sqId,不含 stream 指针)
               │  stream 已在上一跳被转换为 sqId

stream 处理

  1. workspace 强依赖SetExecHandle [model.c:88] 强制检查 workspace。stream=nullptr 时无法通过 rtStreamGetWorkspace 获取 workspace,若 handle 也未提供 workPtr,直接返回 ACL_ERROR_INVALID_PARAM [model.c:100-103]。这是 ACL_C 管线与标准 C++ 管线的关键差异——标准 C++ 管线无此检查。

  2. stream 转换为 sqId:stream 在 model_executor.c:75 处通过 rtStreamGetSqid(execDesc->stream, &sqId) 转换为 sqId,此后原始 stream 指针不再传递给底层 Runtime API。rtNanoModelExecute 接收的 rtMdlExecute_t 中只有 sqid 字段,没有 stream 指针 [model_executor.c:94]。

  3. stream=nullptr 的两种可能

    • 若 handle 提供了 workPtrSetExecHandle 通过,但 rtStreamGetSqid(nullptr, &sqId) 的行为取决于 Runtime 实现——可能返回错误,也可能按默认 Stream 解释。
    • 若 handle 未提供 workPtrSetExecHandle 直接返回 ACL_ERROR_INVALID_PARAM,不会到达 rtStreamGetSqid

2.4.4 总体结论

3 个接口共 7 条路径,stream=nullptr 时的行为汇总如下:

接口 路径 stream=nullptr 行为 结果
aclmdlExecuteV2 OM2 同步 用户 stream 未使用,SO 内部 stream 执行+同步 正常执行
aclmdlExecuteV2 RuntimeV2 同步 用户 stream 仅用于 profiling,内部 default_stream_ 执行+同步 正常执行
aclmdlExecuteV2 RT1 同步 InitModelStream 创建内部 forbidden stream,aclmdlRIExecute 同步执行 正常执行
aclmdlExecuteAsync OM2 异步 stream 透传给 SO 的 RunAsync 待确定(取决于 SO 内部处理)
aclmdlExecuteAsync RuntimeV2 异步 stream 写入 streams[0],透传给 kernel 取决于 kernel 类型:AICore/DSA/STARS 报错,AICPU 透传到 Runtime
aclmdlExecuteAsync RT1 异步 stream 透传给 aclmdlRIExecuteAsyncrtModelExecute 待确定(取决于 Runtime 层处理)
aclmdlExecuteAsyncV2 ACL_C handle 未提供 workPtr:SetExecHandle 返回 ACL_ERROR_INVALID_PARAM;handle 提供 workPtr:通过 workspace 检查,stream=nullptr 透传给 rtStreamGetSqid,由 Runtime 侧处理 取决于 handle 是否提供 workPtr:未提供则报错;提供则待确定(取决于 Runtime 层处理)

2.5 第二章总结

15 个接口按 4 类分组,stream=nullptr 的行为汇总如下:

类别 接口 汇聚点 stream=nullptr 结果
单算子执行类 aclopExecute / aclopExecuteV2 / aclopCast OpExecutor::ExecuteAsync(AclOp) 取决于分支和 kernel 类型(见下表)
单算子执行类 aclopExecWithHandle OpExecutor::ExecuteAsync(OpHandle) 取决于分支(见下表)
编译并执行类 aclopCompileAndExecute / aclopCompileAndExecuteV2 OpExecutor::ExecuteAsync(AclOp) 同单算子执行类
BLAS 类 aclblasGemvEx / aclblasHgemv / aclblasS8gemv / aclblasGemmEx / aclblasHgemm / aclblasS8gemm aclopExecuteV2OpExecutor::ExecuteAsync(AclOp) 同单算子执行类
模型执行类 aclmdlExecuteV2 OM2/RuntimeV2/RT1 同步路径 正常执行(3 条路径均使用内部 stream)
模型执行类 aclmdlExecuteAsync OM2/RuntimeV2/RT1 异步路径 待确定(stream 透传给底层,取决于 SO/Runtime/kernel 处理)
模型执行类 aclmdlExecuteAsyncV2 ACL_C 管线 取决于 handle:未提供 workPtr 则报错;提供则待确定

单算子类(含编译执行类、BLAS 类)的 3 条分支细化:

分支 触发条件 stream=nullptr 结果
A(动态内核选择器) HasSelectFunc 为 true 透传到 RuntimertKernelLaunch 收到 nullptr)
B(RT2)- AICore/DSA/STARS IsRuntimeV2EnableisDynamic 报错(GE 层 FE_ASSERT_NOTNULL(stream)
B(RT2)- AICPU 同上 透传到 Runtime
C(RT1,静态 TBE) 默认或静态模型 透传到 RuntimertKernelLaunchWithFlagV2 收到 nullptr)

全局前提:以上分析均假设当前线程已设置有效 Context。stream=nullptr 时多处调用 aclrtCtxGetCurrentDefaultStream 获取默认 Stream,若无有效 Context 则直接报错。多线程场景不支持使用默认 Stream。