news 2026/9/10 3:17:29

算子融合与计算图优化:突破AI芯片内存墙的关键技术

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算子融合与计算图优化:突破AI芯片内存墙的关键技术

开头先从一次硬件迁移的经历切入。当时我负责把一个视觉模型从GPU服务器搬到某款端侧NPU上,GPU上用TensorRT推理,算子执行时间分布相对均匀,瓶颈基本就在Conv大算子身上。换到NPU上以后,同样的网络,BatchNorm、ReLU、Add这些以前在GPU上毫不起眼的小算子在执行时间占比里高得惊人,个别elementwise算子甚至比前面的Conv还慢。排查了一圈,根本不是计算慢,是数据从DDR搬进搬出、中间结果反复读写片上缓存,把小算子彻底拖垮了。这可能是所有AI芯片开发者都会撞上的第一面墙——内存墙。而拆掉这面墙最基础、也最有效的工具,就是计算图优化里的算子融合。这篇文章就围绕“算子融合与计算图优化”展开,讲清楚融合为什么能省时间、编译器怎么判断能不能融合、融合后的kernel在AI芯片上怎么跑,以及实际调优时容易踩的坑。

1. 算得快不如搬得少:访存瓶颈是怎么拖垮小算子的

1.1 一个反直觉的实测现象

先放一组实测数据。某个端侧NPU上跑一个典型的3x3卷积,输入输出都是112x112x64,算上激活函数,单独执行耗时约0.8ms;但单独跑一个几乎零计算量的ReLU,同样是112x112x64,耗时居然能到0.5ms。什么概念呢?ReLU的逻辑就一句话:大于0的保留,小于0的置0,单纯看数学运算量,它比Conv少了三个数量级。可为什么速度只差这么一点?

因为无论Conv还是ReLU,算完以后都要把结果写回DDR,下一次算子再把它读进来。数据搬运的时间完全碾压了计算本身的时间。ReLU那个kernel里,真正做比较的指令几乎可以忽略,99%的时间都在等数据从DDR进片上、再从片上出DDR。这就是典型的访存密集算子。

1.2 算力与带宽的剪刀差

芯片设计上有个著名的“内存墙”概念:算力(TOPS)的增长速度远远快于DDR带宽(GB/s)的增长速度。比如某款NPU算力标称20TOPS,DDR带宽标称50GB/s。如果一个算子完全访存密集,它每从DDR读入1个字节的输入,只做少量计算,那么实际能达到的速度天花板由带宽决定,跟算力标称值没什么关系。

用一个简单算例说明。假设某个elementwise算子,输入输出各占4MB,一共访问8MB数据。按50GB/s带宽,纯粹搬运就需要8MB / 50GB/s = 0.16ms。而片上计算哪怕只要0.01ms,总耗时也是约0.16ms,访存占了94%。如果不融合,一个计算图上四个类似的算子串行,就是4乘以0.16ms,恰好约等于0.64ms的访存开销。如果这四个算子融合成一个kernel,中间三个张量全部留在片上寄存器或SRAM里,最终只需要从DDR读一次4MB输入、写一次4MB输出,总访存还是8MB,总耗时则是0.16ms。合并前0.64ms,合并后0.16ms,四倍差就是这么省下来的。这里提醒一下,实际数字会因为芯片架构差异而变化,但量级和比例关系是普遍成立的。

1.3 从“算子视角”切换到“数据流视角”

在GPU上开发时,很多人的思维习惯是“算子=一个kernel启动”,NVIDIA CUDA里一个kernel就是一个函数,写起来清晰,但执行时每一次kernel launch都有固定开销,GPU上这个开销相对可控,CPU上则更明显。可AI芯片上和GPU不一样,NPU/TPU这类架构对kernel launch和同步的开销更敏感,小算子一多,调度器就忙不过来,流水线经常打断。

所以做AI芯片编译优化的人经常说一句话:不要从算子的角度看计算图,要从数据流的角度看。算子融合的本质,就是把“多个算子步骤间的多次往返搬运”压缩成“一段连续的数据流水”。它不改变最终算出的数值(大多数时候不改变,后文会讲少数例外),不改变计算图的功能,只是让中间结果尽量别落DDR。这个思路在很多场景下都能延伸,比如FlashAttention,本质上也是这个逻辑的极致版,不过这个后文细说。

想验证自己手头的算子改了访存会不会有效果,有个很朴素的方法:看这个算子的算术强度(arithmetic intensity),也就是总计算量除以总访存量,单位是FLOP/Byte。算术强度大于某个阈值(取决于芯片的算力带宽比,这个值叫Roofline模型里的ridge point)才算计算密集,小于它就是访存密集。elementwise算子通常算术强度极低,0.1都不到,妥妥的访存密集。Conv一般比较高,但某些小shape的Conv也未必高。算子融合做的事情,就是把一串低算术强度的算子“打包”成一个整体来看待,整体算术强度会大幅提升。

2. 三种主流的融合方式,以及它们各自能省多少

实际工程里的算子融合不是只有一种模式。我自己习惯把融合分成三类,逐点融合、纵向融合、横向融合,分别对应不同的数据流特征。

2.1 逐点融合:把一串elementwise拼成一个循环

这是最简单、最常用、也最好写的一类。ReLU、Sigmoid、Tanh、Add、Mul、Scale、Clip,这类算子有个共同特征:输出张量中每个位置的值只依赖输入张量中相同位置的值,互相之间没有任何空间上的依赖。用算法术语说叫pointwise,用老百姓的话说就是“一个萝卜一个坑”。

这类算子融合起来最安全。可以把ReLU和Add融合成一个循环:遍历每个位置,读入两个输入值,相加,过ReLU,写出一个输出值。中间不会多算任何东西,不会改变浮点结果的精度(对绝大多数硬件来说),纯粹是省掉了中间张量的写和读。

我见过一个比较夸张的案例,某个检测模型的后处理分支里有连续七个elementwise算子,当时还没开融合优化,单帧推理时间里有2.3ms就耗在这些小算子上了。后来用编译器的pass把整个链融合成一个elementwise kernel,耗时直接降到0.3ms。七个算子合成一个循环,访存量从“读一次写一次乘以7”变成“读一次写一次乘以1”,省下的钱就是这么实在。

需要注意的是,并不是所有elementwise都能随便融合。两个前提必须确认:第一,每个位置的输出只取决于同一位置的输入,这个好查;第二,算子之间的语义可以交换或组合,这个要小心。比如先做Sigmoid再做Log,理论上可以合成一个LogSigmoid融合kernel,但不同库对LogSigmoid的实现有细微差异,数值结果可能和分步计算差一两个ulp(unit in the last place,最后一位精度单位)。大多数时候这个误差无所谓,但如果模型对数值极度敏感(比如某些强化学习场景),最好做一遍精度对齐测试。

2.2 纵向融合:把不同访存密度的算子串成一条流水线

纵向融合,也叫垂直融合,是一种比逐点融合更“值钱”的融合方式。它通常是把一个访存密集的大算子(比如Conv、MatMul)和紧随其后的一串访存密集小算子(比如BN、ReLU、Add)合并成一个整体。

为什么这个值钱?因为Conv或MatMul这种算子计算量大、访存量也大,属于计算和访存都密集的算子。它执行完以后,输出feature map动辄几十MB,全部写回DDR。后面的ReLU/BN又把这几十MB读进片上、处理完再写回去。一次读写几十MB,在一个需要跑几十帧每秒的模型里,这个开销非常可观。

纵向融合的思路是:Conv在片上算出一部分结果时,不要等整张feature map算完再输出,而是算出一小块就立刻在片上做BN、ReLU、Add,然后把最终结果写回DDR。这样中间结果只有小块数据停留在片上缓存中,不需要完整落回DDR。稍微专业一点说,这是“输出驻留”或“栅格化”的融合策略。

具体能省多少?我调过一个ResNet-50的NPU实现,BatchNorm和ReLU都紧随Conv之后。在开了纵向融合之前,BN和ReLU两个小算子加起来占了整网超过15%的时间;开了融合之后,这15%基本归零,整个网络端到端速度提升约12%。Conv花的时间没变,但后面跟的小算子“消失”了,整条流水线节奏快了很多。

实现时有个重要细节:融合后的kernel内部要控制好Conv输出分块的大小。分块太小,片上计算单元利用率低;分块太大,SRAM装不下,被迫做额外搬运,融合的优势会缩水。这个分块大小没有万能公式,通常要对着芯片的SRAM容量和DMA带宽现场调,我在后面第4章详细讲。

2.3 横向融合:多个同构小算子拼成一整个kernel

横向融合,又叫批处理融合。它的场景是计算图里有好几个结构相同、输入输出互相独立的算子。比如说两个独立的二维卷积,它们的输入分别来自图的不同分支,后面也是各算各的,互不干扰。

在不做融合的情况下,这两个卷积会被分别调度,每个都要经历一次完整的kernel launch、数据搬运、计算、写回。横向融合的做法是:把这两个卷积的batch维度拼在一起,或者把它们的数据铺成同一个大矩阵,当作一个大算子一次算完。这样数据搬运的“固定开销”被摊薄了,片上缓存也可以被更充分地利用。

这种融合在AI芯片上特别有用,因为很多NPU对固定形状的大矩阵计算效率极高,但对很小的算子反而无能为力。比如有两个12x12的小卷积,单独调kernel,可能调度开销占了80%;拼成一个大的batch卷积后,调度的比例迅速下降,芯片算力利用率提升非常明显。不过也要注意,横向融合只适用于完全独立的算子,如果有任何依赖关系、共享输入、或者前后顺序上的约束,就得先通过其他手段把依赖解开。

2.4 Transformer时代为什么更依赖融合

过去CNN时代,融合的重点主要是Conv+BN+ReLU这套常规组合。Transformer火了之后,融合的一个“爆款”案例就是FlashAttention。

Attention的计算图里有几个很有意思的算子:QK^T、Scale、Softmax、Dropout、PV。其中Softmax这一步涉及到跨行的归约(row-wise reduction),它天然需要读取一整行数据;而QK^T又是一个矩阵乘,产生完整的注意力分数矩阵。如果不融合,注意力分数矩阵要完整写回DDR,那可是个NxN的大张量,序列长度一长,内存开销和访存开销都爆炸。

FlashAttention的融合策略是:把QK^T、Scale、Softmax、PV整个融合成一个kernel,内部按块处理,一块一块地在SRAM中完成,永远不把完整的attention matrix落回DDR。这本质上就是一个超大规模的纵向融合加横向融合组合拳。从算子融合的角度看,它并不违反任何设计原则,只是融合粒度更大、复杂度更高。

这给我的一个很深体会是:图优化的兵家必争之地不在单个算子本身,而在于算子和算子之间那些“看不见的数据搬运”。Transformer把这种搬运放大到了一个夸张的程度,谁能在融合上做到极致,谁就能在推理芯片上占据明显的性能优势。

3. 编译器怎么判断“能不能融合”:计算图分析的底层逻辑

很多时候我们并不手写融合kernel,而是依赖编译器(比如MLIR、XLA、TVM)上面现成的optimalization pass。但自动融合不是魔法,它背后有一整套规则和分析。

3.1 计算图的表示:从DAG说起

计算图在编译器内部是DAG,有向无环图。每个节点是一个算子,每条边是一个张量(Tensor)。Tensor的“生命长度”本质上就是图中从某个算子输出到最后一个消费它的算子输入之间的跨度。这个跨度越长,中间结果在DDR里待的时间就越久,访存开销就越大。

算子融合要做的就是:分析这些节点和边,把一段路径上的节点打包成一个新的超大节点,让中间张量不用出片上缓存。为了实现这个打包,编译器需要回答三个问题:能不能合(合法性)、合了划不划算(代价)、用什么方式合(策略)。这三个问题分别靠索引分析、代价模型、调度策略来解决。

3.2 融合合法性:不是相邻就能合

很多第一次接触图优化的人会误以为“两个算子挨着就能融合”,这是最大的误区。融合合法性的判断至少要过四道关。

第一道关是“消费者数量”。一个节点的输出如果被多个后续节点使用,那这个输出必须被完整保存。比如某个算子的输出同时喂给两个分支,融合时你不能把中间结果“顺手扔了”,否则另一个分支就没了数据。这道关可以通过“拷贝”或者“不融合该节点”来解决,编译器通常选择后者。第二道关是“语义正确性”。浮点运算顺序改变、归约顺序改变、广播维度处理不当,都可能造成数值变化。编译器要确认融合后指令序列和融合前的语义等价。第三道关是“内存生命周期”。中间张量如果被融合kernel内部的SRAM承载,但它在后续图中还要被使用,就不能简单留在片上,要确保它能被正确地写给DDR。第四道关是“依赖约束”。如果两个算子之间存在数据依赖,而融合后调度顺序被强制改变(比如并行执行),可能导致结果错误。

实际阅读编译器日志时,经常看到某个融合被判定“not fusible”,详查下来大多是违反了消费者数量或生命周期这两条。自己在写自定义融合pass时,也建议优先检查这两个条件,能省下后面一大半调试时间。

3.3 代价模型:融合并不总是划算的

说到这,得澄清一个观念:融合不是免费的午餐,更不是所有融合都该做。我在第2章里列举了很多收益,但有些情况下融合反而更差。这就需要一个代价模型来算账。

代价模型的输入是芯片的硬件参数:算力、DDR带宽、SRAM容量、寄存器数量、DMA传输速率。输出是一个预测的执行时间。编译器对候选的融合方案分别估算耗时,选最优的。估算的核心是:融合后节省了多少访存,代价是多算了多少计算或占用了多少片内资源。

一个典型反例:把两个elementwise算子融合后,中间结果没法放进寄存器,只能放进SRAM。如果这个SRAM容量很小,DMA做搬运的时间比直接写DDR再读回来的时间还长,那融合就是负收益。另一个反例是:融合导致循环嵌套结构变复杂,最内层循环的计算密度下降,可能让原本可向量化的部分效率降低。我遇到过一次融合后性能下降5%的情况,就是因为多算子融合导致寄存器溢出,中间结果被迫挤到本地内存,访问延迟反而更高了。

所以好的编译器在优化时都做了“收益预判”而不是“无脑融合”。看某条优化日志写着“allow_fusion=true”和“allow_fusion=false”的差别,本质上就是在权衡这个代价模型。

3.4 BN folding的工程细节:比想象中多一层判断

BatchNorm融合是个特例,值得单独拿出来讲。BN在推理阶段的计算是:y = (x - mean) / sqrt(var + eps) * gamma + beta。如果把BN往它前面的Conv里塞,经过一番代数变换,可以得到新的权重和bias:w’= w * gamma / sqrt(var + eps),b’= (b - mean) * gamma / sqrt(var + eps) + beta。这样Conv后面的BN就完全不用算了。这个变换在推理部署里几乎是标配。

但实际工程有个容易被忽略的判断:如果BN前面的算子不是Conv(比如是残差分支里直接接BN,或者前面是个Add),BN folding就不能直接往“上游算子”里塞。这种情况编译器往往会退化为另一条路径:把BN本身写成一组elementwise操作然后和它的上/下游算子做常规融合。另外还有精度问题,把gamma和beta折叠进权重以后,如果权重数值范围变化大,在某些量化精度下可能会有可观测的精度差异,这一点在INT8部署时要特别小心。我当时做INT8量化模型时,BN folding后某些层的输出分布发生了偏移,后来排查发现是折叠后的权重和原始权重的数值范围差异放大引起的,最后只能对个别层关闭这个优化。

4. 融合后的Kernel如何在AI芯片上落地

图分析完毕,融合kernel生成后,就到了芯片适配这一步。这里讲三个关键问题:片内数据管理、多核调度、以及和工具链的配合。

4.1 片上SRAM是融合kernel的主场

AI芯片跟CPU架构最大的不同在于,NPU通常有一块很大的片上SRAM,可以被软件直接当作“暂存区”。这块SRAM速度比DDR快几个量级,但容量有限。融合kernel的编程模型,本质上就是“地图炮”式的数据流组织:用DMA从DDR把输入数据块拉进SRAM,在SRAM里完成所有融合算子的计算,最后把输出块从SRAM搬到DDR。中间张量在这个过程中只存在于SRAM,不需要回DDR。

这就带来一个重要的工程指标:融合kernel的“数据块”到底切多大。切小了,SRAM利用率低,DMA搬运次数增多;切大了,SRAM装不下,编译器会把部分中间数据暂时存到DDR,融合效果就退化了。实际开发中,我通常按这个流程选:先枚举几个候选块大小(比如64x64、128x128、64x128),然后看每个候选下的SRAM占用、DMA传输次数、计算密度,挑一个综合耗时最小的。这个参数在上板测试前很难凭感觉定,靠谱的做法是写一个简短的自动化调参脚本。

4.2 多核并行:融合Kernel的依赖与调度

AI芯片很少是单核跑,多核并行时融合kernel的设计会影响整个系统的吞吐。假设一个融合kernel内部有五个子算子,有的子算子可以并行(比如两个elementwise分支没有依赖关系),有的子算子只能串行(比如BN依赖Conv的输出)。编译器生成机器码时,要把可并行的部分分配到不同核上,并在核间插入同步点。

这里我踩过最大的一个坑是“伪并行”。表面上多个核都在工作,但实际上大部分时间在等一个共享的SRAM缓冲区的读写权限。因为多个核共享同一块SRAM,读写冲突会导致等待,虽然单个核看似利用率高,但端到端吞吐反而下降。解决办法是给不同核分配相互独立的SRAM分区,减少竞争。这个调优通常需要和芯片的存储架构对齐,各家芯片手册里差异很大。

4.3 工具链中的位置:MLIR和自定义Pass

今天主流的AI编译器,无论是芯片厂商自研的SDK还是开源的TVM、XLA,都会提供计算图优化层面的一套工具链。MLIR(Multi-Level Intermediate Representation)现在是绕不开的底座,因为它提供了多层IR抽象,可以在不同抽象级别上做融合优化。比如在“算子级IR”做语义级别的融合判断,在“循环级IR”做执行细节的融合调优。

如果你要为一个新芯片写自己的融合优化,我建议的顺序是:先在IR层面完成合法性分析(第3章的内容),这是芯片无关的;然后到调度层做分块、多核分配、DMA规划,这部分跟芯片强相关;最后做指令生成,这是纯硬件相关的。分层清晰的最大好处是复用性高,换个芯片只需要重写后面两层。这个思路基本上就是MLIR的设计哲学,强烈建议新入行的人顺着这条路径学习,而不是一上来就手撸汇编级kernel。

5. 融合之外的图优化:常量折叠、死代码消除与布局转换

算子融合是计算图优化里最重要的一块,但不是全部。一次完整的图优化pipeline通常还会包含其他几个pass,这里挑常量和布局这两类重点说,因为它们和融合经常协同工作。

5.1 常量折叠:把不依赖输入的算子提前算掉

常量折叠(constant folding)做的事情很简单:如果一个算子的所有输入都是编译期常量,那就不用在运行时算了,直接在编译期把结果算出来,替换这个节点。典型例子是Conv的权重预处理:很多芯片的NPU对权重的排布有特殊要求(比如NCHW转成NHWC或更奇怪的自定义layout),如果不在编译期做,运行时就要重复做很多次。

有些朋友觉得常量折叠很简单,其实它有几个坑。第一个坑是常量太大:把几个百MB的常量在编译期做折叠,会让编译时间爆炸,产出的模型文件也会暴涨。实际工程中通常会对常量大小设上限,超过阈值的就不折叠,留到运行时由加载器处理。第二个坑是常量和变量的混合:一个算子的输入是常量加变量,不能折叠,但可以把常量部分先算掉一部分,这个叫部分常量折叠,很多编译器也支持。第三个坑是常量与融合的配合:折叠完了以后,算子可能变成“只有一个输入是变量”的形态,这反而更适合和上游算子做融合,两个pass的顺序有时候会互相影响。

5.2 死代码消除:把没人用的计算扔掉

死代码消除(DCE)在计算图优化里的思路跟传统编译器一致:如果一个算子的输出没有任何消费者,那这个算子就是死代码,可以删除。计算图里出现死代码的原因很多,常见的是:做图变换时某个分支被挪走了,原节点没清理;或者常量折叠把一个分支变成Dead,后续自动化清理没跟上;或者模型本身存在调试用的输出节点,导出部署模型时没有去掉。

我处理过一个实际问题:某个从PyTorch导出的模型里,居然有十几个输出节点是训练时记录中间特征的,部署时根本用不着,但导出的图里全都保留着。结果这些算子虽然不影响最终结果,却占了大量访存和计算时间。把DCE跑一遍后,模型体积小了20%,单帧推理时间少了8%。这个优化做完之后再紧接着做融合,如图会清爽很多,融合判定也会更快更准。

5.3 布局转换:为硬件排布做的隐式优化

布局转换(layout conversion)指的是把张量在不同内存排布下互相转换,比如NCHW转NHWC、NC1HWC0这类。AI芯片的向量单元对内存连续的数据访问效率高得惊人,对跨步访问则很慢,所以数据布局往往直接影响算子的执行速度。

布局优化虽然不属于融合,但和融合有很强的耦合关系。如果把一个Conv网络从NCHW布局改成NHWC,Conv本身可能变快,但紧接着的BN和ReLU如果也按NHWC算,可能会和别的分支的NCHW数据产生冲突。这时就要把布局转换“嵌入”到融合的kernel里,或者干脆把布局转换算作一个特殊的elementwise算子参与融合判定。很多芯片之所以在端到端优化后能跑得更快,不完全是某个算子被加速了,而是整套layout和fusion是联合调优出来的。

这个主题给到我的一个经验是:在调试AI芯片性能时,不要只看某个pass单独的效果,要整体看待整条pipeline中常量折叠、DCE、layout转换、算子替换、算子融合它们之间的先后顺序和相互作用。调一个跟调四个常常效果差很多。

6. 实测中的坑:什么时候融合反而更慢

讲完这么多正面收益,最后说点实际的。算子融合不是万灵药,用不好也会翻车。下面几个坑都是我或身边的同事在真实项目里踩过的,列出来给后来人省点时间。

6.1 寄存器溢出与“假融合”

那次我们调试一个融合后的kernel,端到端性能反而比融合前差了5%。打开分析报告一看,融合后寄存器分配爆了。因为融合进来的elementwise算子太多,每个算子都有几个中间值需要暂存。寄存器数量有限,放不下的就“spill”到本地存储(stack),而本地存储的访问延迟比寄存器高一个量级,这额外开销直接抵消了融合在访存上的收益。这种事儿在GPU上一样会发生,在NPU上因为寄存器数量往往更紧张,更像一个普遍问题。

解决方法有这么几条:一是减少融合的算子数量,把一个大融合拆成两个中等融合,不要让一个kernel塞下过多子算子;二是调整融合后的循环结构,把大循环拆成若干小循环,让中间变量尽可能复用同一个临时寄存器;三是在代价模型里加入“寄存器溢出惩罚项”,在融合决策时就规避掉这种风险。

6.2 BN folding后的数值精度异常

前面提过BN folding的精度问题,这里补充一个量化场景的具体案例。有个INT8量化模型,融合BN以后在accuracy评测集上跌了0.8个点。排查发现,某几层折叠后的权重范围明显大于融合前,而INT8量化是事先按原权重范围做的scale校准,折叠后权重超出了scale能表达的区间,导致量化误差被放大。

解决思路是:BN folding要在量化校准之前做完,让校准过程面对的是折叠后的权重,而不是折叠前的。如果模型已经导出来、scale值已经算好了,那就只能针对这几层单独关闭折叠,或者重新跑一遍校准。工程上常说的“Q/DQ(量化/反量化)处理和BN折叠的pass顺序”,说的就是这个问题。

6.3 怎么定位“该融合但没融合”的算子

最让人头疼的情况不是融合出错,而是编译器压根没去融合某些本该融合的算子,端到端性能明显异常。这种问题定位手段很重要。我自己常用的排查流程是:先把计算图在融合优化前后的IR打印出来,diff一下哪些节点没被合并;然后看没合并的节点满足什么特征,重点检查消费者数量、依赖关系、shape是否动态、是否因为常量折叠后形态发生变化;逐个排除后,再看是不是代价模型判定“不划算”——这类会被优化日志里标注low benefit,从日志里能读到。

有一次我们找了一个下午的“失踪融合”,最后发现原因只是输入shape是动态的,编译器没法在静态编译期确定分块大小,只能保守地选择不融合。解决方法是把动态shape固定成静态shape(比如把batch固定为1),或者开启编译器特定的动态shape融合开关。这类小问题看着不起眼,实际项目里老花时间。

6.4 一点小建议:融合粒度要跟着场景走

做了这么多年AI芯片优化,我最大的体会是:融合的粒度没有标准答案。同一个模型,在服务端GPU上可能融合起来收益大,在端侧NPU上却因为SRAM太小而被迫拆细;在延迟敏感场景要融合到极致,在高吞吐场景可能要保留部分并行度。这些都是围绕“少搬运、多复用”原则的人工权衡。希望大家做优化时,既要把算子融合当作打磨性能的利刃,也要能退一步看看整体数据流,在融合和并行、精度和速度之间找到适合自己场景的平衡点。这样踩过的坑才不会白踩,调出来的性能才能经得起生产环境的考验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 3:16:50

基于Simulink的PEM燃料电池控制仿真:PID与滑模对比

简介:面向PEM燃料电池控制研究群体的Simulink仿真资源,完整搭建了燃料电池系统模型,并提供PID、积分分离、滑膜控制器三种控制方案,可在同一仿真框架下横向对比策略差异,适合开展控制算法验证与教学实验。压缩包收录18…

作者头像 李华
网站建设 2026/9/10 3:15:31

MCP与A2A协议:企业级多智能体协同的操作系统内核

1. 项目概述:这不是又一个“智能体玩具”,而是一套可落地的企业级协同操作系统你可能已经刷到过“DeepAgents”这个词——它不像LangChain那样铺天盖地讲链式调用,也不像LlamaIndex专注文档检索,更不是某个大厂刚开源就迅速沉寂的…

作者头像 李华
网站建设 2026/9/10 3:15:27

指数移动平均EMA与一阶低通滤波等价性解析及工程实践

先说个我这些年折腾数据滤波和量化指标时最深的体会:指数移动平均(EMA)和一阶低通滤波,本质上就是同一个东西。一个来自金融技术分析,一个来自信号处理与控制系统,但剥开外壳,内里的递归公式长得…

作者头像 李华
网站建设 2026/9/10 3:12:47

哪些学校在用华宸AI智评?2026年9月核实的40所高校名单和各校要求!

哪些学校在用华宸AI智评?2026年9月核实的40所高校名单和各校要求! 先把标题里的两个问题直接回答了。哪些学校在用:2026年9月我逐条核实到明确要求学生使用华宸AI智评的高校有40所,从中国农业大学、兰州大学、郑州大学这类老牌本…

作者头像 李华