V2 独立版愿景

任务管理

HeteroFlow 是本站的异构算力与大模型调度产品。任务管理。任务管理 任务管理是 HeteroFlow 平台的通用计算底座:一份 Docker 镜像加一条启动命令,平台负责任务从创建、调度、执行、故障处置到终态归档的全生命周期。用户不需要指定机器——调度器按 GPU 类型、显存、优先级与反亲和性把任务放到合适的节点,Agent 拉起容器并…

任务管理

任务管理是 HeteroFlow 平台的通用计算底座:一份 Docker 镜像加一条启动命令,平台负责任务从创建、调度、执行、故障处置到终态归档的全生命周期。用户不需要指定机器——调度器按 GPU 类型、显存、优先级与反亲和性把任务放到合适的节点,Agent 拉起容器并持续回报状态。

训练服务与推理服务不是独立系统,而是任务组(TaskGroup)的两个特化形态:训练组是静态 world 的多机容器编排,推理组是带端点与弹性伸缩的多副本服务。本篇讲三者共享的通用底座——状态机、任务模型、任务组编排、智能重试、弹性伸缩、滚动更新与调度窗口;训练框架翻译与推理网关的专门机制见各自的专题篇。

功能总览

能力一句话说明状态
任务状态机14 态全枚举 + 迁移白名单,任何状态变更先过合法性校验已实现
终态上报原子性终态迁移与执行结果由单条带状态谓词的 CAS 语句落库,并发冲突返回 409已实现
任务创建校验未知字段 400 点名、镜像 digest 引用、env 保留前缀、分片参数范围已实现
任务组编排TP / PP / DP / split 四种并行策略 + 训练组形状换算已实现(DP/TP 真机验证)
分布式环境变量注入WORLD_SIZE / MASTER_ADDR / NODE_RANK 等自动注入容器已实现(DDP/DeepSpeed 多机真机验证)
智能重试错误三分类自动处置,OOM 搬家时内存自动翻倍已实现
静态 world 组保护训练组与 TP/PP 组成员失败不做单任务重试,交组级流程接管已实现
推理组自动重建失败组每 10 秒扫描、按模板重建、优先级 100 最多 5 次已实现
无状态副本单副本自愈split/DP 组单副本失败只补该副本,健康副本不动真机验证(双节点 docker kill 实测)
节点失联任务迁移心跳静默判离线后活跃任务迁回 pending 重新调度真机验证(65 秒完成重排)
空闲休眠与唤醒推理任务空闲超时自动休眠释放 60-80% 显存,请求到达秒级唤醒已实现
自动伸缩五种指标驱动副本扩缩,leader-only 每 30 秒评估已实现
滚动更新Generation 代次追踪、surge 批次、金丝雀、行租约互斥已实现
调度窗口按每日时间窗自动暂停 / 缩容 / 停止,成本优化场景已实现
容错事件时间线每次真实容错转移落库一条事件,详情页时间线展示已实现

任务生命周期与状态机

任务的状态由单一事实源约束:ValidTransitions 迁移白名单(core/pkg/scheduler/types.go:833)。任何组件改写任务状态都必须先过这张表,调度器内部没有绕过校验的直写路径,API 上报层另有黑名单作纵深防御。

全部 14 个状态

状态语义是否占卡
pending已创建,等待调度器分配节点计入配额,未绑卡
scheduled调度器已选定节点并完成 CAS 认领,等待 Agent 拉起是
startingAgent 正在拉取镜像、启动容器是
running容器正常运行是
sleeping推理任务空闲超时休眠,GPU 显存释放 60-80%(物理空闲但逻辑预留,卡为唤醒保留)是
waking休眠恢复的过渡状态是
preempting被更高优先级任务抢占,等 Agent 优雅停容器后回 pending是
stopping用户停止 / 运行超时 / 组判死收尾,等 Agent 回收容器是
stoppedAgent 停止容器后上报的终态否
draining缩容或滚动更新淘汰旧副本时的排空状态是
paused预留状态:当前版本上报层拒绝一切转入请求,仅保留历史数据捞回通道是
completed正常退出(终态)否
failed异常退出或超时,可经重试回 pending否
cancelled用户取消或平台主动淘汰(终态)否

终态集合是四个:completed / failed / cancelled / stopped。持卡窗口(GPUHoldingTaskStatuses,core/pkg/types/status.go)覆盖 scheduled 到 preempting 的全部中间态,节点容量核算与租户配额共用这份口径,不存在"调度器算占用、配额不算"的窗口错位。

状态迁移矩阵

源状态可迁移目标典型触发
pendingscheduled / cancelled / failed调度成功 / 用户取消 / pending 超时
scheduledstarting / running / failed / cancelled / pending / preemptingAgent 上报 / 节点离线回退 / 被抢占
startingrunning / failed / cancelled / pending启动成功 / 失败 / 节点离线迁移
runningcompleted / failed / cancelled / pending / draining / paused / sleeping / stopping / preempting正常退出 / 异常 / 抢占回退 / 休眠 / 排空
sleepingrunning / scheduled / waking / failed / cancelled唤醒 / 唤醒失败
wakingrunning / failed / cancelled / pending恢复成功 / 失败 / 节点失联迁回
preemptingpending / failed / cancelledAgent 确认停容器(或看门狗强推)后回队
drainingcompleted / failed / cancelled排空结束
stoppingstopped / failed / cancelledAgent 停容器上报终态
pausedpending / running / cancelled预留的捞回通道
failedpending手动或自动重试
completed / stopped / cancelled(终态,不再迁移)—

三条硬保证:

  • 禁止 stopping → completed:任务进入 stopping 意味着有人已决定停它,把它写成 completed 等于把"被杀死"洗成"成功完成"。该迁移已从白名单源头移除,API 上报层保留黑名单作第二道防线——即使将来白名单被误改,上报层依然拒绝。
  • 终态上报单条 CAS:Agent 上报终态时,状态迁移、finished_at 与执行结果由一条带 WHERE status=from 谓词的 UPDATE 原子完成。CAS 未命中(行已被并发写者改走)时数据库零改动,接口返回 409 而非 200,从结构上消灭了"两步写入之间的覆盖窗口"。
  • 非 CAS 更新同样过表:完整更新模式在落库前同样校验 from→to 合法,任何客户端都无法把任务 CAS 成 pending → completed 这样的非法跳变。

任务创建与配置

创建任务走 POST /api/v1/tasks,资源需求全部是顶层字段,没有嵌套。字段逐项说明(与 core/pkg/types/types.go 的 Task 结构体逐字段核对):

字段类型说明
namestring任务名,长度 ≤100、不含控制字符,越界 400
imagestringDocker 镜像。支持 digest 固定版本(name@sha256:<64位十六进制>);格式校验三处入口(单任务 / 组模板 / 滚动更新)一致
image_pull_credentialobject私有仓库拉取凭证(registry / username / password),语义参考 K8s imagePullSecrets;创建后不可改写,查询回显一律脱敏为 ***
commandstring启动命令。非推理任务且镜像为推理引擎镜像时,Agent 注入 --entrypoint "" 清空镜像入口,让 command 按普通命令执行
argsstring[]命令参数
envstring[]环境变量,每项 NAME=VALUE;HETEROFLOW_ 前缀平台保留(仅 HETEROFLOW_MODEL_PATH / HETEROFLOW_MODEL_CACHE 两个可提交),越权 400 逐项点名
volumesarray卷挂载(host_path / container_path / read_only),路径须落挂载白名单前缀
portsarray端口映射(container_port / host_port,host_port=0 自动分配)
gpu_requiredintGPU 数量(0 = 纯 CPU 任务)。多卡任务由调度器在节点内选卡并写入卡组,Agent 据此注入 GPU 可见性变量
gpu_typestringGPU 类型要求(nvidia / musa / hygon / ascend 等),大小写不敏感匹配节点卡类型标签
gpu_memory_mbintGPU 显存需求(MB),分片调度入口;设置须 ≥32,且与整卡 gpu_required>0 互斥
gpu_compute_ratiofloatGPU 算力比例 (0,1],分片调度入口;0 表示未设置
cpu_required / memory_mbintCPU 核数 / 内存(MB)
priorityint0-100,数值越大越优先;前端三档 正常=0 / 高=40 / 最高=70,100 留给系统自动重建任务;不传按 0 处理
preemptiblebool是否允许被更高优先级任务抢占;只有普通 Docker 任务建议开启,训练任务双向不参与抢占
timeoutint运行超时(秒),0 = 不限制。字段名就是 timeout,写成 timeout_seconds 会被未知字段校验直接 400
max_retriesint最大自动重试次数
idle_timeout_minutesint推理任务空闲休眠超时(分钟),0 = 不休眠
model_pathstring模型路径;指定后调度器只选拥有该模型的节点
task_typestringinference / training / batch;不传时控制面按推理意图信号自动标注
requested_node_idstring用户钉节点意图,创建后不可变(存储层物理上改不动这一列);系统决策的落点在 node_id,两者分列
labelsmap自定义标签;heteroflow.* 保留键(gpu_type / engine / trace_id 除外)带值 400

入口纪律:请求体出现任何服务端不认识的字段一律 400 并逐个点名,拼错字段名不会"看起来成功";group_id、rank、gpu_id、retry_count、result 等服务端独占字段带非零值 400——任务组成员关系只能经任务组入口建立。

关于 GPU 分片:gpu_memory_mb / gpu_compute_ratio 是分片申请入口,QoS 级别不可由创建者指定,落节点后按硬件能力自动推导。当前仅 NVIDIA 卡具备 gold(MIG 硬件分区)与 silver(MPS 驱动级限额)执行通道,其余厂商(含全部国产卡)一律 bronze 纯软件记账防超卖。分片分配失败时任务留在 pending 等下一轮,宁可不调度也不裸跑。

任务组 TaskGroup

单个任务解决"一个容器跑在哪";当一个服务需要多个容器协同(并行推理、分布式训练、多副本扩缩)时,载体是任务组。组内批量创建 Task(rank 0 到 task_count-1),自动注入分布式环境变量,组状态由成员状态聚合协调。

四种并行策略:

策略节点约束任务分配典型场景
tensor_parallel (TP)强制单节点同节点全部 GPU大模型单机推理
pipeline_parallel (PP)每 stage 一个节点允许跨节点超大模型推理
data_parallel (DP)自动计算按可用 GPU 分配批量推理 / 训练
split自动计算每副本 1 GPU,完全独立多副本推理

训练组不是第五种 parallelism 取值,而由 task_type=training 驱动独立形状换算:每节点一个容器,容器内由 torchrun / deepspeed 等启动器按 per_node_gpu 拉起多个进程。这类组是静态 world——WORLD_SIZE、MASTER_ADDR、rank 拓扑创建时一次写死,成员失败不做单任务重试、不支持手动扩缩,这是设计选择而非缺陷,机制详见《训练服务》篇。

创建任务组时自动注入的分布式环境变量(不覆盖用户已有设置):WORLD_SIZE、NNODES(实际节点数)、NODE_RANK、LOCAL_WORLD_SIZE、MASTER_ADDR、MASTER_PORT、DIST_BACKEND,TP/PP 组另有 TENSOR_PARALLEL_SIZE / PIPELINE_PARALLEL_SIZE。RANK / LOCAL_RANK 只在能证明容器内单进程时注入——多进程容器内每个进程的 LOCAL_RANK 各不相同,不存在一个全局正确的容器级取值,交给 torchrun 等启动器自行推导。

组状态协调规则:存在(非历史残骸的)failed 成员 → 组判 failed 并停掉其余成员;全部 running → 组 running;全部 sleeping → 组 sleeping;全部终态 → 组 completed。判死收尾时有节点的任务置 stopping、无节点的直接置 failed。组内多副本间的历史残骸(被同 rank 新任务取代的终态行)不参与判定。

智能重试

任务失败时,系统按错误类型自动选择处置策略。本节从任务视角给出全貌,错误识别与退避算法的机制细节见《智能重试》专题篇。

单任务错误三分类

策略触发条件行为
原地重试 retry_same_node网络超时、连接重置、资源暂不可用第 1 次原地重试,第 2 次起换节点
任务搬家 retry_new_nodeOOM (exit 137)、CUDA OOM、节点故障、容器丢失清空 node_id 换节点;OOM 时内存自动翻倍(上限 16GB)
不重试 no_retry镜像不存在、模块找不到、语法错误、类型错误保持 failed——用户侧错误重试多少次都一样

决策链按优先级:先判是否静态 world 组成员(是则跳过单任务重试保持 failed),再判 retry_count 是否耗尽,最后按错误分类走三档处置。

组级容错:分层恢复

单任务重试只覆盖孤立容器失败。组维度按组的形态分四层:

  • 静态 world 组(训练组、TP/PP 推理组):成员失败保持 failed 交组级流程。原因有二:静态 rendezvous 启动时已成型,单个 rank 原地重启加入不了只会挂死;单任务重试抢先把任务改回 pending 会让组级判死流程看不到 failed 信号,整组无声停在残缺态。
  • 启动期基础设施故障整组重试:全部任务从 StartedAt 起在 10 分钟内失败、且证据充分(用户侧错误一票否决)时,整组重置 pending 一起重启——静态 world 只有整组重启才能重新成型。最多 2 次(退避 1 / 3 分钟),组失败超 30 分钟不再自动捞起,防止把用户以为停掉的账单继续烧。
  • 推理组自动重建:组内没有任何活副本时,调度器每 10 秒扫描失败组,按模板链(或历史任务模板)新建一批任务,优先级 100(高于用户可设的最高档 70,恢复任务排在所有手建任务之前),最多 5 次。重建是新建不删旧行,组状态判定按 rank 与创建时间过滤历史残骸。
  • 无状态副本组单副本自愈:split / data_parallel(或未声明并行策略)的无状态多副本组,某个副本失败且组内还有活副本时,只为该 rank 补建一个新 pending 任务,健康副本一概不动。已在真实双节点验证:docker kill 杀掉一个副本后,组状态全程 running,另一副本持续应答请求,系统自动补建顶替。

节点与 GPU 层容错

  • 节点失联:心跳静默超阈值判离线后,running / scheduled / starting / waking 任务迁回 pending 换节点重排(真机验证 65 秒完成重排不卡死);stopping / draining 任务就地判终态不重跑。钉节点(requested_node_id)的任务在节点只是瞬时失联时仍尊重钉扎意愿,只有唤醒巡检确认节点永久不可承接后才清空钉扎换节点。
  • GPU 卡丢失:节点心跳正常但任务占用的卡连续 3 次不可见时,发 GPU_LOST 告警并记事件,同时把该卡从新任务分配中排除;本任务不迁移、不判失败。卡连续 2 次重新出现时自动闭合并解除排除。

任务操作

操作端点前置状态行为
取消POST /tasks/{id}?action=cancelpending / running / scheduled / sleeping / stoppingCAS 原子认领到 cancelled 后释放分片
重试POST /tasks/{id}?action=retryfailed / cancelled清空执行现场,重置 pending 入队
停止POST /tasks/{id}/stoprunningrunning → stopping,Agent 停容器后上报 stopped;不会以 completed 结束
休眠POST /tasks/{id}/sleeprunning(通常推理任务)释放 60-80% 显存
唤醒POST /tasks/{id}/wakesleeping见下方唤醒语义
更新PUT /tasks/{id}非终态终态任务一律 409,数据库零改动
删除DELETE /tasks/{id}非 running / scheduled / pending释放分片与节点资源,删记录;删空组自动收口为 stopped

唤醒的排队语义(lazy-wake):唤醒并非每次都当场完成,两种情况返回 202 Accepted(请求已受理、服务端自行推进,调用方不必重试):物理卡正被同租户的训练任务借用时返回 queued,训练任务终态后由调度器巡检自动完成唤醒;承载节点已离线时返回 rescheduling,任务迁回 pending 换在线节点重新拉起。

更新的权限边界:status、result、exit_code 等执行上报协议字段只有 Agent 系统密钥与平台运营者能提交;租户调用方(JWT 用户与租户 API Key)即使对自己的任务提交这些字段也返回 403,租户可改的任务级配置只有 idle_timeout_minutes。组内任务的休眠超时建议走任务组级更新入口——TaskGroup ID 跨重建稳定,任务 ID 会随健康检查重建而变化。

平台当前不提供任务级批量操作端点,多任务处置需逐个调用或经任务组级操作完成。

自动伸缩

AutoScaler 是 leader-only 的独立伸缩器,每 30 秒评估所有开启 auto_scale 的任务组:检查冷却时间、采集指标、按 目标副本数 = ceil(当前 × 指标值 / target) 计算、夹到 [min_replicas, max_replicas]、执行扩缩。

五种伸缩指标:

指标target_value 含义
gpu_utilization目标 GPU 利用率 (0-100)
request_queue目标队列深度
concurrent_requests目标网关并发数
vllm_pending_requests目标 vLLM 等待数
vllm_running_requests目标 vLLM 运行数

缩容的安全策略:非 split 模式保护 rank-0 只缩 worker;split 模式优先缩连接数最低的副本;新建不到 60 秒的副本不缩;draining 超 120 秒的任务自动终止。

从零扩容:组内任务全部终止但仍有伸缩配置时,AutoScaler 检测到零副本直接扩到 min_replicas。两个准入门控:租户处于停用状态时本轮跳过扩容(现有副本照常运行,缩容不受影响);实际新建数按租户剩余 GPU 配额封顶,一个都放不下则本轮跳过。调度窗口生效期间本机制让位(见下节)。

滚动更新

任务组的镜像、环境变量、卷可零停机更新。核心流程:一条 CAS 语句内取得执行租约并 generation+1 → 按批次创建新代任务 → 等新副本 running 且健康 → 旧副本置 draining 排空(网关连接排空最多 15 秒)→ 淘汰旧副本到 cancelled 终态 → 循环至全部替换。

关键设计:

  • Generation 代次追踪:新旧副本靠 tasks.generation 与 tasks.draining 两列区分,判据已持久化——历史上这两个字段只写内存读不回来,整个机制在 PostgreSQL 上失效过,现已修。
  • 淘汰终态是 cancelled 不是 completed:被淘汰的旧副本可能从未运行过,记成成功完成会污染任务成功率统计。
  • 行租约互斥:同组滚动更新互斥靠任务组行上的执行租约(TTL 60 秒、每 15 秒续期),取代会话级锁。持有者崩溃后恢复扫描最多 90 秒把组置 failed 并写明原因,之后可重新提交更新。
  • surge 临时超额与租户配额:滚动更新期间允许临时超出租户配额,上限为 surge 副本所需的卡数——满额租户也能不中断地完成更新;超出该上限拒绝。
  • 新一代继承任务意图:用户 env、模型路径、卷挂载、分片诉求、优先级等意图字段整体继承上一代;放置结果(节点、卡、分片 ID)一律清空由调度器重新分配;上一代绑定时平台写入的拓扑与分片变量会剥掉,用户自带的同名变量不动。
  • 失败回滚:新副本健康等待超时则停掉新代、恢复旧副本、状态置 rollback;批次失败同批重试最多 3 次,重试次数用尽则整体回滚并按原配置重建旧副本,组置 failed。

金丝雀发布:partition = N 只更新前 N 个副本,其余保持不变;观察金丝雀稳定后调大 partition 继续全量。

调度窗口

成本优化场景:非工作时间自动减少资源占用。配置参数:

参数说明
enabled / start_time / end_time / timezone是否启用 / 窗口起止(HH:MM)/ 时区
actionpause(缩到 0)/ scale(缩到 scale_to)/ stop(停止所有任务)
scale_toaction=scale 时必填——不传不是 0,是 400

执行机制:leader-only 控制器每 60 秒检查。窗口外执行动作并把原副本数记录到组标签(中途重启不丢恢复目标);回到窗口内 pause / scale 自动恢复,stop 是单向动作需手动拉起;跨午夜窗口(如 22:00-06:00)已支持。窗口生效期间 AutoScaler 跳过该组评估,两个机制不互相打架。静态 world 组(训练组、TP/PP)配 scale 且 scale_to>0 会 400——这类组成员数由组形状决定,缩到 N 是空操作,请用 pause / stop。

推理任务识别特殊处理

推理负载是长跑服务,与批处理任务的生命周期语义不同。判据权威是 task_type 字段:显式标注 inference 即推理;training / batch 等显式值表示明确不是推理;空串(历史存量)才走启发式兼容兜底。

识别为推理后的特殊处理:

  • 超时豁免 + 健康下限:不受 pending 超时与 running 运行超时约束;但 Agent 每次心跳刷新存活回报,running 推理任务超过 15 分钟无任何存活实测且满足多信号死亡判据才判死收回,防僵死容器永久占卡。刻意不用 GPU 空转判死——空闲推理服务的 GPU 利用率本来就是 0。
  • 空闲休眠:空闲超时自动进 sleeping,显存释放 60-80%;新请求到达秒级唤醒。
  • 自动重建:失败后自动以优先级 100 重建,最多 5 次(见智能重试节)。
  • 引擎路由:调度时注入 GPU 类型标签,Agent 据此选推理引擎(vLLM / sglang / MindIE / llama.cpp 等,从镜像名与 GPU 类型推断)。

任务事件与可观测

  • 容错事件时间线:每次真实发生的容错转移(节点失联迁移、容器丢失重排、推理组重建、checkpoint 续训注入、抢占、调度窗口动作、GPU 卡丢失与恢复等 12+ 类)在转移落库成功后写一条事件,任务详情页以时间线展示。纪律是不伪造、不回填——转移没有发生就不写,写入失败只记日志不影响主流程;保留 30 天,每任务每组上限 200 条。
  • GPU 指标:GET /tasks/{id}/gpu-metrics 按任务过滤 GPU 利用率 / 显存时间序列。
  • 能耗:GET /tasks/{id}/energy 返回累计能耗、碳排放、逐卡功耗曲线;GPU 共卡场景下任务级能耗是按份额分摊的估算,如实标注。
  • 任务统计:GET /tasks/stats 按状态计数,可见范围与任务列表同一套租户口径。

API 参考

核心端点(均需认证,路径省略 /api/v1 前缀):

方法与路径说明
POST /tasks创建任务
GET /tasks?status=&node_id=&name=&limit=&offset=任务列表(limit 默认 100 最大 1000)
GET /tasks/{id}任务详情
PUT /tasks/{id}更新(Agent CAS 模式 / 租户字段级模式)
DELETE /tasks/{id}删除(须先到终态)
GET /tasks/stats状态计数统计
GET /tasks/{id}/gpu-metrics任务级 GPU 指标
GET /tasks/{id}/fault-events容错事件时间线
GET /tasks/{id}/energy能耗与碳排放
POST /task-groups创建任务组
GET/PUT/DELETE /task-groups/{id}组查询 / 更新 / 删除
PUT /task-groups/{id}/scale手动扩缩容(replicas 必填;静态 world 组 400)
PUT /task-groups/{id}/scale-policy配置自动伸缩
PUT /task-groups/{id}/update滚动更新
PUT /task-groups/{id}/schedule-window配置调度窗口

创建任务完整示例(逐字段注释):

POST /api/v1/tasks
{
  "name": "resnet-finetune",                    // 任务名,≤100 字符
  "image": "pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime",
  "command": "python train.py --epochs 50",     // 容器内启动命令
  "gpu_required": 2,                            // 申请 2 张整卡(与分片字段互斥)
  "gpu_type": "nvidia",                         // 卡型约束,缺省不限
  "memory_mb": 8192,                            // 内存需求
  "priority": 40,                               // 前端「高」档;0-100
  "preemptible": true,                          // 允许被更高优先级任务抢占
  "timeout": 7200,                              // 运行超时 2 小时,0=不限
  "max_retries": 3,                             // 自动重试上限
  "env": ["OMP_NUM_THREADS=4"],                 // HETEROFLOW_ 前缀不可用
  "volumes": [
    { "host_path": "/data/models",              // 须落挂载白名单前缀
      "container_path": "/models",
      "read_only": true }
  ]
}

响应返回任务 ID 与初始状态 pending。任何未知字段、保留键越权、服务端独占字段都会被 400 点名拒绝——平台的原则是拼错字段当场报错,不静默吞掉。

已知边界

1. 组判死 CAS 候选不含 sleeping:某个 sleeping 组的任务走到 sleeping → failed、且组内没有其它活副本可被单副本自愈接管时,组状态可能卡在 sleeping 而其任务已终态,需人工介入。已排期待修。

2. paused 状态不可用:为将来任务级暂停预留,上报层拒绝一切转入;出边仅作存量数据捞回通道。

3. 无任务级批量操作端点:多任务处置需逐个调用或经任务组级操作。

4. 32MB / 1% 小粒度分片限额的容器内实效尚未真机复测,创建页如实标注。

5. 滚动更新部分失败可能新旧共存:持有者崩溃恢复后组置 failed 并提示重新提交,此时可能新旧副本同时在服务。

6. TF / Paddle 训练框架代码完成、GPU 真机训练未验证,属实验性(见《训练服务》篇);mpirun 仅支持单节点多进程。

7. GPU 共卡场景的任务级能耗为按份额分摊的估算值,非逐卡独立计量。

<!-- 状态标注说明: 真机验证=真实GPU端到端 / 已实现=代码+测试钉住 / 实验性=关键参数未经真机校验 -->