基于Ascend C的FlashAttention算子性能优化最佳实践

 LLM的Attention部分处理给计算系统带来巨大的计算和访存压力。业界先后出现FlashAttention、FlashAttention2等算法,通过计算等价和切分有效降低HBM数据访问量。 
昇腾异构计算架构CANN针对昇腾AI处理器的片上内存和缓存大小,以及数据搬运通路,基于Ascend C算子编程语言优化实现FlashAttention融合算子,充分利用片上缓存,提升Attention处理性能。根据实测,在一些典型场景中CANN的FlashAttention算子相比小算子取得了5倍以上的性能提升,开发者可直接调用相关算子API接口使能大模型极致性能优化。
本文针对FlashAttention反向融合算子的性能优化方案展开介绍,并通过优化实现了典型场景4倍左右的性能提升,希望对开发者优化此类基于Ascend C开发的融合算子带来启发。 

FlashAttention算法简介 

在主流大模型网络模型中,大量使用典型的Multi-Head Attention结构,带来了巨大的计算和内存开销。其运行过程中,矩阵乘和softmax结果存放在片上内存会带来巨大的内存消耗,访存性能严重下降,甚至会导致模型无法正常运行,同时网络中的矩阵和向量计算串行执行,也会导致硬件算力发挥受限。

斯坦福的Tri DAO提出了FlashAttention融合算子,其原理是对attention处理过程进行切分和计算等价,使得attention的多个步骤在一个算子中完成,并且通过多重循环、每次处理一小部分数据,以近似流式的方式访问片上内存,减少了片上内存访问的总数据量,并能够将计算和数据搬运更好的重叠隐藏。

 注意力的正向计算公式为:

为方便表达,以变量S和P表示计算公式: 

注意力的反向计算公式为: 

昇腾CANN基于Ascend C编程语言实现了FlashAttention正反向融合算子,其中反向算子计算流程可参考下图所示: 

本案例对FlashAttention反向算子进行了性能优化,主要涉及的优化手段包括tiling基本块大小调整,核间负载均衡,CV流水并行,MTE2流水优化以及FixPipe流水优化等,并在Atlas A2训练系列产品/Atlas 800I A2推理产品 验证平台下收益4倍左右的性能提升。下面以如下两个输入场景为例,介绍整个优化过程。

  • 第一个场景的输入维度信息为:B=1,N1=12,N2=12,S1=6144,S2=6144,D=128,并且为casual场景,casual场景即atten_mask的形状为下三角。
  • 第二个场景的输入维度信息为:B=24,N1=5,N2=5,S1=9216,S2=9216,D=64,不带atten_mask和drop_mask输入。

tiling基本块调整 

 根据以往优化的经验,循环间可能存在一些不必要的头开销,循环越多性能可能越差;满足UB最大空间限制的情况下,UB切分的基本块越大,循环越少,算子中通过InitBuffer接口分配UB buffer大小。

pipe->InitBuffer(ubBuffer, 120 * 1024);   
pipe->InitBuffer(tmpBuffer, 30 * 1024);   
pipe->InitBuffer(vecClc3, 8 * 1024);

 如上代码所示,InitBuffer接口的第二个参数表示buffer占用的大小,所有buffer大小的和即为占用的总空间。这里120 * 1024 + 30 * 1024 + 8 * 1024 = 158KB < UB Size,没有充分利用UB空间。
接下来试图通过调整tiling基本块进行性能优化,在满足UB空间大小够用的情况下,tiling基本块切分的越大越好。下图为优化前按照(64, 128)切分计算,总共需要循环计算32次:

考虑到UB空间没有用满,基本块调整到(128, 128),如下图优化后只需循环计算16次,切分后算子性能提升一倍:

 CV流水并行

 从流水图可以看到,可以看出两侧的流水都存在大段的空隙(图中绿色为vector部分流水,橙色为cube侧流水),CV之间流水很大程度上未并行,需要考虑CV流水优化。

 由于FAG算子中cube计算比vector计算快且存在依赖性,同时为了减少CV之间的通信次数,通过缓存机制实现让matmul提前计算多块,这里的缓存机制指的是将mm一次性计算多个基本块缓存到GM上。如下代码中,SetTail设置的SingleM和SingleN大小为BaseM,BaseN的倍数,即matmul一次发起多个基本块的计算,实现matmul结果的缓存,vector侧分多次取matmul的结果。

mm3.SetTail(s2CvExtend, -1, preS1Extend);   
mm3.SetTensorA(mulWorkSpaceGm[pingpongIdx * coreNum * cubeBaseMN + cBlockIdx * cubeBaseMN], true);  
mm3.SetTensorB(queryGm[mm2aTensorOffsetCv]);   
mm3.template IterateAll<false>(dkWorkSpaceGm[bTensorOffsetCv], true);

下图是实现mm1、mm2和mm3缓存的流水图,绿色的vector流水与橙色的cube流水均变得更密集,并行度提高,cv的间隔减小,提升了算子性能:  

基于缓存mm1/mm2/mm3的优化后,在本轮Vector等Cube流水的间隔,插入下一轮循环的Vector计算,这样使Vector流水与Cube流水之间的并行度更高,反映到流水图中为Vector计算更密集: 

相关优化点实现伪代码如下所示: 

 mm1计算;
dropout();
Sub();
dropout(); // 下一轮循环的Vector计算 
Sub();  // 下一轮循环的Vector计算 
mm2计算;
Softmax();
AttenMask();
...

 核间负载均衡

对于上述场景一,casual场景下可能存在核间分布不均匀的情况,如下图经过atten mask掩码后,红色部分是算子需要计算的部分,绿色无需计算;如果不按照基本块的个数来分核,按照第一根轴的大小8(行)来分核,假设平均分到9个核上,每个核做ceil(8 / 9) = 1行,则第一个核只需做1个基本块,但是第8个核需要做8个基本块的计算,出现严重的负载不均衡: 

因此需要考虑将红色块均匀分到多个核上计算,尽量实现每个核的计算量均匀,负载均衡。优化后,红色块总共36个基本块,均分到每个核上,每个核的计算量为4块,性能提升一倍。

 FixPipe流水优化

通过对场景一的Profilling数据进行分析可以看到,aic_fixpipe_ratio占比极高,占比高达81%,出现了很严重的bound: 

同时,CAModel工具打印发现存在很多异常的128B搬运,经过代码排查,发现workspace地址未512B对齐。代码实现中使用SetGlobalBuffer接口设置workspace的起始地址,如果起始地址不是按照512B对齐,搬运效率会很低,可以强制地址512B对齐来避免这个情况,下面代码中ADDR_ALIGN_SIZE即为512:

// init workspace address   
syncGlobal.SetGlobalBuffer((__gm__ int32_t*)workspace);   
uint64_t workspaceOffsets = SYNC_GLOBAL_WORKSPACE_SIZE;   
dqWorkSpaceGm.SetGlobalBuffer((__gm__ float*)workspace + workspaceOffsets / sizeof(T2));   
workspaceOffsets = (workspaceOffsets + qPostBlockTotal * sizeof(float) + ADDR_ALIGN_SIZE) / ADDR_ALIGN_SIZE * ADDR_ALIGN_SIZE;  dkWorkSpaceGm.SetGlobalBuffer((__gm__ float*)workspace + workspaceOffsets / sizeof(T2));   
workspaceOffsets = (workspaceOffsets + kvPostBlockTotal * sizeof(float) + ADDR_ALIGN_SIZE) / ADDR_ALIGN_SIZE * ADDR_ALIGN_SIZE;  dvWorkSpaceGm.SetGlobalBuffer((__gm__ float*)workspace + workspaceOffsets / sizeof(T2));   
workspaceOffsets = (workspaceOffsets + kvPostBlockTotal * sizeof(float) + ADDR_ALIGN_SIZE) / ADDR_ALIGN_SIZE * ADDR_ALIGN_SIZE;  
// matmul1 and matmul2 workspace size   
matmulWorkspaceSize = cubeBaseMN * sizeof(float);  
mm1WorkspaceGm.SetGlobalBuffer((__gm__ T2*)(workspace + workspaceOffsets + cBlockIdx * matmulWorkspaceSize));  mm2WorkspaceGm.SetGlobalBuffer((__gm__ T2*)(workspace + workspaceOffsets + coreNum * matmulWorkspaceSize + cBlockIdx * matmulWorkspaceSize));   // drop workspace offset   
workspaceOffsets = (workspaceOffsets + coreNum * cubeBaseMN * sizeof(float) * INPUT_NUMS + ADDR_ALIGN_SIZE) / ADDR_ALIGN_SIZE * ADDR_ALIGN_SIZE;   
dropWorkSpaceGm.SetGlobalBuffer((__gm__ T1*)workspace + workspaceOffsets / sizeof(T1));    
// mul workspace offset   
workspaceOffsets = (workspaceOffsets + coreNum * cubeBaseMN * sizeof(half) * 2 + ADDR_ALIGN_SIZE) / ADDR_ALIGN_SIZE * ADDR_ALIGN_SIZE;   
mulWorkSpaceGm.SetGlobalBuffer((__gm__ T1*)workspace + workspaceOffsets / sizeof(T1));

 修改代码,workspace地址经过512B对齐后,fixpipe时间减半:

 MTE2流水优化

 从场景二采集的profiling和打点图来看,mte2_ratio占比高,cube MTE2出现了明显bound,且部分MTE2搬运时间异常。

 

将输入数据排布格式从BSH更改为BNSD后,数据搬运连续,不需要跳地址读取数据,搬运效率提升一倍,部分异常搬运时长降低了一半。 

 优化方案性能收益

  • 调整tiling基本块:理论评估vector切块越大,计算和搬运循环次数越少,同时能够充分利用搬运带宽和vector算力。基本块大小从(64, 128)增大到(128, 128)后,性能提升一倍,实测与理论分析一致。
  • CV流水并行:CV流水掩盖的时间即为提升的性能,符合预期的收益。
  • 核间负载均衡:优化前负载最多的核的计算量减少的倍数,即为预期提升的性能;案例中优化前负载最多的核的计算量大小为8块,优化后为4块,实际性能提升一倍,符合预期的收益。
  • FixPipe优化:从Profiling数据看出FixPipe占比0.8,优化后占比0.55,实测算子性能提升45%,与理论分析一致。
  • MTE2优化:从Profiling数据看出MTE2占比0.52,优化后占比减少一半,实测算子性能提升30%,与理论分析一致。

 开发者在对基于Ascend C开发的融合算子进行性能优化时,可参考此案例中的优化思路。

更多学习资源 

 了解更多Ascend C算子性能优化手段和实践案例,请访问:昇腾Ascend C-入门课程-学习资源-算子文档-昇腾社区

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/25673.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为Nanopi m1交叉编译opencv

为Nanopi m1交叉编译opencv 一、下载交叉编译器 根据之前的博客进行 二、下载opencv和必要库 sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-devgit clone https://github.com/opencv/opencv.git cd opencv三、进行编…

鸿蒙开发必备:《DevEco Studio 系列一:实用功能解析与常用快捷键大全》

系列文章目录 文章目录 系列文章目录前言一、下载与安装小黑板 二、IDE被忽略的实用功能-帮助&#xff08;Help&#xff09;1.Quick Start2. API Reference3.FAQ 三、常用快捷键一、编辑二、查找或替换三、编译与运行四、调试五、其他 前言 DevEco Studio&#xff09;是基于In…

nginx-虚拟主机如何配置

8、 nginx 命令功能 nginx -c /path/nginx.conf # 以特定目录下的配置文件启动nginx: nginx -s reload # 修改配置后重新加载生效 nginx -s stop # 快速停止nginx nginx -s quit # 正常停止nginx nginx -t # 测试当前配置文件…

协程库——面试问题

1 同步、异步 1.1 同步 代码顺序执行&#xff0c;完全由用户控制. 同步阻塞 等待可读、可写的时候阻塞&#xff0c;不让出cpu。读、写之后&#xff0c;下面的代码才能执行、 同步非阻塞 等待可读、可写时&#xff0c;不会阻塞cpu&#xff0c;返回失败&#xff0c;设置错误码为…

I/O 系统的功能、模型与接口

目录 I/O 系统的基本功能 1. 设备独立性 2. 缓冲 3. 设备共享 4. 高速缓存 5. 设备管理 I/O 系统的层次结构与模型 1. 单块传输模型 2. 缓冲管理模型 3. 通道模型 4. 虚拟设备模型 5. 直接内存访问&#xff08;DMA&#xff09;模型 6. 层次结构示意图 I/O…

【代码随想录训练营】【Day 48】【动态规划-7】| 卡码 57, Leetcode 322, 279

【代码随想录训练营】【Day 48】【动态规划-7】| 卡码 57&#xff0c; Leetcode 322&#xff0c; 279 需强化知识点 python 的幂次计算&#xff0c; 10 ** 5&#xff0c; 10 **&#xff08;0.5&#xff09; 题目 卡码 57. 爬楼梯&#xff08;第八期模拟笔试&#xff09; 注…

RK3568-修改fiq-debugger调试串口

瑞芯微SDK默认将uart2_m0作为调试串口,以下方法将调试串口修改为uart5_m1。修改bootloader 修改/OK3568-linux-source/rkbin/tools/ddrbin_param.txt文件,5表示串口5。1表示复用m1。执行./ddrbin_tool ddrbin_param.txt ../bin/rk35/rk3568_ddr_1560MHz_v1.11.bin命令修改ub…

Permissions 0644 for ‘/home/jsy/.ssh/id_rsa‘ are too open

1、问题 执行git pull --rebase 报错 WARNING: UNPROTECTED PRIVATE KEY FILE! Permissions 0644 for /home/jsy/.ssh/id_rsa are too open. It is required that your private key files are NOT accessible by others. This private key will be ignored. Load key…

el-table 实现表头置顶【干货满满】附源码

a)一般情况下&#xff0c;想要在 ElTable 上实现表头固定&#xff0c;滑动滚动条时希望表头常显&#xff0c;不被滚动条顶上去。这时候就需要给表格添加高度&#xff0c;但是这个高度需要提前确定好&#xff0c;不是很方便&#xff0c;表格上边一段距离不是固定的&#xff0c;常…

字节面试:CPU100% 如何处理?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的线上问题的场景题&#xff1a; 1.CPU100%&#xff0c;你是怎么处理的&…

操作系统复习-存储管理之虚拟内存

虚拟内存概述 有些进程实际需要的内存很大&#xff0c;超过物理内存的容量。多道程序设计&#xff0c;使得每个进程可用物理内存更加稀缺。不可能无限增加物理内存&#xff0c;物理内存总有不够的时候。虚拟内存是操作系统内存管理的关键技术。使得多道程序运行和大程序运行称…

算法金 | A - Z,115 个数据科学 机器学习 江湖黑话(全面)

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 机器学习本质上和数据科学一样都是依赖概率统计&#xff0c;今天整整那些听起来让人头大的机器学习江湖黑话 A - C A/B Testing (A/B …

windows域控共享网络驱动器

背景 假设在一家公司&#xff0c;有新入职的员工。我们给其创建了域账号&#xff0c;有一些共享的文件需要其可以直接访问到。我们可以采用共享目录的形式&#xff0c;但是每次都要输入共享端的ip或者主机名&#xff0c;比较麻烦。我们希望创建的域账号访问共享文件更便捷一些…

数据库原理(概论)——(1)

数据库概述 一、数据库的四个基本概念 1.数据 描述事物的符号记录 2.数据库 数据库是长期存储在计算机内的、有组织的、可共享的大量数据的集合。数据库中的数据按一定的数据模型组织、描述和存储、具有较小的冗余度、较高的数据独立性和易扩展性&#xff0c;并可为各种用户共…

注解 - @RequestBody

注解简介 在今天的每日一注解中&#xff0c;我们将探讨RequestBody注解。RequestBody是Spring框架中的一个注解&#xff0c;用于将HTTP请求体中的内容绑定到控制器方法的参数上&#xff0c;通常用于处理JSON数据。 注解定义 RequestBody注解用于将HTTP请求体的内容绑定到方法…

-31-()

在终端运行时消除输入空格对程序的影响可以使用{在scanf后加“getchar()”或者在scanf&#xff08;“空格%d”,&a&#xff09;} 按位与和移位操作符只能用于整数且都要转位二进制后进行相应操作 不创建临时变量&#xff0c;实现两个数的交换&#xff1a;1——使用加减法&…

MySQL bin-log日志恢复数据

目录 一、开启二进制日志 二、检查二进制日志是否开启 三、使用二进制日志备份和恢复 使用二进制日志备份恢复前先创建备份&#xff1a; 应用二进制日志&#xff1a; 扩展用法&#xff1a; 四、常见命令和操作 五. 使用 mysqlbinlog 工具查看二进制日志 1. 查看二进制…

数据结构笔记 线性表的查找 顺序,折半,分块查找

顺序查找&#xff1a;从头找到尾&#xff0c;或者从尾找到头 顺序查找的性能&#xff1a; 其中&#xff0c;辅助空间的O&#xff08;1&#xff09;用于存放哨兵的 折半查找&#xff1a;向下取整&#xff1a;指当计算的结果不为整数时取小于计算结果的整数。 折半查找的性能&am…

Magnet pro for mac v2.14.0中文激活版:高效窗口管理工具

Magnet for Mac是一款专为Mac用户设计的窗口管理工具&#xff0c;旨在帮助用户更高效地管理和布局多个应用程序窗口&#xff0c;提升工作效率。 Magnet pro for mac v2.14.0中文激活版下载 这款软件拥有直观易用的界面和丰富的功能&#xff0c;支持用户将屏幕分割成多个区域&a…

textattack报错:不能导入自定义search_methods (cannot import name ‘xxx‘ from ‘xxx‘)

1. 报错信息 ImportError: cannot import name AAA from textattack.search_methods (/home/666/anaconda3/envs/textattack37_env/lib/python3.7/site-packages/textattack/search_methods/__init__.py)2. 出错简述 贴一段test1.py的模块导入 #建议使用&#xff01; import…