ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

AI芯片软硬件协同设计:有效算力、编译器与部署实战

AI芯片软硬件协同设计:有效算力、编译器与部署实战

做了几代 AI 芯片的软硬件协同设计之后,我最大的一个感受是:“AI 芯片”这四个字本身就很容易让人跑偏。做一颗能跑模型的芯片并不难,难的是让模型在真实负载下跑出接近标称算力的成绩,而这背后几乎全是软硬件接口的问题。这个系列前两篇聊过宏观的架构轮廓和工具链布局,这篇“3”想再往下钻一层,把 AI 芯片软硬件设计中最容易被忽略、却最能决定项目成败的几个环节摊开讲:有效算力与峰值算力的差距、架构路线取舍、指令集与内存模型、编译器在整个项目里的位置、芯片回来之前的软硬件联调,以及部署阶段绕不开的量化、稀疏化和 Runtime 闭环。

这篇东西不是科普稿,也不是给投资人看的 PPT。它更适合真正在做 AI 芯片或准备做 AI 芯片的人:芯片前后端工程师、编译器/工具链开发者、驱动与 Runtime 工程师,以及需要做 AI SoC 选型评估的架构决策者。很多内容是我在实际项目里踩过的坑,写着写着可能有点直白,但确实是想让后来的人少走弯路。

1. 有效算力与峰值算力之间的鸿沟:AI 芯片性能兑现的真正战场

先抛一个反直觉的结论:AI 芯片的账面算力(TOPS)在发布 PPT 上有多漂亮,和最终用户在真实模型上跑出来的吞吐量,完全是两码事。做了三代产品之后,我对团队里的硬件工程师说得最多的一句话是:“你们的 KPI 不是 TOPS,是用户在真实网络上的有效算力。”

因为从硬件设计到软件完全跑起来,中间隔着三道坎:片外带宽够不够喂饱计算阵列、算子形状能不能贴合硬件的数据流、编译器和驱动有没有把指令调度到最优。这三道坎任何一道没跨过去,账面算力就得打个对折甚至三折。

1.1 账面算力与实测吞吐:一个典型 NPU 的真实写照

举一个很常见的例子。一款设计目标为 16TOPS INT8 算力的 NPU,PE 阵列是 32x64 的 MAC 阵列,主频 1GHz,配套 DDR4-3200,理论带宽约 25.6GB/s。这种规格在规格表上挺能打,但我在类似平台上跑过 ResNet-50,实测吞吐往往只有标称的 40%-60%,也就是 6-9TOPS 的有效算力。如果跑 Transformer 类的模型,情况只会更难看,因为 attention 里面的 QKV 投影、softmax、LayerNorm 这些算子形状极不规则,硬件利用率掉到 30% 以下都常见。

差距去哪了?很多时间根本没有花在“计算”上,而是花在“等数据”上。PE 阵列在片内等着一块块 weight 和 feature map 从 DDR 里被搬进来,片内 SRAM 容量就那么大,搬一次算一会儿,算完再搬下一块,计算阵列真正满负荷工作的时间占比低得可怜。用开车来类比就是:高速路段设计时速 120,但上下匝道只有一个车道,你实际平均时速可能连 40 都不到——而这个“匝道”,就是 SRAM 容量和 DDR 带宽共同决定的访存瓶颈。

1.2 内存墙:堆算力越猛,带宽越先到头

为什么带宽会比算力更早卡住?用一次简单卷积的手工估算就能说明白。假设一个 3x3 卷积层,输入是 128 通道的 56x56 feature map,输出也是 128 通道。计算量大约是:56x56x128x128x3x3x2,约 9 亿次 FLOPs。这个量级对 16TOPS 的 NPU 来说,理想情况下 0.06 秒就跑完。但如果每个算子计算完都把中间结果写回 DDR,下一层再读进来,那么一次完整的 layer 读写就要搬运 2x56x56x128 字节(约 1.6MB),整个网络几十上百层累加起来,访存数据量会膨胀到计算量的几十倍。在 25GB/s 的带宽下,光搬数据就足够把推理时间拖垮。

所以做 AI 芯片,架构选型时第一优先级不是把 MAC 阵列堆多狠,而是把“每层计算需要从片外拿多少数据、中间结果能不能留在片上”算清楚。真正的优化主战场包括:片上 SRAM 容量怎么分配、多级缓存/片上 Buffer 怎么设计、数据复用率能不能提上去,以及 DMA 引擎能不能在计算的同时把下一块数据预取进来。这些软硬件协同的细节,才是有效算力的真正来源。

1.3 算子形状与硬件规则的错位:被低估的第三种损耗

带宽不是唯一瓶颈。深度神经网络的计算形状非常杂:1x1 卷积本质上是矩阵乘,但 3x3 卷积是 im2col 或直接卷积,depthwise 卷积每个通道单独处理,group conv 又要在通道维度分组,attention 里还要做各种 gather/scatter 和动态 shape。这些形状落在固定尺寸的 PE 阵列上时,会产生大量 padding、补零、低利用率的小算子。

我在一次适配 YOLO 系列模型时就踩过这个坑。目标检测网络里有一些很小的分支层,比如 1x1 conv 输出通道只有 16,而 PE 阵列一次处理 64 通道,结果就是硬件为了喂满阵列,不得不把多个不相关的通道拼在一起算,或者干脆空转。这类损耗在纯 GEMM 基准测试里完全看不出来,但一跑真实网络就原形毕露。所以做芯片架构设计时,最忌讳只看理论峰值,一定要拿目标负载里的真实算子分布做一遍映射统计。硬件是为数据和算子服务的,不是为浮点峰值服务的。

2. 架构路线的岔路口:脉动阵列、SIMT 与可重构数组

聊完性能差距的来源,必然会走到架构选择这个问题上。AI 芯片最常见的三种路线——SIMT 风格(GPU 式)、脉动阵列风格(TPU 式)、可重构数据流风格(CGRA)——各有各的脾气,选择哪条基本决定了后面编译器、驱动和 Runtime 要付出多大的代价。

很多人以为选型就是看算力密度,实际上选型选的是“软件栈的未来成本”。我见过不止一个项目,最初选了一个看起来算力密度最高的架构,做了一年发现编译器根本驾驭不了,最后被迫砍掉一半功能。这一节把三种路线放在一起对比,重点讲它们对软硬件协同设计的影响。

2.1 SIMT 的隐藏延迟策略,在 AI 推理场景的错位

GPU 式 SIMT 架构的思路是用大量线程并行来隐藏访存延迟:一个 warp 在等内存数据时,调度器立刻切换到另一个 warp。这种设计对大规模并行计算非常有效,训练场景下尤其是这样,大批量、大矩阵,GPU 几乎能把计算阵列喂得满满的。

但推理场景往往 batch 很小,甚至 batch=1。此时 GPU 的大量线程切换机制、shared memory 同步、warp 调度器本身的功耗和面积开销,反而成了负担。比如做端侧推理时,经常出现一个算子只启动了 8 个线程做 elementwise 操作,但整套调度器和多级缓存仍然要全功率待命,能效比自然很难看。不是说 SIMT 不好,而是要用对地方:它适合做“大而整齐”的工作,不适合“小而杂”的推理负载。

2.2 脉动阵列:让数据在 PE 之间流动,换掉随机访存

脉动阵列是另一种极端:把一组 PE 按照规律连起来,数据像流水线一样从一端流入,每个 PE 只和自己的邻居交换数据,避免了对片内多端口随机读写的依赖。这个思路在能效和面积效率上表现极佳,因为你不需要给每个 PE 配独立的寄存器文件和读取端口,数据复用基本靠流动实现。

但脉动阵列有它的刚性:阵列维度固定,每个 PE 的运算宽度固定,遇到非常规形状的算子就需要做大量边界处理。最典型的是 attention 里的 softmax 和 LayerNorm,这些算子计算非常简单,但它们在数据流里的位置决定了阵列很难用脉动方式覆盖——你必须把数据从阵列里倒腾出来做这些操作,再送回去。所以脉动阵列设计多了一条潜规则:重点服务 GEMM 类算子,其余杂算子交给旁边的小向量单元。没有这条配合,整颗芯片的实际使用率会很难看。

2.3 CGRA 的野心与代价:灵活性的钱从编译器那里出

CGRA(粗粒度可重构数组)的思路更激进:配置 PE 之间的互联拓扑来匹配不同算子。理论上,它为每个算子都能生成最适合的数据流结构,灵活性比固定脉动阵列高得多。但问题在于:配置空间爆炸式增长,最优映射是 NP-hard 级别的搜索问题,最终都得靠编译器和调度器来兜底。

我接触过的 CGRA 项目都有同一个特点:硬件看起来惊艳,软件团队苦不堪言。因为编译器要从巨大的配置空间里找到一个接近最优的解,同时还要保证时序、端口和 Buffer 没有冲突,编译时间动不动以小时计。对于面向市场交付的 AI 芯片团队,这几乎是不可能接受的工程体验。现实的做法往往是 CGRA 和固定算子单元混着来:用一小块可重构阵列覆盖杂算子,用固定流程的 MAC 阵列覆盖主力 GEMM。想明白这一点,很多架构争论其实可以提前画上句号。

3. 指令集与内存模型:NPU 的“宪法”和“土地制度”

架构路线定了,接下来要解决的是指令集和内存模型。这是软硬件协同设计中最像“立宪”的环节:指令集规定了软件能用哪些能力,内存模型规定了数据怎么在系统中流动。这两件事定了,编译器和硬件团队就分头干活了,后面再想改,代价非常大。

反复强调这个是因为在实际项目里,指令集设计经常被压缩到很短的周期,导致后面编译器团队叫苦连天。反过来,内存模型选择失误的话,后期性能调优几乎没有空间。

3.1 显式搬运与隐式缓存:两条很难回头的设计路线

内存模型有两个基本选项。第一种是显式搬运模型,软件(主要是编译器)负责控制 DMA 把数据从 DDR 搬进片上 SRAM,算完了再搬回去,类似 DSP 的做法。第二种是隐式缓存模型,硬件里做一个带 cache 一致性的缓存层次,软件像访问普通内存一样访问数据,硬件自动维护。

显式搬运模型的优点是性能确定、可预测——你知道每个时钟周期数据在哪里,编译器可以做出精确调度;缺点是把大量负担压在编译器上,工具链一旦做得不好就是灾难。隐式缓存模型软件写起来轻松,但硬件要处理缓存命中率、一致性协议,而且遇到不规则访存时性能波动非常大,没法给编译器一个稳定的调度依据。

我做设计时更倾向于显式搬运模型,因为 AI 算子的访存模式在编译期大部分是可静态分析的。数据在哪里、什么时候需要、什么时候写回,编译器都能算出来,这正是显式搬运模型的舒适区。而隐式缓存看着对软件友好,真实做下来容易演变成“硬件团队省了事,性能永远到不了位”。

3.2 标量、向量、矩阵三级指令:NPU 的职责切分

无论走哪条路线,NPU 指令集通常拆成三级。标量指令负责控制流、地址计算、循环计数和同步:一个 for 循环展开成几条标量指令,告诉下一个计算单元“现在该算这组数据了”。向量指令负责逐元素操作:ReLU、sigmoid、elementwise add、normalize 这些,它们不是矩阵乘,但数量极多,如果没有专用向量指令,编译器只能拿矩阵单元硬凑,效率非常低。矩阵指令则是整个芯片的算力核心,负责 GEMM/GEMV,通常对应一个或多个专用计算阵列。

这三级的调度配合很关键。我见过一个早期项目,没有独立向量单元,所有激活都靠矩阵单元做 matmul 形式计算,结果是资源利用率低、功耗还高。后来加了向量单元,整个模型性能提升了 30% 以上。做指令集设计时,千万不要只盯着矩阵乘,去统计一下目标网络里 elementwise 和 reduction 类操作的占比,结果会让你吃惊。

3.3 寄存器视图的一致性,是软硬件协同的第一份合同

指令集定好了,紧接着就是寄存器视图。寄存器列表在工程上就是软硬件团队之间的“合同”:硬件按这个实现,软件按这个编程。但实际项目里,寄存器视图是最容易产生分歧和 bug 的地方。

典型问题有三种:一是地址对齐,软件按 32 位写了某个控制位,硬件实现时按字节处理,结果位错位;二是位域更新时序,软件写完配置寄存器立即去读状态寄存器,但硬件还没同步完,读回来的值还是旧的,轮询永远超时;三是中断状态位没有在硬件侧做“读清除”或“写 1 清除”,软件读完后状态不自动拉低,中断风暴直接挂死系统。

我的建议非常直白:用统一的寄存器描述文件(例如 IP-XACT 或 SystemRDL)作为唯一事实来源,从它同时生成 RTL、头文件和文档。这样从源头消除“文档和实现不一致”的问题。这个习惯一定要在项目第一天建立,否则等芯片流片回来再靠人工对文档,你会在验证阶段浪费掉几个月的窗口期。

4. 没有编译器就没有护城河:从 PyTorch 到芯片上的二进制

很多做 AI 芯片的团队,把大部分精力放在 RTL 上,编译器团队反而是后妈养的。在我看过的项目里,凡是这么做的基本都后悔了。AI 芯片的竞争力不是裸片面积小、主频高,而是工具链能让用户模型在上面的有效算力逼近理论值。硬件只是上限,编译器才决定了用户实际能摸到什么水位。

编译器不是“把 PyTorch 模型转成二进制”这么简单。它掩盖了一整条流水线:模型导入、图优化、算子融合、算子映射、指令调度、内存规划、代码生成。每一环都直接和前面的指令集、内存模型、阵列拓扑耦合。

4.1 算子融合:把反复进出 DDR 的中间结果留在片上

算子融合是编译器里收益最明显、也最见功力的优化。举一个最简单的例子:卷积 + Bias + ReLU + 池化。如果不做融合,每一层算子都要把中间结果写回 DDR,然后再读出来做下一层,一个字节的中间结果来回要付两次访存的钱。融合之后,整条链在片上 SRAM 里一气呵成,DDR 只需要读写最终输出。

实测里,同一个模型在融合前后的推理耗时差距经常超过 30%-50%,这个数字不是理论推导,是我在项目里实测出来的。编译器团队要做的是在 IR 层面识别这种算子链,然后在后端把它们映射到同一条硬件流水线。为此硬件设计阶段就要预留 fusing 的能力:片上 Buffer 能不能串联、DMA 能不能一次搬多块数据、Ping-Pong 缓存能不能在计算的同时预取下一块。这些都是软硬件协同中的“契约”,编译器单方面做不来。

4.2 静态内存规划与指令调度:替硬件做一张精确时间表

AI 模型编译期静态分析的优势在于:数据的生命周期基本是已知的。编译器可以提前算出来每一块 feature map 什么时候被产生、什么时候被消费、什么时候可以被回收,然后为整个网络排出一张 DDR 与 SRAM 之间的搬运时间表。

实际工程里,这一步会涉及给片上 SRAM 分区,比如配置 buffer 区、计算输入区、输出暂存区、参数区;还要为每个 DSP/DMA 通道分配优先级,避免同一个时间点多个搬运请求抢占带宽。最简单也最见效的技巧是 double buffering:计算第 N 块数据的同时,DMA 预取第 N+1 块,这样计算阵列永远不饿肚子。但要小心依赖冲突——如果第 N+1 块的数据是第 N 块算出来的,你没法预取,只能老老实实按顺序执行,编译器调度时要把这类数据依赖关系先算清楚。

早期项目里我用过一个笨办法:先在周期级模拟器上测出每个算子的跑分,再把这些算子作为节点做 list scheduling。这个过程很像排课表:每个算子占用的资源不同,数据依赖又给它们排了先后,谁先谁后、哪个资源并行利用,调度器在几万次约束里找出一个可行的排程。编译器做到这一步,才叫真正开始兑现硬件能力。

4.3 给 LLM 做适配的真实体验:头疼的往往不是 GEMM

最近两年,所有 NPU 工具链都被一个现实逼着做转型:给 LLM 做适配。一开始很多人以为 LLM 的主角是巨型 GEMM,把 GEMV 和 GEMM 优化好就足够了。实际做下来你会发现,最耗时间的都是“杂活”:attention 的 QKV 投影之前要做一堆 reshape 和 transpose,padding 和 mask 操作在图上到处都是,softmax 里要对整行做 max-subtract-exp-sum,LayerNorm 要对均值方差做归一,还有 KV cache 的索引与追加。这些算子单独看都不大,但数量多、形状交互复杂,最容易把编译器的优化彻底打乱。

另一个难啃的点是动态 shape。LLM 推理时序列长度是动态的,KV cache 大小也随轮次变化,这直接挑战了编译器“静态分析一切”的根基。为了支持动态长度,编译器不能把所有 buffer 都按最大尺寸静态分配,否则片上内存会爆炸;也不能每一步都重新做地址计算,否则调度开销会失控。现在相对稳妥的做法是分层分块:给批量尺寸做静态规划,给序列长度留动态余量,再用运行时指令搬运和地址重计算来兜底。这个方向还在快速演进,但有一点已经非常清楚:给 LLM 做适配,工具链团队的工作量远超想象,绝不是“跑通一个 GEMM Kernel”就够的。

5. 芯片还没回来,软件栈也得先转起来:虚拟原型与最小系统调试

说完了编译器,接着聊一个在进度压力下特别容易被挤掉、但真正决定流片后能否快速 bring-up 的环节:芯片回来之前的软硬件联调。流片周期长,芯片从 tapeout 到 sample 回来往往要几个月,而你可能需要在芯片回来当天就跑通一个 demo,给老板和客户交代。等样品到了才开始搞软件栈,几乎必然翻车。

这里的经典做法是:在 RTL 还没冻结之前,就搭一套虚拟原型和一个最小硬件调试系统,让软件团队提前把驱动、Runtime 和关键算法调通。

5.1 虚拟原型:把“芯片回来之后”的软件工作提前几个月

虚拟原型就是用 SystemC/TLM 这类方式把 SoC 和 NPU 的关键行为做成寄存器级/事务级模拟模型。它跑不了太大规模的数据,但寄存器读写、中断触发、DMA 搬运这些控制流语义是可以模拟出来的。有了虚拟原型,驱动团队可以在真实芯片回来之前就把设备枚举、中断处理、DMA 配置、寄存器读写的代码写好并调通,编译器团队也可以把编译产物先灌进去做功能验证。

必须诚实说清楚它的局限:虚拟原型不是周期精确的模拟器,跑性能分析和功耗分析基本不靠谱。它只适合验证“控制流对不对”,不适合验证“性能有多快”。所以在用虚拟原型时,我会单独搭一个轻量的周期级模拟器,专门给编译器做性能回归。两者分开用,效率最高。

5.2 寄存器与中断的验证盲区:联调时踩得最深的坑

做软硬件联调这些年,我见过太多了寄存器视图问题在真实芯片回来后集中爆发。最典型的场景:软件驱动往 DMA 控制寄存器里写完描述符地址,就把启动位置 1,然而硬件侧是先读启动位再取描述符地址。如果顺序反了,第一轮搬运拿到的是空描述符,DMA 直接卡死,中断永远不来。这类问题在 RTL 仿真里往往能被发现,但如果验证环境只做定向测试,很容易漏掉。

另一个高发坑是中断状态位不清零。有些硬件把中断状态寄存器设计成“只需读,不需要清”,但实际上中断源会持续拉高,CPU 进入 ISR 后读了状态位,中断信号还没掉,一退出又立刻触发,整个系统空转在中断风暴里。最有效的工程防护是在硬件里加一个测试模式寄存器,允许软件强制置位或清零中断源,这样驱动开发时就可以自主制造和消除中断,而不用每次都求硬件团队改 RTL。

5.3 裸片点亮第一阶段:最小系统三板斧

芯片 sample 回来以后,软件 bring-up 别急着跑模型。我在多个项目里验证过最有效的顺序是先过一个“最小系统三板斧”:

第一板斧,剪断一切不必要的上电分支,把最小系统需要的电源域、时钟域整理干净,确保 CPU 能稳定跑起来、JTAG 能连上。第二板斧,确认系统时钟和复位:很多上电不稳的问题都表现为寄存器读回全是 0 或者全是 0xFFFFFFFF,跑一遍地址自检就能定位。第三板斧,拿一个最简单的裸机 demo 验证“搬运-计算-写回”三步:往片上 SRAM 写一块数据,让 DMA 从一个地址搬到另一个地址,再把结果读回来对比。

另外特别提醒:上电初期一定要考虑看门狗的问题。我看过不止一次,整个系统一直在被看门狗复位,软件怎么查都觉得硬件坏了,最后发现是默认看门狗超时时间设置得太短,代码还没跑起来就又被复位了。先把看门狗关掉,等最小系统验证通过,再打开并配置正确的喂狗策略。

这几步走顺之后,再把编译器生成的第一个 model checkpoint 跑起来,这样的 bring-up 路径基本不会出大岔子。

6. 部署最后一公里:量化、稀疏化与 Runtime 闭环

芯片能跑模型了,但离“用户真正部署”还有最后一公里:模型太小、内存放不下、延迟下不来,需要量化;网络里有大量冗余,需要稀疏化;硬件能力最终要通过 Runtime 和驱动暴露给用户,需要把整个闭环打通。这一节聊的几个话题,都是我在部署真实客户模型时反复踩过坑的地方。

6.1 INT8 量化:从 FP32 到定点,不是丢精度而是重建分布

量化本质上不是“把小数点砍掉”,而是在目标硬件支持的精度空间里重建数据的分布。最主流的两种做法是 PTQ(训练后量化)和 QAT(量化感知训练)。PTQ 的优点是省事,拿一组代表性数据,统计每一层激活值的 min/max 或分布直方图,然后用 KL 散度或 MSE 找一个尽量保住信息的 scale(有些硬件还有 zero point 概念)。QAT 则是把量化误差放进训练过程里,让模型自己适应低精度表达,精度通常更好,但要重新训练,成本高。

实际项目里,PTQ 最容易炸的位置往往在网络的特殊结构上:残差结构的加法会把两路量化误差叠加;LayerNorm 对数值尺度敏感;softmax 的输入差一点点,输出概率就变化很大。处理经验是:这些特殊层不要一刀切量化,保留 FP32 中间计算或用混合精度,比如用 INT8 做矩阵乘,但累加器和最终输出用 FP32 写回。这张表格是我的经验总结:

量化方法工作量精度损失硬件要求
动态量化低,运行期做 scale中等,适合大模型粗部署需要硬件支持动态 scale
PTQ中,需要标定数据集通常可控,特殊层要额外处理需要校准逻辑和 per-channel 支持
QAT高,要训练最低需要硬件指令集支持伪量化节点

6.2 稀疏化落地:跳过计算的同时别把流水线打乱

稀疏化在理论上能白赚很多加速比,但硬件端实现起来很容易“拣了芝麻丢西瓜”。剪枝后的模型参数变成稀疏矩阵,如果硬件还是按密集矩阵去读,那剪枝省下的 FLOPs 全部变成无效的访存和等待,速度不升反降。所以做一个高效稀疏加速器的核心,是让硬件能跳过零值计算,而不是把稀疏数据重新填回密集格式。

目前工程上最成熟的是结构化稀疏,典型如 2:4 稀疏——每 4 个连续权重里最多 2 个非零。这种规则让硬件可以用一个很小的索引位图跳过零值计算,流水线不会乱。而我强烈建议普通团队先别碰非结构化稀疏,虽然压缩率高,但它会让编译器的数据布局、DMA 搬运和 PE 调度复杂好几倍,后端访存很容易变成随机访问,那还不如密集计算快。

6.3 Runtime 与驱动:AI 芯片更像一个“显式调度的协处理器”

最后一段是 Runtime 与驱动。和 CPU/GPU 那种“跑指令流”的设备不同,AI 芯片更像一个需要被显式调度的协处理器:Runtime 要在用户空间分配好片上内存、配置好算子序列、把 DMA 描述符提交给内核驱动,内核驱动再负责 IOMMU 映射、中断处理和硬件同步。分工上,驱动尽量薄、只碰硬件,Runtime 尽量厚、负责模型级逻辑和调度。

真正在部署阶段会遇到的麻烦是显式设备管理的复杂度:多个推理任务并发时,到底谁占用计算阵列,谁在等带宽;片上内存碎片如何避免;高优任务怎么样能插队但不打断低优任务正在执行的 DMA。这些不是 RTOS 概念能直接套的,必须针对 NPU 的特性设计调度策略。我的经验是先做一个简单的任务队列和优先级抢占模型,把稳定性跑出来,再谈性能优化——部署阶段的用户不怕慢一点,怕的是动不动就死锁或 OOM。

写到这里,话题差不多收住了。最后再分享一点个人的体会:如果让我重新立项做一款 AI 芯片,我会在第一周就把三件事定下来——编译器团队用的 mock 接口、寄存器描述文件的唯一事实来源、以及一个能跑软件栈的虚拟原型。硬件确实决定了算力天花板,但能让用户实际拿到的性能贴近天花板,软件栈从第一天开始就是主战场,不是可有可无的附属件。

返回列表