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 上编译。

如果不包含该头文件,则需要自行移除或替换诸如 aclInitaclrtSetDevice 等函数调用。

CPU_SIM 内存模型

通常情况下,CPU_SIM 中所有 Tile 的内存都分配在系统内存中。这与 NPU 后端不同:在 NPU 后端中,内存会划分为 host memory、device memory,以及设备内部不同的片上存储位置。

为了让 CPU_SIM 的行为更接近 NPU,CPU_SIM 会模拟若干与 NPU 架构对应的独立内存位置。

CPU_SIM 会为每个线程分配以下内存区域:

  • UB
  • L1
  • L0A
  • L0B
  • L0C

这些区域本质上是按目标 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 的系统内存,而不是 L1L0A 等预分配缓冲区。因此它不会与这些片上模拟缓冲区重叠;只有 TASSIGN 才会使用这些模拟缓冲区。

使用建议

在 CPU_SIM 下,建议采用以下两种策略之一:

  • 直接内存绑定:为每个 Tile 显式调用 TASSIGN 绑定内存,并手动计算合适的偏移。
  • 自动分配:启用 __PTO_AUTO__,由系统自动为 Tile 分配内存;如有需要,仍可混合使用 TASSIGN

注意:

必须采用上述两种方式之一。如果既不显式绑定,也不启用自动分配,程序可能会因空指针访问而触发段错误。