稀疏矩阵乘法硬件加速:基于行积算法与操作计数负载均衡的设计与实现
1. 项目概述:为稀疏计算“瘦身”与“提速”
在深度神经网络推理、推荐系统、科学计算和图分析这些现代计算的核心场景里,矩阵乘法(Matrix Multiplication, MM)是当之无愧的“算力吞噬者”。一个有趣且普遍的现象是,这些工作负载中的数据往往极其“稀疏”——矩阵中超过90%,甚至99%的元素都是零。想象一下,你面前有一张巨大的网格,上面星星点点地分布着几个有意义的数字,其余全是空白。传统的计算硬件,比如我们熟知的CPU、GPU,甚至是专为矩阵计算优化的脉动阵列(如Google的TPU),在处理这种稀疏矩阵乘法(Sparse MM, SpMM)时,就像让一台重型卡车去运送几件小包裹,绝大部分动力都浪费在了搬运“空气”(零值)上。它们无法智能地跳过这些无效计算,导致性能低下,能耗却居高不下。
因此,设计一个能“看见”并“忽略”零值的专用SpMM硬件加速器,就成了提升整个系统效能的关键。这不仅仅是做一个更快的计算单元,更是要设计一套全新的计算哲学:只对有意义的数据(非零元素)做功。本次分享的项目,正是基于这一理念,提出并实现了一个基于行积(Row-Wise Product)算法的稀疏矩阵乘法硬件加速器,并配套了一个精巧的基于操作计数的负载均衡优化方案。我们的核心武器是业界通用的压缩稀疏行(CSR)格式,它就像为稀疏矩阵量身定做的“压缩包”,只存储非零元素及其位置信息,极大地节省了存储和带宽。
简单来说,这个项目解决了两个核心痛点:第一,如何高效地执行稀疏计算?我们选择了行积算法,它相比内积和外积,在稀疏场景下避免了复杂的索引匹配,减少了对大容量片上存储的依赖,数据访问模式也更友好。第二,如何让多个计算核心(PE)高效协同工作?我们提出了一种智能的矩阵分块策略,不是简单地把矩阵切成大小相等的块,而是根据每个块实际需要进行的乘法累加(MAC)操作数量来划分,确保每个PE的活儿差不多一样多,从而避免“忙的忙死,闲的闲死”的局面。
经过实测,在我们设计的32个处理单元(PE)的加速器上,相比传统的128x128和256x256规模脉动阵列,平均取得了13.6倍到47.9倍的性能提升。而我们的负载均衡方案,也比简单的固定分块或仅按非零元素数量分块,带来了最高8.5%和6.3%的额外性能收益,且预处理开销几乎可以忽略不计(平均仅占加速器计算时间的0.06%)。接下来,我将深入拆解这个加速器的设计思路、硬件架构、负载均衡算法的细节,并分享在实现过程中踩过的坑和总结出的实战经验。
2. 核心思路与方案选型:为什么是“行积”+“CSR”?
在设计稀疏矩阵乘法加速器时,摆在我们面前的有几条主流技术路径:内积(Inner-Product)、外积(Outer-Product)和行积(Row-Wise Product)。每种方法都有其适用的场景和固有的优缺点。我们的选择并非凭空而来,而是基于稀疏计算的特性和硬件实现的成本效益深思熟虑的结果。
2.1 传统方法的困境:内积与外积在稀疏场景的短板
内积法是最直观的矩阵乘法理解方式:结果矩阵C的每个元素C[i][j],是矩阵A的第i行与矩阵B的第j列的点积。但在稀疏世界里,问题来了:A的第i行和B的第j列可能都只有零星几个非零元素。为了计算它们的点积,硬件必须进行复杂的“索引匹配”(Index Matching),即快速找出A行和B列中那些列索引与行索引相等的非零元素对。这个过程需要大量的比较和查找操作,在硬件上实现既复杂又耗时,特别是当矩阵非常稀疏时,为了一次有效的乘法,可能要做很多次无效的索引比对。
外积法则是将计算视为一系列秩-1矩阵的叠加:矩阵A的每一列与矩阵B的对应行做外积,得到一个个部分结果矩阵,最后将它们累加起来。这种方法避免了内积的索引匹配问题,因为A的列和B的行直接相乘,所有配对都会产生结果。但它的代价是会产生大量的中间部分结果。这些部分结果矩阵本身也可能是稀疏的,但为了累加,它们必须被暂存在片上或片外存储器中。对于大规模矩阵,这部分存储开销会成为巨大的瓶颈,严重制约了硬件的可扩展性和能效。
2.2 行积法的优势:为稀疏计算量身定制
行积法采取了不同的视角。计算A x B = C时,对于矩阵A中的每一个非零元素A[m][n](位于第m行,第n列),我们将其视为一个标量。然后,取出矩阵B的第n行(一个行向量),用标量A[m][n]去乘这个行向量。这样,一次“标量-向量”乘法会产生结果矩阵C中第m行的一部分结果。遍历A中所有非零元素,并对同一位置C[i][j]的结果进行累加,就得到了最终的C。
这种方法在稀疏计算中展现出三大显著优势:
- 无需索引匹配:计算直接由A元素的列索引n驱动,去取B的第n行。这是一个简单的、确定性的寻址操作,没有内积法中的复杂匹配逻辑。
- 中间结果存储压力小:部分结果直接累加到最终结果矩阵C的对应行中,不需要像外积法那样生成并存储大量的中间矩阵。片上只需要维护正在计算和累加的C行数据,存储需求大幅降低。
- 数据局部性友好:计算过程按A的行顺序进行,对A的访问是顺序的(CSR格式存储)。同时,对B的访问也是以行为单位。这种访问模式与CSR格式的存储方式(按行压缩)天然契合,有利于充分利用缓存和内存带宽,减少随机访问。
注意:行积法的一个潜在缺点是,对于A中同一行的不同非零元素,它们会多次读写C的同一行进行累加,可能带来一定的读写冲突或同步开销。但在我们的架构中,通过合理的PE任务划分和片上缓冲设计,可以有效管理这一问题。
2.3 数据格式的选择:为什么是CSR?
确定了行积算法,接下来需要选择稀疏矩阵的存储格式。常见的格式有坐标格式(COO)、压缩稀疏行(CSR)、压缩稀疏列(CSC)等。我们选择CSR,主要基于以下几点考量:
- 广泛支持:CSR是科学计算和众多软件库(如SciPy、CuSPARSE)中的标准稀疏格式之一,生态成熟,便于与现有软件栈集成。
- 行访问高效:CSR格式由三个数组构成:
values存储非零值,column_indices存储每个非零值所在的列号,row_ptr存储每行第一个非零值在values数组中的起始位置。这种结构使得按行遍历非零元素变得极其高效(通过row_ptr快速定位),完美匹配行积算法。 - 存储紧凑:相比COO格式需要存储每个非零元的行号和列号,CSR通过
row_ptr压缩了行索引信息,对于行数很多的矩阵,节省了可观的存储空间。相比位图(Bitmap)格式(需要为每个元素分配1bit标记是否为零),在极高稀疏度下,CSR的压缩率更高。
在我们的设计中,输入矩阵A和B都以CSR格式存储,输出矩阵C也动态生成CSR格式。这形成了一个从输入到输出的全CSR流水线,最大化地减少了数据转换的开销和存储压力。
3. 硬件加速器架构深度解析
有了行积算法和CSR格式的理论基础,我们来看看如何将它们落地为一个高效的硬件电路。我们的核心是一个可扩展的多PE(Processing Element)架构,每个PE都围绕行积-CSR计算模型精心设计。
3.1 单PE核心计算单元设计
单个PE是执行稀疏行积计算的基本单元。它的设计目标是高效地实现图5所示的算法。图6展示了其硬件架构框图,我们可以将其分解为几个关键模块来理解:
数据供给与索引管理:
- PE内部设有多个缓冲区,用于存放输入矩阵A、B的CSR数据(
NVA,CIA,RPA和NVB,CIB,RPB)以及正在构建的输出矩阵C的CSR数据(NVC,CIC,RPC)。 - 两个关键的索引寄存器
idxA和idxB是计算的“指挥棒”。idxA指向当前正在处理的矩阵A的非零元素在NVA/CIA数组中的位置。idxB的初始值由CIA[idxA]决定,它指向矩阵B中对应行(行号=CIA[idxA])的第一个非零元素在NVB/CIB中的位置。 - 乘法器从
NVA[idxA]和NVB[idxB]读取操作数进行乘法运算。
- PE内部设有多个缓冲区,用于存放输入矩阵A、B的CSR数据(
结果写入与CSR动态构建: 这是设计中最精妙也最具挑战性的部分。由于输出矩阵C也是CSR格式,且非零元素在每行内必须按列索引升序排列,我们不能简单地将部分结果写到任意位置。PE需要动态地在
NVC和CIC数组中为新的结果元素“插入”一个条目,或找到已存在的条目进行累加。- 写入控制单元(Writing Control Unit):这是该模块的大脑。它持续比较
CIB[idxB](当前B元素的目标列号)和CIC[idx_search](当前在C的CSR数组中搜索位置的列号)。 - 四状态机与处理:基于比较结果,写入控制单元触发图4所示的四种情况之一:
- 情况a (CIC < CIB):当前搜索位置的列号小于目标列号,说明目标位置还在后面。只需递增搜索索引
idx_search,继续向后查找。 - 情况b (CIC > CIB):当前搜索位置的列号大于目标列号,说明找到了应插入的位置。此时需要触发右移单元,将
NVC和CIC数组中从idx_search到idx_empty-1的所有元素向右移动一位,腾出一个空位,然后写入新结果。 - 情况c (CIC == CIB):找到了匹配的列索引,直接将乘积累加到
NVC[idx_search]上即可。 - 情况d (idx_search == idx_empty):搜索指针已到达数组末尾的空位,无需移动,直接在此空位写入新结果。
- 情况a (CIC < CIB):当前搜索位置的列号小于目标列号,说明目标位置还在后面。只需递增搜索索引
- 这个动态插入/累加机制保证了输出CSR数组始终有序,是行积算法能高效生成CSR格式输出的关键。
- 写入控制单元(Writing Control Unit):这是该模块的大脑。它持续比较
累加与更新: 当写入控制单元确定好写入位置(对应情况b, c, d)后,乘法器的结果会被送入累加器,与
NVC中对应位置的值(如果是情况c)或初始值0(情况b, d)相加,然后写回。同时,CIC对应位置被更新为CIB[idxB]。完成一次标量-标量乘加后,idxB递增以处理B当前行的下一个非零元素。当B的当前行处理完毕,idxA递增,处理A的下一个非零元素。
3.2 多PE扩展与共享缓冲
单个PE的性能有限。为了提升吞吐量,我们可以实例化多个相同的PE。在多PE设计中:
- 共享片上缓冲:输入矩阵A、B的CSR数据通常存储在较大的共享片上缓冲区(SRAM)中,所有PE通过一个互联网络(如交叉开关或总线)访问这些数据。这避免了数据在多个PE间的重复存储。
- 私有计算资源:每个PE拥有自己独立的乘法器、累加器、索引寄存器组和写入控制单元。这样,多个PE可以并行处理矩阵的不同部分。
- 关键挑战——写冲突:由于所有PE共同构建同一个输出矩阵C的CSR数组,如果多个PE试图同时写入或修改C的同一行,就会发生冲突。我们的解决方案是通过矩阵分块和任务调度,确保在任何一个调度周期内,不同的PE负责计算C中完全不同的行区域,从而从根本上避免写冲突。这正是下一节负载均衡要解决的核心问题之一。
4. 负载均衡优化:从“均分数据”到“均分工作”
使用多个PE并行计算,最理想的状态是大家同时干完活。但如果任务分配不均,整体完成时间就会由最慢的那个PE决定。传统的矩阵并行计算,往往简单地将矩阵按行或按列等分成大小相同的块(固定分块,Fixed Tiling)。但在稀疏矩阵乘法中,这行不通!因为一个块里非零元素多,计算量就大;非零元素少,计算量就小。更复杂的是,计算量并不单纯取决于非零元素的数量。
4.1 问题本质:为什么非零元素数量不等于计算量?
考虑一个简单的例子:矩阵A的一个非零元素A[i][j],需要与矩阵B的第j行所有非零元素相乘。如果B的第j行有10个非零元,那么A[i][j]就会引发10次乘加运算(MAC)。如果B的第j行只有1个非零元,则只引发1次运算。因此,分配给一个PE的计算负载,应该是它负责的A的子块中,每个非零元素A[m][n]所对应的B的第n行中非零元素数量的总和。我们称之为操作计数(Operation Count)。
基于非零元素数量(Element Count)的分块,只考虑了A子块的“重量”,却忽略了与之配对的B子块的“密度”,必然导致负载不均。我们的目标,是基于精确的操作计数来进行分块。
4.2 基于操作计数的矩阵分块算法
我们的负载均衡方案分为两步:水平划分和垂直划分,最终将矩阵A切分成一个网格状的瓦片(Tile),并为每个瓦片配对B矩阵的一个行块。
水平划分(Horizontal Division of Matrix A):
- 目标:将矩阵A的行划分成若干组,使得每组(一个水平条带)包含的非零元素总数尽可能相等。
- 方法:直接利用CSR格式中的
RPA数组。RPA[i+1] - RPA[i]就是第i行的非零元数量。我们按行顺序累加这个数量,当累计值达到“总非零元数/PE数”的阈值时,就切一刀。这样确保了每个水平条带承载的“数据量”大致均衡。
垂直划分(Vertical Division of Matrix A):
- 目标:在水平划分的基础上,对每个水平条带再进行垂直划分,使得最终每个小瓦片(Tile)的操作计数尽可能相等。
- 方法:这是算法的核心。我们需要计算每个垂直划分候选点的操作计数。
- 操作计数预计算:在主机CPU上,我们预先对矩阵A进行采样(例如采样10%的行),快速估算出
opstotal。对于采样到的A的每一行中的每个非零元素A[i][j],我们查看RPB数组,得到RPB[j+1] - RPB[j],即B矩阵第j行的非零元数量。这个数量就是A[i][j]将引发的MAC操作数。对所有采样元素求和并放大,即可估算出总操作数opstotal。 - 划分决策:目标是让每个PE分配到的操作数接近
⌈opstotal / N_PE⌉。我们在垂直方向上移动划分线,计算划分线左侧所有A瓦片对应的操作计数之和,当该和值最接近目标值时,确定划分点。由于A是CSR格式,垂直划分实际上是在CIA数组上确定一个列索引范围,使得落入该范围的A的非零元素对应的操作计数之和满足要求。
- 操作计数预计算:在主机CPU上,我们预先对矩阵A进行采样(例如采样10%的行),快速估算出
矩阵B的划分:
- 矩阵B的划分是跟随矩阵A的垂直划分而定的。A的每个垂直条带对应一个列索引范围
[col_start, col_end)。那么,矩阵B中行号落在这个范围内的所有行,就被划归为同一个B的行块(Tile Row)。这样保证了计算的一致性:A瓦片(i,j)只与B的行块j相乘。
- 矩阵B的划分是跟随矩阵A的垂直划分而定的。A的每个垂直条带对应一个列索引范围
4.3 瓦片配对与调度策略
划分完成后,我们得到了一个N x N的A瓦片网格(假设有N个PE),以及N个B的行块。如何将它们分配给N个PE,并安排执行顺序呢?
我们采用了一种对角线调度策略,其伪代码如图8所示。对于第k个调度轮次(sched_round),我们将瓦片对(tile_A(i, (i+k-1) % N), tile_B((i+k-1) % N))分配给第i个PE(i从0到N-1)。
这种策略的精妙之处在于:
- 避免资源争用:在同一个调度轮次内,任何两个PE所访问的A瓦片(来自A的不同行区域)和B的行块(B的不同行集合)都是互不重叠的。这意味着它们不会同时读写输出矩阵C的同一行,彻底消除了PE间的写冲突。同时,它们访问的输入数据区域也不同,减少了对共享输入缓冲区的访问竞争。
- 负载均衡:由于分块是基于操作计数的,每个瓦片对的计算量理论上相近。再经过这种轮转调度,每个PE在各轮次中承担的工作量也趋于平均。
图9用一个2PE的简单例子清晰展示了整个过程:先水平划分A使非零元数相等,再垂直划分使操作计数相等(各为4),最后通过两轮调度,让PE0和PE1分别计算(A00, B0)、(A01, B1)和(A11, B1)、(A10, B0),完美避免了冲突。
4.4 预处理开销与实战考量
你可能会问,这个分块调度算法需要在主机CPU上执行,会不会带来很大的额外开销?我们的评估结果给出了令人放心的答案:开销极低,几乎可忽略。即使在32个PE的配置下,矩阵分块和调度所带来的预处理延迟,平均也只占加速器本身计算时间的0.055%。对于4PE和16PE配置,这个开销更是低至0.00033%和0.01015%。这是因为:
- 采样而非全量计算:我们只对A矩阵采样10%的行来估算操作计数,大大减少了预计算量。
- 计算简单:分块算法主要涉及的是对CSR数组(
RPA,CIA,RPB)的扫描和简单算术运算,计算复杂度是O(nnz),对于稀疏矩阵来说非常高效。 - 一次计算,多次使用:对于固定的矩阵A和B,分块方案只需计算一次,便可以反复用于多次SpMM计算(例如在迭代算法中)。
实操心得:在实际硬件设计时,主机CPU计算出的分块信息(每个PE负责的A瓦片的行/列范围、B的行块范围、以及各PE的初始索引寄存器值)可以通过控制寄存器或DMA描述符的方式传递给加速器。加速器上电或任务开始前,由驱动加载这些配置,PE即可开始并行计算。这套方案将复杂的负载均衡逻辑放在软件端,保持了硬件架构的简洁和高效。
5. 性能评估与对比分析
理论再优美,也需要实战检验。我们构建了一个周期精确的模拟器,其行为基于实际的FPGA原型实现,并与广泛用于DNN加速的脉动阵列(128x128和256x256规模)进行了性能对比。
5.1 实验设置与基准测试
- 对比平台:
- 脉动阵列:模拟了128x128和256x256两种规模,采用权重静止(Weight Stationary)数据流,时钟频率设为1 GHz。这是对标Google TPU的典型配置。
- 我们的SpMM加速器:评估了4PE、16PE、32PE三种配置。其中,16PE和32PE的设计是与TPUv4i和TPUv1中的一个矩阵乘法单元(MXU)进行等功耗(Iso-power)对比的,尽管我们的设计使用32位浮点数而TPU使用低精度整数,这使我们的对比条件更为保守。
- 测试集:采用了来自SuiteSparse矩阵集的14个具有不同维度和稀疏度的真实矩阵作为基准(见表2)。我们根据这些矩阵的维度和非零密度,合成了方阵A和B进行乘法测试。
- 评估指标:主要对比执行完整SpMM运算的延迟(Latency)。
5.2 加速效果:碾压性的优势
表3的对比结果令人振奋。我们的32PE-SpMM加速器,相比128x128和256x256的脉动阵列,平均取得了47.9倍和13.6倍的加速比。即使是16PE版本,也分别有8.8倍和2.5倍的平均加速。4PE版本在面对小矩阵时可能不占优,但在处理大规模矩阵(如wg,m2,az等)时,性能依然优于脉动阵列。
核心原因在于计算效率的本质不同:
- 脉动阵列:其固化的数据流无法跳过零值操作。无论矩阵多稀疏,它都需要遍历整个稠密矩阵的空间进行计算,计算量是
M*N*K。随着矩阵增大,延迟线性增长。 - 我们的SpMM加速器:只对非零元素执行MAC操作,计算量正比于
nnz(A) * avg_nnz_per_row(B)。在稀疏度很高(例如>95%)的场景下,有效计算量远小于稠密计算量。因此,我们的加速器性能随矩阵规模增长较慢,可扩展性远优于脉动阵列。
当然,对于非常小的稠密矩阵(如wv,p3),脉动阵列因其极高的时钟频率和规整的数据流,可能仍有优势。但这恰恰说明了专用稀疏加速器的必要性——它针对的是脉动阵列不擅长的主流稀疏负载。
5.3 负载均衡方案的有效性验证
我们进一步比较了三种分块策略的性能:
- 固定分块(FT):简单地将矩阵按行、列数均等分割。
- 基于非零元素数量的分块(ET):仅考虑使每个块的非零元素数量均衡。
- 我们的基于操作计数的分块(OT)。
表4的结果证实了我们的分析:OT方案在绝大多数情况下优于FT和ET。在32PE配置下,OT相比FT和ET平均带来了8.5%和6.3%的性能提升,在个别矩阵(如pg)上提升甚至超过25%。提升幅度随着PE数量增加而变大,因为PE越多,负载不均衡的负面影响越容易被放大。
ET方案虽然比FT好,但因为它忽略了B矩阵行稀疏度的差异,其分块仍然不是最优的。我们的OT方案通过精确预估计算负载,实现了更精细的均衡。
注意事项:分块策略的性能也受数据分布模式影响。在极少数特定分布下(如测试中的
f3矩阵,4PE配置),FT可能偶然取得更好效果。这是因为固定分块有时会巧合地匹配计算负载分布。但OT方案的优势在于其稳定性和普适性,它不依赖于数据的特定分布模式,而是通过计算来保证均衡,因此在各种真实、未知的稀疏矩阵上都能提供可靠的高性能。
6. 硬件实现细节与FPGA原型
为了让设计落地,我们在Xilinx ZCU106 FPGA开发平台上实现了4PE版本的SpMM加速器原型。
6.1 实现结果
表1总结了实现的关键数据:
- 时钟频率:综合后达到214.27 MHz。对于包含复杂控制逻辑(如动态CSR构建)的FPGA设计来说,这是一个合理的频率。
- 资源利用率:在ZCU106平台(包含大量DSP、BRAM、LUT资源)上,我们的设计占用了中等水平的资源。这证明了设计的可实现性。
- 功耗:实测功耗约为6.18W。
需要强调的是,这是在FPGA上的实现。如果采用ASIC工艺进行流片,通过定制化设计、优化关键路径、使用更先进的存储单元,时钟频率可以大幅提升(达到GHz级别),同时功耗会显著降低。FPGA原型的主要价值在于验证功能正确性和架构可行性。
6.2 关键硬件设计技巧
- CSR缓冲区的设计:输入A、B的CSR缓冲区通常设计为多bank SRAM,以支持多个PE并行访问。输出C的CSR缓冲区设计更为关键,因为它需要支持随机插入(右移操作)。一种高效的设计是将其组织为多个独立的行缓冲(Row Buffer),每个PE被分配一组互不相交的行,这样就能并行写入而无冲突。行缓冲内部可以采用双端口SRAM或小型的寄存器文件,配合地址生成逻辑来管理有序插入。
- 写入控制单元的优化:图4中的四种情况判断和对应的操作(递增搜索、右移、累加、直接写入)需要在一个或几个周期内完成。可以通过并行比较器和简单的状态机来实现。右移操作可以通过一个多路选择器(MUX)链或一个小的移位寄存器来实现,对于CSR行缓冲来说,移动的数据量(一行内的非零元)通常不大,开销可控。
- 数据通路与流水线:乘法器和累加器可以流水化。关键路径往往在写入控制逻辑和CSR缓冲区的访问上。需要仔细平衡流水线级数,确保在目标频率下能稳定工作。
7. 常见问题、挑战与未来展望
在设计和实现这个加速器的过程中,我们遇到并克服了一系列挑战,也看到了未来可以继续优化的方向。
7.1 实战中遇到的典型问题与解决思路
输出CSR动态构建的硬件开销:
- 问题:在硬件中动态维护一个有序的、不断增长的CSR数组(特别是右移操作)是面积和时序上的挑战。
- 解决思路:我们采用了“行缓冲”架构。每个PE(或一组PE)负责输出矩阵C的一个连续行区间。这样,每个行缓冲是独立的,规模较小,右移操作只发生在一个缓冲区内,硬件实现简单。通过负载均衡分块,我们确保了行区间的划分,从算法上避免了跨行缓冲的写冲突。
不规则访存与带宽压力:
- 问题:稀疏计算导致对输入矩阵B的访问是间接的、不规则的(通过
CIA索引跳转),这可能造成缓存命中率低,内存带宽利用率不佳。 - 解决思路:我们在硬件中设计了较大的输入缓冲区,并采用预取(Prefetching)机制。主机CPU在传输数据时,可以根据分块信息,将每个B的行块连续地放入加速器的片上缓冲。在计算时,一个B的行块会被反复使用(与A瓦片中的多个元素相乘),这提供了良好的数据局部性,缓解了带宽压力。
- 问题:稀疏计算导致对输入矩阵B的访问是间接的、不规则的(通过
负载均衡预计算的精度与开销权衡:
- 问题:100%精确计算操作计数需要扫描整个A矩阵,开销大。
- 解决思路:采用采样法。我们发现,只需对A矩阵随机采样约10%的行,就能足够准确地估算出总操作计数和分布,从而做出高质量的分块决策。这使预处理开销降低了近一个数量级,而性能损失微乎其微。
7.2 未来可能的优化方向
- 支持更广泛的数据类型与稀疏格式:当前设计针对32位浮点数和CSR格式。未来可以扩展支持INT8、BFLOAT16等低精度格式以适配DNN推理,以及支持块稀疏(Block Sparse)、结构化稀疏等格式,进一步提升存储和计算效率。
- 更自适应的负载均衡:当前的分块是静态的、在计算前确定的。对于动态稀疏模式(如在迭代法中稀疏模式变化),可以探索轻量级的运行时负载监测与动态任务迁移机制。
- 与片上网络(NoC)集成:当PE数量扩展到成百上千时,PE间以及PE与共享缓冲间的通信将成为瓶颈。设计一个高效的、支持不规则稀疏数据访问模式的NoC是下一步的研究重点。
- 软件栈与编译器支持:要让硬件发挥最大效能,需要强大的软件支持。开发能够自动将稠密矩阵运算转换为稀疏格式、调用本加速器、并进行高效分块调度的编译器中间件和运行时库,是推向实际应用的关键。
回过头看,这个项目的核心价值在于它提供了一套从算法、架构到系统优化的完整稀疏计算解决方案。它不只是一个更快的计算单元,而是一个考虑了数据格式、并行效率、负载均衡和实际硬件约束的协同设计。在数据稀疏性日益普遍的时代,这类专为稀疏性而生的加速器设计思路,或许比单纯追求更高峰值算力的稠密加速器,更能代表未来高效计算的方向。
