# torch_npu/__init__.py:22-38
acc = torch._C._get_accelerator()
if acc.type != "cpu":
raise RuntimeError(
f"Two accelerators cannot be used at the same time "f"in PyTorch: npu and {acc.type}. You can install "f"the cpu version of PyTorch to use your npu device, "f"or use the {acc.type} device with "f"'export TORCH_DEVICE_BACKEND_AUTOLOAD=0'."
)
torch_npu 初始化流程详解
目录
概述
torch_npu 是华为昇腾 NPU 的 PyTorch 扩展库,通过 PyTorch 的插件机制实现无缝集成。本文档详细分析 torch_npu 的初始化流程,包括:
自动加载机制
Entry Points 机制
# setup.py:753-760 entry_points={ 'console_scripts': [ 'torch_npu_run = torch_npu.distributed.run:_main', ], 'torch.backends': [ 'torch_npu = torch_npu:_autoload', ], }这是 PyTorch 2.0+ 提供的设备后端自动加载机制。当用户执行
import torch时,PyTorch 会自动扫描已安装包中注册的torch.backends入口点,并调用对应的函数。自动加载流程
_autoload 函数实现
# torch_npu/__init__.py:333-335 def _autoload(): # 恢复环境变量,子进程需要继承其值 os.environ["TORCH_DEVICE_BACKEND_AUTOLOAD"] = ORG_AUTOLOAD这个函数非常简单,只是恢复了环境变量。真正的初始化在模块加载时完成。
完整初始化流程
步骤概览
__init__.py:14-15__init__.py:22-38__init__.py:40-66__init__.py:209-215__init__.py:217-221__init__.py:238步骤 1: 防止循环依赖
# torch_npu/__init__.py:14-15 ORG_AUTOLOAD = os.getenv("TORCH_DEVICE_BACKEND_AUTOLOAD", "1") os.environ["TORCH_DEVICE_BACKEND_AUTOLOAD"] = "0"在
import torch之前临时禁用自动加载,避免循环依赖。步骤 2: 检查加速器冲突
# torch_npu/__init__.py:22-38 acc = torch._C._get_accelerator() if acc.type != "cpu": raise RuntimeError( f"Two accelerators cannot be used at the same time " f"in PyTorch: npu and {acc.type}. You can install " f"the cpu version of PyTorch to use your npu device, " f"or use the {acc.type} device with " f"'export TORCH_DEVICE_BACKEND_AUTOLOAD=0'." )确保系统中没有其他加速器(如 CUDA)同时启用。
步骤 3: 加载核心模块
# torch_npu/__init__.py:40-66 import torch_npu.npu # 核心 NPU 功能 import torch_npu._C # C++ 扩展模块 import torch_npu.npu.amp # 自动混合精度 import torch_npu.optim # 优化器 import torch_npu.dynamo # Dynamo 支持步骤 4: 注册 NPU 设备(最关键!)
# torch_npu/__init__.py:209-215 # 将 PyTorch 预留的 privateuse1 设备重命名为 'npu' torch.utils.rename_privateuse1_backend("npu") # 注册设备模块,使得可以访问 torch.npu torch._register_device_module('npu', torch_npu.npu) # 自动生成设备相关方法 unsupported_dtype = [torch.quint8, torch.quint4x2, torch.quint2x4, torch.qint32, torch.qint8] torch.utils.generate_methods_for_privateuse1_backend( for_tensor=True, # 生成 torch.Tensor.npu() for_module=True, # 生成 torch.npu.* 方法 for_storage=True, # 生成存储相关方法 unsupported_dtype=unsupported_dtype )这行代码的效果:
torch.Tensor.npu()- 将张量移动到 NPUtorch.npu.is_available()- 检查 NPU 是否可用torch.npu.device_count()- 获取 NPU 数量torch.npu.current_device()- 获取当前设备步骤 5: 应用 Monkey Patches
# torch_npu/__init__.py:217-221 _apply_patches(all_monkey_patches) # 应用各种补丁 _apply_class_patches() # 类级别补丁 _asd_patch() # ASD 补丁 _except_handler.patch_excepthook() # 异常处理补丁步骤 6: 初始化 C++ 扩展
# torch_npu/__init__.py:238 torch_npu._C._initExtension()torch._C vs torch_npu._C
这两个是完全不同的模块,分别属于不同的包:
对比表格
torch._Ctorch_npu._CPyInit__C()PyInit__C()torch._Ctorch_npu._Ctorch._C(PyTorch 核心)
典型使用:
import torch # torch._C 是 PyTorch 的内部模块 torch._C._is_device_backend_autoload_enabled() torch._C._get_accelerator() torch._C._cuda_getDeviceCount()torch_npu._C(NPU 扩展)
典型使用:
import torch_npu # torch_npu._C 是 torch_npu 的内部模块 torch_npu._C._npu_init() torch_npu._C._npu_setDevice(0) torch_npu._C._npu_synchronize() torch_npu._C._initExtension()加载时机
总结
torch._Ctorch_npu._C两者虽然入口函数名相同(
PyInit__C),但模块名不同,是独立的两个模块,互不冲突。PyInit__C 调用时机
触发位置
# torch_npu/__init__.py:66 import torch_npu._C # 触发 PyInit__C()调用链
PyInit__C 函数实现
// torch_npu/csrc/InitNpuBindings.cpp:216-219 PyMODINIT_FUNC PyInit__C(void) { return initModule(); }initModule() 函数
// torch_npu/csrc/InitNpuBindings.cpp:165-214 PyObject* initModule() { // 1. 初始化线程数 at::internal::lazy_init_num_threads(); // 2. 注册 Python 方法 AddPyMethodDefs(methods, TorchNpuMethods); AddPyMethodDefs(methods, TorchSanitizerMethods); AddPyMethodDefs(methods, THNPModule_get_methods()); AddPyMethodDefs(methods, torch_npu::utils::npu_extension_functions()); // 3. 创建 Python 模块 torch_npu._C static struct PyModuleDef torchnpu_module = { PyModuleDef_HEAD_INIT, "torch_npu._C", // 模块名 nullptr, -1, methods.data() }; module = PyModule_Create(&torchnpu_module); // 4. 初始化核心对象 THNPStream_init(module); // Stream 对象 THNPEvent_init(module); // Event 对象 THNPGraph_init(module); // Graph 对象 THNPMemPool_init(module); // MemPool 对象 // 5. 注册设备属性和内存 RegisterNPUDeviceProperties(module); BindGetDeviceProperties(module); RegisterNPUDeviceMemories(module); // 6. 初始化通信和 profiler initCommMethods(); torch_npu::profiler::initMstx(module); return module; }Python C 扩展机制
基本原理
Python 解释器是用 C 语言实现的(CPython),它提供了 C API 让开发者可以用 C/C++ 编写扩展模块。
核心组件
1. 模块定义 (PyModuleDef)
static struct PyModuleDef module_def = { PyModuleDef_HEAD_INIT, // 必须的头部 "模块名", // 模块名称 nullptr, // 模块文档 -1, // 模块状态大小 methods // 方法数组 };2. 方法定义 (PyMethodDef)
static PyMethodDef methods[] = { // Python 方法名, C 函数指针, 调用方式, 文档 {"_initExtension", (PyCFunction)THPModule_initExtension, METH_NOARGS, nullptr}, {"_npu_init", (PyCFunction)THNPModule_initExtension, METH_NOARGS, nullptr}, {"_npu_setDevice", (PyCFunction)THNPModule_setDevice_wrap, METH_O, nullptr}, {nullptr, nullptr, 0, nullptr} // 结束标记 };3. 模块初始化函数 (PyInit_xxx)
// 命名规则:PyInit_模块名 // 模块名 mymodule → PyInit_mymodule // 模块名 torch._C → PyInit_torch__C(点号替换为下划线) PyMODINIT_FUNC PyInit_mymodule(void) { return PyModule_Create(&module_def); }4. C 函数实现
// C 函数签名必须符合 Python 要求 // 参数:self (模块或实例), args (位置参数) static PyObject* THPModule_initExtension(PyObject* self, PyObject* noargs) { // 执行 C++ 代码 _initialize_python_bindings(); // 返回 Python None Py_RETURN_NONE; }参数传递方式 (METH_xxx)
METH_NOARGSfunc()METH_Ofunc(arg)METH_VARARGSfunc(*args)METH_KEYWORDSfunc(**kwargs)METH_STATICClass.func()METH_CLASSClass.func(cls)调用流程
常用 CPython API
PyModule_Create()PyModule_AddObject()PyArg_ParseTuple()PyArg_ParseTupleAndKeywords()Py_BuildValue()Py_RETURN_NONEPyLong_FromLong()PyUnicode_FromString()PyErr_SetString()PyImport_ImportModule()完整示例
// ============== C++ 代码 ============== // 1. 定义 C 函数 static PyObject* add_numbers(PyObject* self, PyObject* args) { long a, b; // 解析参数:两个整数 if (!PyArg_ParseTuple(args, "ll", &a, &b)) { return nullptr; // 解析失败,返回 NULL 表示异常 } // 计算结果 long result = a + b; // 返回 Python 整数对象 return PyLong_FromLong(result); } // 2. 方法定义 static PyMethodDef MyMethods[] = { {"add", add_numbers, METH_VARARGS, "Add two integers"}, {nullptr, nullptr, 0, nullptr} }; // 3. 模块定义 static struct PyModuleDef mymodule = { PyModuleDef_HEAD_INIT, "mymodule", "My example module", -1, MyMethods }; // 4. 初始化函数 PyMODINIT_FUNC PyInit_mymodule(void) { return PyModule_Create(&mymodule); }# ============== Python 代码 ============== import mymodule result = mymodule.add(3, 5) # 调用 C 函数 print(result) # 8_initExtension 调用流程
完整调用链
代码详解
1. Python 调用入口
# torch_npu/__init__.py:238 torch_npu._C._initExtension()2. C++ 方法注册
// torch_npu/csrc/utils/TensorType.cpp:337-340 static PyMethodDef TorchNpuExtensionMethods[] = { {"_initExtension", (PyCFunction)THPModule_initExtension, METH_NOARGS, nullptr}, {nullptr, nullptr, 0, nullptr} };3. 方法添加到模块
// torch_npu/csrc/InitNpuBindings.cpp:176 AddPyMethodDefs(methods, torch_npu::utils::npu_extension_functions());其中
npu_extension_functions()返回TorchNpuExtensionMethods数组:// torch_npu/csrc/utils/TensorType.cpp:342-345 PyMethodDef *npu_extension_functions() { return TorchNpuExtensionMethods; }4. C++ 实现函数
// torch_npu/csrc/utils/TensorType.cpp:328-334 static PyObject *THPModule_initExtension(PyObject *_unused, PyObject *noargs) { HANDLE_TH_ERRORS _initialize_python_bindings(); // 核心:初始化 Python 绑定 Py_RETURN_NONE; END_HANDLE_TH_ERRORS }5. 初始化 Python 绑定
// torch_npu/csrc/utils/TensorType.cpp:266-291 void _initialize_python_bindings() { // 1. 初始化 NPU ATen 类型 initialize_npu_aten_types(tensor_types); // 2. 初始化 Python 元类 py_initialize_metaclass(metaclass); // 3. 获取张量字典 auto tensor_dict = get_tensor_dict(); // 4. 初始化每个 Python 类型对象 // torch.npu.FloatTensor, torch.npu.DoubleTensor 等 for (auto &tensor_type : tensor_types) { py_initialize_tensor_type(tensor_type.py_type, tensor_type.name, tensor_dict.get()); } // 5. 将类型对象添加到模块 py_bind_tensor_types(tensor_types); }时序图
与 _npu_init 的区别
_initExtension()_npu_init()# torch_npu/__init__.py # 第238行:初始化 Python 绑定(不初始化 ACL) torch_npu._C._initExtension() # _npu_init 在 _lazy_init() 中调用(首次使用 NPU 时) # torch_npu/npu/__init__.py:271 torch_npu._C._npu_init()aclInit 调用时机
完整调用链
NpuSysCtrl::Initialize() 详细流程
// torch_npu/csrc/core/npu/sys_ctrl/npu_sys_ctrl.cpp:145-248 NpuSysCtrl::SysStatus NpuSysCtrl::Initialize(int device_id) { // 1. 双重检查锁定,防止重复初始化 if (init_flag_) { return INIT_SUCC; } std::lock_guard<std::mutex> lock(init_mutex_); if (init_flag_) { return INIT_SUCC; } // 2. 获取 ACL 配置文件路径 std::string json_path = GetAclConfigJsonPath(); const char *json_path_ptr = json_path == "" ? nullptr : json_path.c_str(); // ★★★ 3. 调用 aclInit - 这是 ACL 初始化的关键接口!★★★ auto init_ret = aclInit(json_path_ptr); // 第157行 // 处理重复初始化的情况 if (init_ret == ACL_ERROR_REPEAT_INITIALIZE) { repeat_init_acl_flag_ = false; ASCEND_LOGI("acl has allready init by other component."); } else if (init_ret != ACL_ERROR_NONE) { NPU_CHECK_ERROR(init_ret, "aclInit"); } // 4. 初始化 Dump 功能 if (c10_npu::option::OptionsManager::CheckAclDumpDateEnable()) { NPU_CHECK_ERROR(aclmdlInitDump()); } // 5. 初始化内存分配器 c10_npu::NPUCachingAllocator::init(); c10_npu::NPUWorkspaceAllocator::init(); // 6. 设置设备 if (!c10_npu::is_lazy_set_device()) { auto ret = aclrtGetDevice(&device_id_); if (ret != ACL_ERROR_NONE) { device_id_ = (device_id == -1) ? 0 : device_id; NPU_CHECK_ERROR(c10_npu::SetDevice(device_id_)); } } // 7. 获取 SoC 版本 auto soc_name = c10_npu::acl::AclGetSocName(); c10_npu::SetSocVersion(soc_name); // 8. 设置浮点溢出模式 if (!c10_npu::is_lazy_set_device()) { if (c10_npu::IsSupportInfNan()) { c10_npu::acl::AclrtSetDeviceSatMode( aclrtFloatOverflowMode::ACL_RT_OVERFLOW_MODE_INFNAN); } else { c10_npu::acl::AclrtSetDeviceSatMode( aclrtFloatOverflowMode::ACL_RT_OVERFLOW_MODE_SATURATION); } } // 9. 初始化算子编译模式 auto acl_op_init_mode = c10_npu::option::OptionsManager::GetAclOpInitMode(); if (acl_op_init_mode == 0) { at_npu::aclops::InitAclops(); } else { at_npu::aclops::InitializeJitCompilationMode(); } // 10. 设置确定性计算 if (!c10_npu::is_lazy_set_device()) { SetDeterministicFromLevel(); NPU_CHECK_ERROR(c10_npu::acl::AclrtSetOpExecuteTimeOut(kMaxOpExecuteTimeOut)); } init_flag_ = true; return INIT_SUCC; }ACL 配置文件
// torch_npu/csrc/core/npu/sys_ctrl/npu_sys_ctrl.cpp:108-130 std::string GetAclConfigJsonPath() { std::string npu_path = GetTorchNpuFile(); std::string json_path = ""; // 根据是否延迟设置设备选择不同的配置文件 if (c10_npu::is_lazy_set_device()) { json_path = npu_path.append("torch_npu/acl_default.json"); } else { json_path = npu_path.append("torch_npu/acl.json"); } return json_path_str; }配置文件位置:
torch_npu/acl.json- 标准配置torch_npu/acl_default.json- 延迟初始化配置初始化时序图
_lazy_init 触发时机
延迟初始化函数
# torch_npu/npu/__init__.py:242-282 def _lazy_init(): global _initialized, _original_pid, _queued_calls if _initialized or hasattr(_tls, 'is_initializing'): return with _initialization_lock: if _initialized: return # 核心:调用 C++ 初始化函数 torch_npu._C._npu_init() _original_pid = os.getpid() _tls.is_initializing = True try: _queue_call(_queued_calls) finally: delattr(_tls, 'is_initializing') _initialized = TruePython 层触发点
1. 设备相关操作
synchronize()npu/utils.py:70current_device()npu/utils.py:95device.__enter__()npu/utils.py:1172. 内存相关操作
mem_get_info()npu/__init__.py:495memory_stats()npu/memory.py:139empty_cache()npu/memory.py:8693. 随机数相关操作
get_rng_state_offset()npu/__init__.py:349manual_seed()npu/random.py:23seed()npu/random.py:1644. 设备属性查询
get_device_properties()npu/__init__.py:485utilization()npu/__init__.py:5665. 其他操作
init()npu/__init__.py:239ipc_collect()npu/utils.py:84C++ 层触发点
// torch_npu/csrc/utils/LazyInit.cpp:13-31 void npu_lazy_init() { pybind11::gil_scoped_acquire g; if (!npu_run_yet) { // 导入 Python 模块 auto module = THPObjectPtr(PyImport_ImportModule("torch_npu.npu")); if (!module) { throw python_error(); } // 调用 Python 的 _lazy_init 方法 auto res = THPObjectPtr(PyObject_CallMethod(module.get(), "_lazy_init", "")); if (!res) { throw python_error(); } npu_run_yet = true; } }C++ 层调用位置:
// torch_npu/csrc/core/npu/NPUHooksInterface.cpp:28-33 void NPUHooksInterface::init() const { torch_npu::utils::npu_lazy_init(); } // torch_npu/csrc/npu/Module.cpp PyObject* THNPModule_setDevice_wrap(...) { torch_npu::utils::npu_lazy_init(); // ... } PyObject* THNPModule_getDevice_wrap(...) { torch_npu::utils::npu_lazy_init(); // ... }典型触发场景
import torch # 此时 _lazy_init() 尚未调用 # 场景 1: 创建 NPU 张量 x = torch.ones(5, device="npu") # ↓ # Tensor.__new__ → NPUHooksInterface::init() → _lazy_init() # 场景 2: 查询设备 torch.npu.current_device() # ↓ # current_device() → _lazy_init() # 场景 3: 同步设备 torch.npu.synchronize() # ↓ # synchronize() → _lazy_init() # 场景 4: 设置随机种子 torch.npu.manual_seed(42) # ↓ # manual_seed() → _lazy_init() # 场景 5: 显式初始化 torch.npu.init() # ↓ # init() → _lazy_init()常见问题解答
Q1:
torch.tensor([1])会触发 NPU 初始化吗?答案:不会。
torch.tensor([1])默认在 CPU 上创建张量,不会触发 NPU 初始化。import torch # 情况1: 不触发 NPU 初始化 x = torch.tensor([1]) print(x.device) # cpu # 情况2: 触发 NPU 初始化 y = torch.tensor([1], device="npu") print(y.device) # npu:0 # 情况3: 先创建 CPU 张量,再移动到 NPU(也会触发) z = torch.tensor([1]).npu() print(z.device) # npu:0Q2: 为什么采用延迟初始化?
import torch时不需要初始化 ACL,加快启动速度Q3: 如何判断 NPU 是否已初始化?
import torch # 方法1: 检查初始化状态 print(torch.npu.is_initialized()) # 方法2: 检查是否可用(会触发初始化) print(torch.npu.is_available())Q4: torch._C 和 torch_npu._C 有什么区别?
torch._Ctorch_npu._C两者是独立的模块,互不冲突。
Q5: _initExtension 和 _npu_init 有什么区别?
_initExtension()_npu_init()Q6: 初始化流程总结
torch_npu._autoload()PyInit__C()_initExtension()rename_privateuse1_backend("npu")torch_npu.npu._lazy_init()THNPModule_initExtension()aclInit()aclrtSetDevice()NPUCachingAllocator::init()附录:关键文件路径
setup.py:753-760torch_npu/__init__.pytorch_npu/__init__.py:209-215torch_npu/csrc/InitNpuBindings.cpptorch_npu/csrc/utils/TensorType.cpptorch_npu/npu/__init__.py:242-282torch_npu/csrc/npu/Module.cpp:662-697torch_npu/csrc/core/npu/sys_ctrl/npu_sys_ctrl.cpptorch_npu/csrc/utils/LazyInit.cpptorch_npu/csrc/core/npu/impl/NPUGuardImpl.cpp参考资料