ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PTO-ISA 多核编程实战:SPMD 工作划分、输出归属与负载均衡完全指南

PTO-ISA 多核编程实战:SPMD 工作划分、输出归属与负载均衡完全指南 PTO-ISA 多核编程实战SPMD 工作划分、输出归属与负载均衡完全指南【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa导读本文是 CANN pto-isa 仓库中《Multi-core Programming》的深度实战解读。文章以 PTO Tile Lib 的多核编程模型为主线系统讲解类 SPMD 工作划分、按输出归属切分 tile、负载均衡与内存局部性权衡、核间通信边界以及多核并行与单核流水线重叠如何协同。读完本文你将掌握在AICOREkernel 中用get_block_idx()定位核心身份、把大张量切成规则 tile 范围并分配到多核、在 CPU 仿真与目标 backend 上完成从单核正确实现到多核性能调优的完整开发流程。1. 概述PTO 多核编程的底层心智模型在 pto-isa 仓库中多核 kernel 普遍采用SPMD单程序多数据风格执行模型多个核心运行同一份 kernel 主体代码每个核心根据自身的block / core 身份处理不同的工作范围行、列、tile 或 block 区间。这种工作分配方式与 PTO 文档一贯的 tile 化编程模型天然契合——快速开始教程、向量加法教程与优化指南中的示例均遵循该模式。一个典型 PTO kernel 的骨架如下来自 快速开始教程#include pto/pto-inst.hpp using namespace pto; template typename T __global__ AICORE void MyKernel(__gm__ T* out, __gm__ T* in0, __gm__ T* in1) { // 使用 GlobalTensor Tile TLOAD/T* ops TSTORE }其中__gm__全局内存GM指针标记AICORE声明该函数运行在设备端的单个“core”上CPU 仿真中它只是普通函数注解GlobalTensor对 GM 数据的带 shape/stride/layout 元数据的视图详见 GlobalTensor 编程模型Tile片上 tile 对象概念上是 tile 存储中的二维缓冲详见 Tile 编程模型。2. 当前仓库中最主要的多核模型类 SPMD 的工作划分2.1 核心模式本仓库中最常见的多核写法可以概括为三条所有核心执行相同的 kernel 代码每个核心处理输入或输出的不同区域划分方式围绕行、列、tile 或 block 范围展开。这种模式天然适用于逐元素算子、基于 tile 的归约算子、GEMM 类算子以及 attention 类分块计算。仓库源码中有大量直接体现该模式的实现例如gemm_performance_kernel.cpp 中每个核心通过get_block_idx()计算自己负责的 C tile 在二维 block 网格中的位置再据此推导 A/B/C 三块张量的 GM 偏移// Work partition (SPMD-style): // - Each core owns a contiguous C tile of shape [singleCoreM, singleCoreN]. // - It reads the corresponding A panel [singleCoreM, K] and B panel [K, singleCoreN]. constexpr uint32_t mIter m / singleCoreM; uint32_t mIterIdx get_block_idx() % mIter; // get current launch core idx uint32_t nIterIdx get_block_idx() / mIter; uint64_t gmOffsetA mIterIdx * singleCoreM * k; uint64_t gmOffsetB nIterIdx * k * singleCoreN; uint64_t gmOffsetC mIterIdx * singleCoreM * n nIterIdx * singleCoreN;topk_kernel.cpp 中每个核心按get_block_idx()切走一段连续的行区间constexpr int validRow gShape0 * gShape1 * gShape2 * gShape3 / blockDim; __gm__ T* src origSrc get_block_idx() * validRow * gWholeShape4; __gm__ T* out origOut get_block_idx() * validRow * topk; __gm__ uint32_t* index origIndex get_block_idx() * validRow * topk;fa_performance_kernel.cppFlash Attention中block_idx决定行切片的起始偏移并用get_coreid()输出每个核心的 Cube/Vec 块编号用于性能画像const int block_idx get_block_idx(); const int block_offset_rows block_idx * static_castint(Cube_S0); ... __gm__ float* o_out_block o_out static_castsize_t(block_offset_rows) * static_castsize_t(HEAD_SIZE);get_block_idx()并不是凭空假设的运行时接口它在 CPU 仿真侧有真实实现cpu_stub.hpp 中它从 CPU 仿真的执行上下文pto::cpu_sim::execution_context.block_idx读取 block 编号并支持通过 hook 注入多核执行上下文。2.2 为什么这种模型更受青睐类 SPMD 划分之所以成为本仓库的默认选择是因为它与 PTO 的编程特点一致基于 tile 的工作分解每个核心天然处理一组规则 tile无需关心全局张量的整体形状可预测的 GM 访问模式核心 i 的读写区间是静态可计算的便于编译器与运行时优化更直接的负载均衡只要 tile 大小均匀各核心工作量即可近似均衡更简单的同步结构核心之间没有共享写目标时几乎不需要跨核同步。在大多数情况下让每个核心负责一段规则且连续的工作区域远比引入不规则的核间协作更容易分析与优化。3. 实际划分建议从输出归属到规则 tile 循环3.1 按输出归属划分默认策略一个稳妥的默认策略是按输出区域的归属来划分工作——负责计算某个输出 tile 的核心同时负责存储该 tile。具体到不同算子类型向量类算子按线性输出区间划分例如前文 topk 按validRow行区间切分矩阵类算子按 tile 行、tile 列或二维 block 网格划分例如前文 GEMM 的mIter × nIter网格按行归约的算子给每个核心分配一行或多行输出。这样做的好处是计算与存储同属一个核心中间状态尽量保留在本地避免核间写冲突——每个 GM 地址只有一个写入者依赖关系清晰便于后续用事件Event表达流水线。3.2 尽量保持工作量均衡为核心分配 tile 时建议尽量让每个核心承担相近的计算量避免把尾部小块工作集中到单个核心——例如行数不能被核心数整除时应把余数行分散而非堆给最后一个核心在可能的情况下同时兼顾规则访问与均衡划分。需要特别警惕的是数学上平均的划分如果破坏了内存局部性依然可能性能很差。负载均衡与局部性必须放在一起考虑详见第 4 节。3.3 保持规则的 tile 循环结构当每个核心遵循相同的 tile 循环结构时多核 kernel 更容易验证和优化。典型结构为确定当前核心负责的 tile 范围通过get_block_idx()与网格参数在该范围内迭代执行TLOAD - transform / compute - TSTORE在需要时通过valid region处理边界 tile例如 tadd 测试用例 中通过valid_row/valid_col描述有效区域。GEMM 的多核版本完整展现了这一结构gemm_performance_kernel.cpp 中每个核心对mLoop × nLoop的 tile 网格迭代内层对 K 维用MultiBufferedBUFFER_NUM双缓冲执行TLOAD - TEXTRACT - TMATMUL/TMATMUL_ACC - TSTORE。4. PTO 中真正重要的多核问题4.1 负载均衡最慢核心决定吞吐PTO kernel 常常同时包含GM 数据搬运、布局变换、vector/cube 计算与显式同步四类工作。如果某个核心分到明显更多的 tile或分到的 tile 计算代价更高例如边界 tile 需要额外 valid-region 处理、某些核心承担了额外的变换或归约整体吞吐就会被最慢核心拖累。实践中建议重点检查三点输出空间是否划分得足够均匀边界 tile 是否过度集中在少数核心是否只有部分核心承担了额外的变换或归约工作例如 topk 中每个核心还需先TLOAD一份索引数据这部分固定开销是否均衡。4.2 内存局部性划分要与 GM 访问模式协同良好的多核划分应尽量保持 GM 局部性理想模式通常具备连续的读写访问对邻近 tensor 区域的重复利用例如 GEMM 中 A 面板被多个 N 方向 tile 复用稳定的 tile shape 与 stride。局部性差的表现通常是数据搬运开销相对计算开销偏高。判断一个划分方案时建议同时评估“每核心搬运字节数”与“每核心计算量”的比值而不是只看 tile 数是否均等。4.3 核间通信一般计算 kernel 的默认边界本仓库在 docs/isa/comm/ 下系统文档化了通信指令如TPUT/TGET点对点同步通信、TPUT_ASYNC/TGET_ASYNC异步通信、TNOTIFY/TWAIT/TTEST信号同步等。但对于一般计算 kernel不应默认把任意的跨核 producer-consumer 调度视为标准日常模型。更稳妥的实践是尽量减少跨核依赖清晰划分输出归属让每个输出地址只有一个生产者仅在确有必要时保留真实的 producer-consumer 同步且同步应尽量局部化只在真正有依赖的指令对之间建立 Event 约束而不是使用宽泛的全局 barrier。如果某个 kernel 确实依赖通信指令应参考通信 ISA 参考及对应指令页面如 TPUT.md、TWAIT.md并对照指令头文件 pto_comm_inst.hpp 与类型定义 comm_types.hpp 核对签名。5. 多核并行与流水线优化的关系多核并行和流水线重叠解决的是两个不同层面的问题多核并行通过把工作分配到多个核心来提升整体吞吐流水线重叠通过重叠 load / transform / compute / store 阶段来提升单核利用率。一个高性能 kernel 往往同时需要两者合理的每核 tile 划分 高效的核内流水线。前文 GEMM 例子就是两者结合的典型外层按核心划分二维 block 网格多核并行内层用MultiBuffered双缓冲重叠TLOAD/TEXTRACT/TMATMUL单核流水线。关于重叠、缓冲与同步的细节可参考流水线并行指南与事件与同步。6. 编程边界什么不属于严谨的多核 PTO 文档多核 PTO kernel 通常围绕tile 归属、规则工作划分和显式依赖来描述。除非在专门的运行时或 backend 文档中另行定义以下内容不属于当前仓库文档范围脱离仓库上下文、凭空假设的运行时接口契约例如无依据的get_block_idx()用法——本仓库的实际实现位于 cpu_stub.hpp应以该处签名为准TCOMPUTE、TFILL这类并非当前公开 PTO intrinsic 的占位式指令在TLOAD/TSTORE中使用 Python 风格张量切片的伪语法本仓库使用GlobalTensor的 shape/stride 与指针偏移来表达访问范围把MPMD多程序多数据直接写成当前仓库普通 PTO kernel 的标准公开编程模型。这类写法在其他场景中可用于说明思路但不适合作为严谨的仓库文档引用。7. 多核开发流程从单核正确到多核调优开发多核 PTO kernel 的实用流程先从单 tile / 单核的正确实现开始——保证TLOAD - compute - TSTORE数据流在 向量加法教程 的单 tile 示例意义下正确明确每个核心的输出归属——定义输出空间如何被核心索引将工作划分为规则的 tile 范围——用get_block_idx()与网格/行区间参数计算各核心的 GM 偏移与循环边界先在 CPU 仿真上验证正确性——使用python3 tests/run_cpu.py --verbose运行参考 快速开始教程仓库提供了丰富的 CPU 单测例如 tadd 测试用例 及其黄金数据生成脚本 gen_data.py再在目标 backend 上调优 tile 大小、划分方式和重叠策略——结合 profiling 数据例如 Flash Attention kernel 中用get_coreid()打印各核心 Cube/Vec 块起止时间判断负载是否均衡再调整baseM/baseK/baseN、stepM/stepK/stepN与BUFFER_NUM等参数。这种流程与现有 PTO 文档保持一致也能避免过早引入不必要的复杂度——先确保“每个核心做对”再追求“每个核心做快”。8. 结语在当前 PTO Tile Lib 仓库中可靠的多核编程理解方式是默认采用类 SPMD 的工作划分所有核心执行同一份代码、各管一段数据以输出归属和规则 tile 范围为核心组织工作计算输出 tile 的核心同时负责存储它保持规则、连续、均衡的访问模式负载均衡与内存局部性协同考虑将多核划分与单核流水线优化结合起来多核解决吞吐、流水线解决单核利用率。相比把推测性的伪 API 或未经验证的执行模型写成既定接口这种基于仓库实际 intrinsicget_block_idx()、TLOAD/TSTORE、MultiBuffered等与真实 kernel 实现GEMM、TopK、Flash Attention的写法更准确也更符合 pto-isa 仓库的实际情况。【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表