cann/cann-samples L1bank冲突介绍

张开发
2026/5/9 17:23:33 15 分钟阅读

分享文章

cann/cann-samples L1bank冲突介绍
L1bank冲突介绍【免费下载链接】cann-samples算子领域高性能实战演进样例与体系化调优知识库项目地址: https://gitcode.com/cann/cann-samples1. 原理介绍1.1 背景L1缓存以256 KB为粒度划分为两个独立Bank。当读写操作指向同一Bank时即触发Bank冲突导致MTE1带宽利用率下降并中断MMAD指令执行序列。为此L1双缓冲机制需要将两份待缓存数据分别放置于不同Bank通过分离读写访问的目标Bank消除冲突产生的必要条件从而保障带宽效率和指令连续性。1.2 原理原先设计中L1A ping-pong数据采用连续排布方式。硬件层L1被划分为两个Bank每个Bank大小为256KB。由于每次搬运指令的发包大小为256B一次传输必定横跨8个Bank Group在此排布下必然引发L1 Bank的读写冲突。针对这一问题可采用自主申请两块Bank空间的方式加以解决即分别申请L1一半大小的空间一块分配给Ping使用另一块分配给Pong使用从而实现Ping与Pong缓冲区的完全隔离。通过这种隔离机制可有效避免Bank内的读写冲突。2. 实践通过合理分配L1 ping和pong空间来避免bank冲突2.1 代码以一个典型的MatMul计算为例修改以下代码可避免L1 bank冲突传统写法// [Ping A][Pong A][Ping B][Pong B] for (uint64_t iter0 0; iter0 kL1TileNum; iter0) { // 计算A矩阵和B矩阵在L1缓存中单份数据所需的空间大小 uint64_t AOffsetL1 baseM * kL1 * sizeof(T); // A矩阵单份大小 M维度 × K分块大小 × 数据类型字节数 uint64_t BOffsetL1 baseN * kL1 * sizeof(T); // B矩阵单份大小 N维度 × K分块大小 × 数据类型字节数 // Ping/Pong双缓冲空间分配通过l1BufId0或1区分两块独立Bank空间 l1BufferAOffset[l1BufId] l1BufId * AOffsetL1; // A矩阵偏移Ping区从0开始Pong区紧邻A矩阵单份大小 // B矩阵偏移预留两份A矩阵空间后再根据l1BufId偏移B矩阵单份大小 l1BufferBOffset[l1BufId] tool::DOUBLE_BUFFER_COUNT * AOffsetL1 l1BufId * BOffsetL1; }切分L1给ping pong分别分配独立的空间来避免L1 bank 冲突// [Ping A][Ping B] | [Pong A][Pong B] for (uint64_t iter0 0; iter0 kL1TileNum; iter0) { // 使用L1总容量的一半作为Ping和Pong的隔离边界实现两块Bank空间的完全隔离 // l1BufId 0 对应Ping区l1BufId 1 对应Pong区 uint64_t l1Offset (AscendC::TOTAL_L1_SIZE 1) * l1BufId; l1BufferAOffset[l1BufId] l1Offset; // A矩阵在L1中的起始偏移 l1BufferBOffset[l1BufId] l1Offset baseM * kL1 * sizeof(T); // B矩阵紧邻A矩阵存放 }关键改动点:L1空间按Bank边界切分:将L1总容量的一半256KB作为Ping区和Pong区的物理隔离边界确保两块缓冲区分别位于不同的Bank中从根本上避免Bank地址重叠导致的读写冲突。Ping/Pong地址空间完全隔离:Ping区起始偏移为0Pong区起始偏移为L1总容量的一半两块空间互不重叠。每个Bank内部A矩阵与B矩阵连续存放充分利用空间的同时保持访问局部性。2.2 修改注意点L1容量边界对齐要求:切分L1空间时需确保Ping和Pong各占一半即每块大小为 TOTAL_L1_SIZE 1。若L1总容量存在其他预留空间需重新计算实际可用容量避免越界或空间浪费。3. 性能结果对比3.1 case前后性能以基础 MatMul 算子开启 double-buffer 为例在相同输入规模M512, K512, N512下进行性能测试并利用 Profiling 工具采集硬件流水线的执行状态。测试结果表明启用 L1 bank 冲突优化后原先 MTE1 搬运流水线的时间变短从而提升了算子的整体执行性能。完成 L1 bank 冲突优化后4. 结论适用场景多缓冲机制场景:当算子采用L1多缓冲机制进行流水线优化时需将Ping和Pong缓冲区分别放置于不同Bank否则连续排布会导致读写冲突削弱双缓冲的并行效果。出现带宽瓶颈当算子性能受限于MTE1搬运带宽时Bank冲突导致的串行化等待会进一步加剧带宽瓶颈通过空间隔离消除冲突可显著提升搬运效率。通过将L1缓存按Bank边界切分为Ping和Pong两个独立空间实现地址空间的完全隔离可从根本上避免L1上的读写冲突有效保障MTE1带宽利用率和MMAD指令连续性。5. 编译 执行编译样例从项目根目录启动构建参考项目README.md在仓库根目录下完成编译和安装后进入当前样例目录cmake -S . -B build -DNPU_ARCHdav-3510 cmake --build build --parallel cmake --install build --prefix ./build_out cd ./build_out/1_Features/memory_optimization/l1_bank_conflict/如需单独编译当前样例可使用以下指令cmake --build build --target l1_bank_conflict cp ./Samples/1_Features/memory_optimization/l1_bank_conflict/scripts/profile_matmul.py ./build/Samples/1_Features/memory_optimization/l1_bank_conflict/ cd ./build/Samples/1_Features/memory_optimization/l1_bank_conflict/运行样例使用可执行文件直接执行算子用例需要指定矩阵乘维度并随机生成输入数据。./l1_bank_conflict 1024 2048 4096打印如下执行结果证明样例执行成功。matmul run successfully!如果存在精度问题则会打印错误数据并显示如下结果。matmul run failed!测试性能 运行性能测试脚本指定矩阵乘法的维度后执行。python3 profile_matmul.py 1024 2048 4096打印如下执行结果证明样例性能测试成功。[Profile Breakdowm] ---------------------------------------------------------------------------------------------------- | candidate | kernel(us) | mac(us) | scalar(us) | mte1(us) | mte2(us) | fixpipe(us) | icache_miss(%) | | l1_bank_conflict | 50.871 | 40.212 | 2.842 | 11.065 | 36.999 | 2.751 | 1.000 | ----------------------------------------------------------------------------------------------------与相同规模下的基础 MatMul 算子开启 double-buffer对比[Profile Breakdowm] --------------------------------------------------------------------------------------------- | candidate | kernel(us) | mac(us) | scalar(us) | mte1(us) | mte2(us) | fixpipe(us) | icache_miss(%) | | n_buffer | 66.000 | 40.810 | 2.558 | 10.659 | 37.595 | 1.980 | 1.200 | ---------------------------------------------------------------------------------------------可以看到整体计算时间缩短性能有所提升。6. 支持架构NPU ARCH 3510【免费下载链接】cann-samples算子领域高性能实战演进样例与体系化调优知识库项目地址: https://gitcode.com/cann/cann-samples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

更多文章