CPU_SIM¶
CPU_SIM 是一个面向纯 CPU 系统执行的后端实现。
与 NPU 后端相比,CPU_SIM 当前存在以下差异和限制:
- 所有操作都以同步方式执行(同步相关操作通常为空实现)。
- 使用特定的内存模型来模拟 NPU 内存层次(见下文)。
- 多线程支持尚不完整(
Tile对象的内存访问不具备线程间同步能力,因此不建议跨线程共享 Tile)。
启用 CPU_SIM¶
可通过设置编译宏 __CPU_SIM 启用 CPU 后端(CPU_SIM)。启用后,可使用标准面向 CPU 的编译器(如 gcc 或 clang)构建程序。
为兼容原本面向 NPU 的程序,仓库在 include/pto/common/cpu_stub.hpp 中为 CPU 平台提供了一些 Ascend 相关函数的替代实现。对于已经使用 NPU 后端的已有程序,包含该头文件后通常只需做少量修改即可在 CPU 上编译。
如果不包含该头文件,则需要自行移除或替换诸如 aclInit、aclrtSetDevice 等函数调用。
CPU_SIM 内存模型¶
通常情况下,CPU_SIM 中所有 Tile 的内存都分配在系统内存中。这与 NPU 后端不同:在 NPU 后端中,内存会划分为 host memory、device memory,以及设备内部不同的片上存储位置。
为了让 CPU_SIM 的行为更接近 NPU,CPU_SIM 会模拟若干与 NPU 架构对应的独立内存位置。
CPU_SIM 会为每个线程分配以下内存区域:
UBL1L0AL0BL0C
这些区域本质上是按目标 NPU 架构容量预分配的数组。TASSIGN 会从这些数组中为 Tile 绑定某一段内存。例如:
- 若对
Loc == Mat的 Tile 调用TASSIGN(tile, 10),则该 Tile 会绑定到L1[10]开始的位置。
当前支持的架构包括 A2A3 和 A5。可通过 pto::NPUMemoryModel::Initialize 为每个线程指定要模拟的架构;该函数应在每个线程中调用一次。
- 若不显式调用,则默认使用 A2A3 架构。
更多信息请参考 include/pto/cpu/NPUMemoryModel.hpp。
自动内存分配¶
CPU_SIM 也支持自动内存分配。
启用方式是定义编译宏 __PTO_AUTO__。启用后,会使用惰性分配机制:当首次尝试获取 Tile 的内部内存指针时,如果此前未通过 TASSIGN 绑定内存,则自动为其分配内存。
需要注意的是,在自动分配模式下,内存来自 PC 的系统内存,而不是 L1、L0A 等预分配缓冲区。因此它不会与这些片上模拟缓冲区重叠;只有 TASSIGN 才会使用这些模拟缓冲区。
使用建议¶
在 CPU_SIM 下,建议采用以下两种策略之一:
- 直接内存绑定:为每个 Tile 显式调用
TASSIGN绑定内存,并手动计算合适的偏移。 - 自动分配:启用
__PTO_AUTO__,由系统自动为 Tile 分配内存;如有需要,仍可混合使用TASSIGN。
注意:
必须采用上述两种方式之一。如果既不显式绑定,也不启用自动分配,程序可能会因空指针访问而触发段错误。