Eager和图模式下控核介绍
简介
在多流集合通信场景下,AIV模式会存在核占满情况,此时如果进行全核同步,可能会出现死锁情况,导致用户脚本卡死。
例如在以下多流路并发场景中,设备总Vector核数为48个核。此时:
- 卡1的reduceScatter算子获取到16个vector核,卡2的dispatchCombine算子获取到24个vector核。
- NonZero算子需要一次性拿到48个vector核才会执行,并且会占用设备上的剩余核数。
- NonZero算子占用了卡1上剩余的32核,导致dispatchCombine算子无法获取所需的核数执行。卡2上NonZero算子占用了剩余的24核,导致reduceScatter算子无法执行。
- 卡1上的NonZero算子等待reduceScatter算子释放核,而reduceScatter算子等待卡2上的reduceScatter算子执行。
- 卡2上的NonZero算子等待dispatchCombine算子释放核,但dispatchCombine算子依赖卡1上的dispatchCombine算子获取到核执行。
- 卡1和卡2互相等待,导致卡1和卡2互锁卡死,算子因获取不到核导致超时。
对此,可通过对阻塞算子(NonZero)进行控核来解决。
图 1 多Stream并发场景卡死场景

使用方法
为解决上述问题,torch_npu框架提供了单算子(Eager)模式、GE图模式、aclgraph模式下的控核能力。
注意,控核是一个非强制性行为,由框架侧启用,其实际效果取决于算子侧的支持情况(使用限制参考:使用说明 > 不同算子控核说明),且需要用户自行分析模型脚本中需要指定核数的算子。在使用控核解决多流场景下算子卡死的问题时,互相卡住的算子使用的核总数不能超过当前设备物理核的总数。
-
Eager模式
单算子场景下,支持对算子进行进程级和Stream级的控核。
-
进程级控核
# 获取指定Device上的device资源限制 torch.npu.get_device_limit(device) -> Dict # 设置指定Device的Device资源限制 torch.npu.set_device_limit(device, cube_num=-1, vector_num=-1) -> None使用示例:
import torch import torch_npu torch.npu.set_device(0) torch.npu.set_device_limit(0, 12, 20) print(torch.npu.get_device_limit(0)) -
Stream级控核
# 获取指定Stream的device资源限制 torch.npu.get_stream_limit(stream) -> Dict # 设置指定Stream的Device资源限制 torch.npu.set_stream_limit(stream, cube_num=-1, vector_num=-1) -> None # 重置指定Stream的Device资源限制 torch.npu.reset_stream_limit(stream) -> None使用示例:
import torch import torch_npu batch_size = 2 hidden_size = 16 x = torch.randn(batch_size, hidden_size).npu() stream = torch.npu.current_stream() torch.npu.set_stream_limit(stream, 3, 8) with torch.npu.stream(stream): output = torch_npu.npu_swiglu(x, dim=-1)
-
-
图模式(max-autotune)
GE图模式场景下提供了算子级核数和全局核数(session)配置,其中算子级优先级高于全局核数的配置,详细说明参见AI Core和Vector Core限核功能。
-
算子级控核:
with torchair.scope.limit_core_num(op_aicore_num: int, op_vector_num: int )使用如上with语句块,语句块内的算子均使用入参指定核数。
- op_aicore_num:表示算子运行时的最大AI Core数,取值范围为[1, max_aicore]。
- op_vectorcore_num:表示算子运行时的最大Vector Core数,取值范围为[1, max_vectorcore]。
-
全局核数配置:
该功能通过torchair.get_npu_backend中compiler_config配置来指定全局最大核数,示例如下:
import torch_npu, torchair config = torchair.CompilerConfig() # 全局核数配置项 config.ge_config.aicore_num = "24|100" npu_backend = torchair.get_npu_backend(compiler_config=config) opt_model = torch.compile(model, backend=npu_backend)其中aicore_num配置项用于指定全局最大AI Core和Vector Core数,输入为字符串类型,必须用“|”来分隔。分隔符左边参数表示全局最大AI Core数,整数类型,取值范围为[1, max_aicore]。分隔符右边参数表示全局最大Vector Core数,整数类型,取值范围为[1, max_vectorcore]。
GE图模式场景下控核示例如下:
import torch, os import torch_npu import torchair from torchair.configs.compiler_config import CompilerConfig from torchair.core.utils import logger import logging logger.setLevel(logging.DEBUG) # 定义模型model class Model(torch.nn.Module): def __init__(self): super().__init__() def forward(self, in1, in2, in3, in4): # 指定算子级核数 with torchair.scope.limit_core_num(4, 5): mm_result = torch.mm(in3, in4) add_result = torch.add(in1, in2) mm1_result = torch.mm(in3, in4) return add_result, mm_result,mm1_result model = Model() config = CompilerConfig() config.debug.graph_dump.type = "pbtxt" # 指定全局核数 config.ge_config.aicore_num = "24|48" npu_backend = torchair.get_npu_backend(compiler_config=config) model = torch.compile(model, backend=npu_backend, dynamic=False, fullgraph=True) in1 = torch.randn(1000, 1000, dtype = torch.float16).npu() in2 = torch.randn(1000, 1000, dtype = torch.float16).npu() in3 = torch.randn(1000, 1000, dtype = torch.float16).npu() in4 = torch.randn(1000, 1000, dtype = torch.float16).npu() result = model(in1, in2, in3, in4) print(f"Result:\n{result}\n") -
-
图模式(npugraph_ex)
npugraph_ex后端(aclgraph)模式提供了Stream级核数配置,两种模式的底层实现并不一样,max-autotune模式实现了算子级核数配置,npugraph_ex实现了Stream级核数配置,详细说明参见AI Core和Vector Core限核功能。
接口如下:
with torch.npu.npugraph_ex.scope.limit_core_num(op_aicore_num: int, op_vector_num: int )使用如上with语句块,语句块内的算子均使用入参指定核数。
- op_aicore_num:表示算子运行时的最大AI Core数,取值范围为[1, max_aicore]。
- op_vectorcore_num:表示算子运行时的最大Vector Core数,取值范围为[1, max_vectorcore]。
其中max_aicore和max_vectorcore表示设备的实际AI Core、Vector Core数。npugraph_ex场景下控核示例如下:
import torch import torch_npu class Model(torch.nn.Module): def __init__(self): super().__init__() def forward(self, in1, in2, in3, in4): # 指定Stream级核数 with torch.npu.npugraph_ex.scope.limit_core_num(4, 5): mm_result = torch.mm(in3, in4) add_result = torch.add(in1, in2) mm1_result = torch.mm(in3, in4) return add_result, mm_result,mm1_result model = Model().npu() model = torch.compile(model, backend="npugraph_ex", dynamic=False, fullgraph=True) in1 = torch.randn(1000, 1000, dtype = torch.float16).npu() in2 = torch.randn(1000, 1000, dtype = torch.float16).npu() in3 = torch.randn(1000, 1000, dtype = torch.float16).npu() in4 = torch.randn(1000, 1000, dtype = torch.float16).npu() result = model(in1, in2, in3, in4) print(f"Result:\n{result}\n")
使用说明
-
不同算子控核说明
-
计算算子
- Ascend C算子:AI Core、AI Vector类算子控核生效,AI CPU类算子不生效。
- 其他算子(TBE、ATB等算子):无法保证控核生效。
-
通信算子
- HCCL算子:AIV模式控核生效,AI CPU模式不生效。
- MC2算子:控核生效。
-
-
npugraph_ex后端(aclgraph)模式下控核与静态Kernel共同开启
在当前处理逻辑中,支持静态编译的计算算子在静态编译后失去了Tiling的行为。这意味着除非在编译态能够传递算子的核数,否则无法对此类算子进行控核。
当前阶段通信算子没有静态Kernel,在执行的时候能够正常获取核数,不影响控核的生效。
当CANN版本小于等于CANN 8.5.0时,当同时开启静态Kernel和控核的情况下,保证控核特性优先生效,静态Kernel功能将被禁用。
当CANN版本大于CANN 8.5.0时,静态编译支持传入核数信息,因此控核和静态Kernel可以同时启用。