ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

交换芯片数据通路核心机制:从Crossbar到Cell Fabric

交换芯片数据通路核心机制:从Crossbar到Cell Fabric 1. 从一颗 51.2T 芯片说起数据通路为什么是交换芯片的“骨架”做了快十年的网络芯片和交换机系统我经常发现一个现象大家在选型或者看芯片规格书的时候眼睛基本都盯着端口速率、查表容量、ACL 条目、缓存大小这些“可见参数”很少有人会去细看框图中间那段密密麻麻的总线和队列结构。但恰恰是这段东西决定了一颗芯片能不能在标称的线速下稳定工作也决定了你把它塞进数据中心后遇到突发流量时会不会莫名其妙地丢包。数据通路Data Path说白了就是报文从入端口进来经过解析、查表、编辑、缓冲、交换最后从出端口发出去的完整路径。很多人以为交换芯片最难的是查表算法或 ACL 匹配实际工作中你会发现查表通常可以靠多级流水和哈希并行撑过去真正让架构师睡不着觉的是数据通路上的带宽、时序和拥塞控制。Crossbar、VOQ、Shared Buffer、Cell Fabric 这四个词基本就是理解这颗芯片内部骨架的四把钥匙。说个直观的账。一颗 51.2Tbps 的芯片内部逻辑时钟通常是 1GHz 上下。哪怕内部数据总线做到 512bit一条总线理论带宽也就 512Gbps。51.2Tbps 意味着同一时刻要有一百条这样的总线在满速搬运数据而且这还没把写缓冲和读缓冲的双向流量算进去。所以你会发现真实芯片里的数据通路不是一根线而是几十上百条并行总线、多级交叉矩阵和一大片存储阵列的组合。哪一级设计得不好哪一级就会成为整个芯片的吞吐瓶颈。这篇文章我想先把数据通路这张图画清楚重点讲四个核心机制Crossbar 为什么存在、VOQ 怎么解决队头阻塞、Shared Buffer 怎么分配内存、Cell Fabric 为什么要把报文切碎。调度算法、QoS、可编程处理这些内容后面有机会单独开一篇讲因为水太深一篇塞不下。2. Crossbar 到底解决什么问题从全互联到 N×N 交换矩阵2.1 为什么不用“谁都能连谁”的全互联最原始的交换思路是每个输入端口到每个输出端口都拉一根独立的线类似电话交换时代的总配线架。N 个端口就需要 N² 根连线64 端口就是 4096 根这还没算差分信号和地线。放在芯片内部布线资源会直接爆炸时序收敛根本做不完。所以 Crossbar 的出现本质上是在“连通性”和“物理可实现性”之间找了个折中所有输入端口和所有输出端口之间共享一个 N×N 的交叉点阵列通过交叉点的开关状态决定某个输入在某个时刻连到哪个输出。交叉点本身只是个简单的晶体管开关真正难的在于“控制”。任何一个时刻一个输入端口只能连接到最多一个输出端口一个输出端口也只能接收最多一个输入端口的流量。也就是说Crossbar 提供的是“物理连通可能性”具体连不连、连哪一对需要一个仲裁器在每个时间片内做决策。2.2 调度匹配问题Crossbar 的核心矛盾Crossbar 的调度问题本质上是一个二分图匹配问题左边 N 个输入右边 N 个输出有报文等待的输入和有空闲接收能力的输出之间才存在“边”。理想情况下我们希望每个时间片都找到最大匹配这样 Crossbar 的利用率最高。但最大匹配算法复杂度太高硬件上根本跑不完所以实际芯片里用的多是近似最大匹配的启发式算法。最经典的是 iSLIP 这类轮询算法。它的思想很简单每个输入维护一个指向输出的轮询指针每个输出也维护一个指向输入的轮询指针。每一轮迭代中输入先向所有有排队需求的输出发请求输出从多个请求里挑一个授权授权信号发回去之后输入再从中选一个确认。关键是每次成功匹配后指针会移动到下一个位置这样避免总是优先服务同一个端口保证公平性。多轮迭代可以逼近最大匹配硬件上通常做 3 到 4 轮就够了轮数再多收益非常有限但延迟会直线上升。2.3 工程里真正头疼的三个 Crossbar 问题排除了调度算法Crossbar 在实际芯片设计中还有几个绕不开的坑第一是组播复制。Crossbar 一次只能让一个输入连到一个输出组播报文需要复制成多份分别占用不同的时间片。这意味着如果一个端口有大量组播流量即使每个单播队列都空闲Crossbar 的调度器也会被组播请求占满单播时延会被拉高。很多芯片的做法是把组播单独做一份复制队列在入端口就复制好然后像单播一样调度代价是缓冲翻倍。第二是端口速率不均衡。如今芯片上经常混合 400G、100G、25G 的端口不同端口的发包能力差异很大。Crossbar 的仲裁必须考虑端口速率的权重否则慢端口会频繁占住交叉点快端口反而饿肚子。这个逻辑跟 CPU 调度里的权重轮询很像但硬件上要把每个端口的速率映射成权重值换算起来非常繁琐。第三是信号完整性。交叉点阵列是典型的深组合逻辑连线长、扇出大在 1GHz 以上时钟下很容易成为时序瓶颈。所以高端芯片里 Crossbar 往往不是一块完整的阵列而是被拆成多个小的 crossbar slice每个 slice 处理一部分数据位宽最后并行拼出完整带宽。这种做法也直接影响了后文要讲的 Cell Fabric 设计。提示Crossbar 并不等于无阻塞交换。物理上所有交叉点都在但同一时刻的仲裁结果决定了可用的通路只有 N 条。调度算法的好坏直接决定 Crossbar 的实际利用率能到 90% 还是只有 60%。3. VOQ 的诞生队头阻塞逼出来的 N² 队列设计3.1 一个简单的队头阻塞例子如果不做任何队列优化每个输入端口只有一个 FIFO报文按到达顺序排成一条队。问题是假设端口 1 的队头报文要去端口 8但端口 8 当前正忙而队里第二个报文其实要去空闲的端口 2。因为 FIFO 的特性第二个报文必须等队头先走哪怕端口 2 空着也只能干等。整个输入端口的吞吐就这么被一个队头报文卡住了。这叫 Head-of-Line Blocking简称 HOL 阻塞。HOL 阻塞的代价非常大。理论分析早就证明在均匀随机流量下单个 FIFO 的 Crossbar 最大吞吐只有约 58.6%远达不到线速。任何商用交换芯片都不可能接受这种效率所以必须想办法让“去不同端口的报文不要互相拖后腿”。3.2 虚拟输出队列换一种排队方式解法就是 VOQ虚拟输出队列。核心思路是在每个输入端口内部不再维护一条 FIFO而是为每个输出端口单独维护一条逻辑队列。这样从端口 1 来的报文如果要去端口 8就进端口 1 的 VOQ[8]要去端口 2就进 VOQ[2]。队头各自独立一个队列里的队头被阻塞不影响其他队列里的报文向空闲端口发送。“虚拟”两个字体现在存储上。这些队列并不是真的在每个输入端口旁边放 N 块独立 SRAM而是在一块大的共享存储池里划分逻辑队列。每个队列只是一个链表头指针和长度计数器真正的报文数据保存在公共的 Cell Buffer 里。这样做的好处是降低存储碎片坏处是链表管理逻辑非常复杂尤其在多端口并发读写时指针操作根本来不及顺序执行需要大量并行硬件加速。3.3 N² 的内存代价和 QoS 叠加VOQ 最大的代价是队列数量爆炸。N 个输入端口每个端口 N 个虚拟队列一共是 N² 个队列。64 端口就是 4096 个队列这还不算优先级。现在数据中心芯片普遍支持 8 个 QoS 等级所以实际队列数是 N×N×C也就是 64×64×832768 个队列。每个队列至少有一个描述符描述符里包含头尾指针、队列长度、时间戳等信息8 到 16 字节。光描述符内存就要 512KB 左右看起来不大但调度器要在一个时间片内扫描成千上万个描述符的状态来决定谁有资格发送这对调度器的处理能力是巨大的考验。所以你会看到现代交换芯片的调度器根本不是软件能干的活而是由多级硬件状态机加专门的 traffic manager 完成。调度器读取 VOQ 的占用情况按算法的优先级挑选队列再向 Crossbar 仲裁器发起请求。VOQ 长度信息也会通过信用credit机制反馈给上游用来做反压防止某个输出端口拥堵导致缓冲溢出。3.4 实现 VOQ 时容易被忽略的细节实际项目里VOQ 的水线设置比算法本身更容易出问题。队列太长报文排队时延变大TCP 的 RTT 敏感应用会受影响队列太短突发流量一来就丢包。常见的做法是给每条 VOQ 设置一个动态阈值阈值随着空闲缓冲区的多少自动调整整体缓冲充裕时单条队列可以占用较多空间吸收突发整体缓冲紧张时单条队列的上限被压紧避免少数大流量把公共缓冲吃光。这里特别提醒一句很多人以为共享缓冲越大越好其实不对。共享缓冲如果无节制地允许抢占会导致低优先级流量饿死高优先级流量。QoS 队列不仅要隔离彼此还要在共享池的分配上体现优先级差异。这个“隔离与共享”的度是数据通路设计里最考验架构师经验的地方之一。4. Shared Buffer 的共享池生意内存利用率与访问带宽的平衡4.1 为什么每端口私有缓冲不划算假设 32 个端口每个端口配 16MB 私有缓冲总共 512MB。某一个端口在突发时流量巨大16MB 瞬间被灌满后面的报文只能丢与此同时其他端口可能完全空闲它们的缓冲闲着不用。这种“旱的旱死涝的涝死”的现象说明私有缓冲的利用率非常低。为了吸收同样的突发流量私有方案需要给每个端口都配置峰值所需的大缓冲总成本高得离谱。Shared Buffer 的逻辑正好反过来所有端口共享一大块缓冲池谁需要谁用。空闲端口不占内存繁忙端口可以借用整个池子的空间来吸收突发。这就是统计学上的复用效应网络流量越分散共享池带来的节省越明显。一般经验是同样吞吐等级下共享缓冲可以比私有缓冲节省 50% 以上的存储面积。4.2 动态分配的“水位”游戏共享池要真正工作靠的是精细的“水位管理”。最简单的共享池是绝对公平每个端口最多占 1/N。但这样又回到了私有化的老路因为繁忙端口永远只能用 1/N。所以实际方案都是动态阈值端口的可占用上限不是固定值而是根据当前空闲缓冲量来计算。常见的公式是端口可用阈值 (空闲缓冲量 该端口当前已用量) / 端口数 再乘以一个可调系数。空闲缓冲多的时候每个端口都能放开用空闲缓冲少的时候大家同时收窄。这个动态水位的计算在硬件里每个周期都要跑所以要设计成简单的近似运算不能做除法器之类的大逻辑。我见过不少团队在动态阈值上过度设计流水线延迟暴涨最后得不偿失。4.3 真正的瓶颈共享缓冲的读写带宽Shared Buffer 的经典问题是带宽墙。N 个端口在同一时刻可能都要写一个 cell、读一个 cell共享内存必须支持 N 个写端口和 N 个读端口同时操作。如果 buffer 是一个真正的单口 SRAM那完全不可能。现实中的解决方案是用多 bank 结构把内存切成若干个物理 bank通过哈希函数把不同流映射到不同 bank以减少访问冲突。即使这样bank 冲突依然存在。两个端口的数据恰好命中同一个 bank就必然有一个端口的访问要被推迟。为了把冲突概率降到可接受范围bank 的个数要远大于端口数比如 32 端口配 64 到 128 个 bank。还有个更麻烦的问题cell 数据通常跨 bank 存储读一个 cell 可能要并行访问多个 bank所以内部总线的位宽做得非常宽常见的是 4096bit 甚至更宽。这也是为什么芯片内部 SRAM 的面积占比非常大数据通路里的存储根本不像大家想象的只是“缓存”那么简单。4.4 Shared Buffer 与外部内存的互动光靠片上 SRAM 容量有限很多中高端芯片还会外接 HBM 或者高带宽内存来扩充缓存池。但外接内存在延迟和带宽上和片上 SRAM 不是一个量级。通常做法是片上 SRAM 作为快速缓冲存放正在被调度的活跃 cell外接内存作为慢速候选池存放溢出的报文。数据在两级存储之间搬移需要专门的 DMA 引擎管理。这里最容易踩的坑是外接内存的带宽很容易被大量小报文耗尽因为小报文的数据量小但读写的次数多内存的有效带宽大打折扣。所以设计二级缓冲时一定要参考实际业务里的报文长度分布而不是只看内存峰值带宽数字。5. Cell Fabric 的“信元化”思路固定长度带来调度自由度5.1 为什么要把报文切成 cell以太网帧长度从 64 字节到 1518 字节不等巨型帧更是可以达到 9000 字节。如果让变长报文直接进入交换核心调度器面对的时间片长度完全不可预测有的报文只要一个时间片有的要占几百个时间片这在硬件调度里是灾难。所以现代交换芯片普遍采用信元化Cellification方案把变长报文在入口处切分成固定长度的 Cell通常 64B 到 256B 之间然后以 cell 为单位在交换核心内传输和调度出口再重新组装成原始报文。固定长度的好处是调度变成“均匀时隙”问题。每个时间片调度器只需回答一个问题哪些输入端口发一个 cell 给哪些输出端口。所有 cell 占用时间一样长资源竞争变得非常规整Crossbar 仲裁和 Shared Buffer 分配都能用简单的计数器来跟踪。5.2 Segmentation and Reassembly切和拼的边界信元化并不是说把报文切了就完事难点在于“拼回去”。每个 cell 必须携带足够的信息才能在出口重组报文 ID、cell 序号、报文总长度、优先级等。如果采用定长 cell 而报文长度不是 cell 长度的整数倍最后一个 cell 会有填充需要标记实际有效字节数。重组一侧还要维护一张“正在拼包”的上下文表记录每个报文当前已经到达了多少个 cell、还差几个、是否乱序。乱序是重组里最头疼的问题。如果交换核心内部是多级 fabric不同路径的延迟不同同一个报文的 cell 完全可能乱序到达出口。处理方式有两种一种是出口缓存做重排按序号整理后再发另一种是在写进入口时给同一个报文的 cell 打上相同的路径标签强制走同一条路径从而保证顺序。后者的代价是牺牲路径均衡前者的代价是增加出口延迟。高端芯片往往两者结合默认强制同路径乱序仍然发生时再做重排兜底。5.3 Cell Fabric 的两种典型形态严格意义上Cell Fabric 是指以 cell 为单位进行交换的 fabric 结构它和 Crossbar 并不是互斥概念。第一类是基于 Crossbar 的 cell fabriccell 在输入侧排队调度器决定哪个输入端口的哪个 VOQ 发送到哪个输出端口fabric 本身每个时隙完成一次交叉连接。第二类是基于共享内存的 cell fabriccell 被写入一个公共的 cell buffer然后根据目的端口被读出来发送所有 exchange 操作发生在内存读写层级。实际芯片里两种形态经常融合。比如输入侧先做 VOQcell 进入一个大的共享内存池共享内存输出侧再通过一个较小的 crossbar 连到各个出端口。这样既享受了共享内存的高利用率又通过 crossbar 实现了多端口并发输出避免共享内存单独扛不住高带宽。而且 cell fabric 还有利于端到端的流控——上游发送 cell 之前先确认下游有足够的 cell buffer 接收防止丢包。这套机制就是 credit-based flow control在很多数据中心芯片里已经是标配。5.4 Cell 大小的选择是个权衡Cell 定多大直接影响芯片的面积、吞吐和时延。Cell 太小头开销占比大fabric 的效率低。比如 64B cell 在 100G 端口满速运行时每秒要处理约 2 亿个 cell调度器和重组表的工作压力非常大。Cell 太大头开销小但内部延迟增加小报文在入口等待凑满一个 cell 的时间也变长导致整体时延变差。行业里常见的折中是 128B 或 256B。做低时延场景的芯片会偏小做高吞吐场景的会偏大。这个选择没有绝对最优只能根据目标市场定。注意cell 大小还影响 headroom 计算。以太网 CRC 和帧间隙在入口处理时也要留够余量否则换算到 cell 数时会出现尾包溢出导致缓冲管理逻辑出错。这个问题在报文长度接近 cell 整数倍时尤其隐蔽容易在极端包长测试里暴露。6. 商用交换芯片的数据通路组合架构分类与瓶颈经验6.1 三类芯片的典型数据通路形态不同定位的芯片数据通路设计差别很大。我把常见方案归成三类有经验的同行应该能对号入座架构类型数据通路特征典型定位优势劣势共享内存主导单一 Cell Buffer所有端口读写共享池中低端、低时延框式接入实现简单缓存利用率高延迟低带宽受限于内存访问能力扩展性差Crossbar VOQ输入侧 VOQCrossbar 仲裁交换高端框式核心、数据中心 Spine扩展性好可支持非常高的端口速率调度复杂组播和 QoS 难处理吞吐依赖调度效率多级 Cell Fabric多级 Crossbar 或 Folded Clos 式交换结构超大规模、机箱式多板卡可跨越物理板卡带宽拓展无上限结构复杂信元在级间传递乱序风险大调试困难实际商用芯片很少是纯某一种形态。我看到过不少芯片框图表面上是 Crossbar但仔细看内部仍然有一个很大的 shared buffer 做 headroom 吸收也有标榜 shared buffer 架构的芯片其实输出侧藏着小规模的 crossbar。所以看芯片架构不能只看市场宣传要去看它内部的 cell 走向和排队点。6.2 决定数据通路上限的几个关键点我在实际项目中总结过一颗交换芯片数据通路能否跑满线速通常取决于几个点第一是“最短路径上有没有串行瓶颈”。有些芯片为了节省面积让所有端口共享一组查找引擎。查表是慢路径流量稍微不均匀就会出现排队等效带宽立刻下降。数据通路里任何串行处理点都可能成为瓶颈所以正规设计都会做多实例并行。第二是“内外带宽是否匹配”。端口总带宽是 25.6Tbps而内部 fabric 带宽设计只有三倍以下余量组播、广播风暴一来就爆。经典经验是 fabric 带宽至少要做端口总带宽的三到四倍才能承受多对一的 traffic pattern。第三是“Headroom 是否足够”。当端口从空闲状态突然变成拥塞时fabric 里已经存在的在途 cell 必须要有一个缓冲暂存区这个区域叫 headroom。如果 headroom 算少了拥塞窗口切换瞬间就会丢包即使端口后续拥塞很快缓解了也没用。Headroom 的计算要结合延迟带宽积和 cell 数量很多丢包问题最后查出来其实就是 headroom 不足。6.3 调试数据通路时我常用的几个切入点实际调试中我基本会按顺序检查先看端口统计里有没有 CRC err 或 undersize排除物理层问题再看 fabric 的 cell 计数是不是与报文计数一致定位切包/重组阶段然后看 VOQ 深度和丢包计数器的变化曲线判断是否调度不公或水线设置不合理最后才动 QoS 配置和 buffer 水位参数。这个过程能过滤掉大部分问题避免一上来就被复杂特性带偏。还有一个经验数据通路相关的丢包往往发生在 high-water mark峰值水位时刻而不是平均流量时刻。所以复现问题时不能只跑平均负载的流量要用突发脉冲流量并且要把监控周期设置得足够细。很多丢包问题在秒级平均曲线上完全看不出来但放大到微秒级就能看到缓冲被瞬间打满。7. 数据通路设计里那些“不说不知道”的边界经验做了这么多年每次评审新芯片的数据通路设计方案总有一些反复出现的共性教训这里挑几个印象最深的展开说。第一个是时钟域跨越问题。入端口是 SerDes 恢复出来的时钟域核心 fabric 是芯片主时钟域缓冲区和 VOQ 管理在跨时钟域时非常容易丢状态。设计不仔细的话FIFO 指针在异步边界上会读到不稳定的值导致队列长度计算错误进而触发错误的背压或者错误的丢弃。处理办法无非是两级同步加格雷码但这个环节的验证一定要做形式化验证光靠仿真很难覆盖所有时钟相位组合。第二个是 cell buffer 的写冲突处理。即使有多 bank哈希冲突仍然存在。芯片里最常见的做法是给每个 bank 配一个小型 pending FIFO当两个端口同时命中同一个 bank 时把其中一个访问暂存一拍。但这个 FIFO 深度如果不够冲突高峰时会溢出表现为随机丢包。这类问题隐蔽性极强因为平均冲突率很低只有跑到某些固定 pattern 的流量时才会触发。第三个是组播与单播共存时的 buffer 分配冲突。组播报文在共享缓冲区里通常只存一份但出端口计数时要复制多份。如果调度器在统计队列占用时把组播按复制倍数计算一个组播 cell 可能瞬间占用大量队列配额造成其他单播队列被误伤。正确做法是把组播的“存储占用”和“调度占用”分开统计存储上只算物理 cell 数调度上按复制倍数加权。这个细节做错的芯片组播场景下性能会非常诡异。第四个是 QoS 的优先级反转。VOQ 和 Shared Buffer 通常按优先级划分但少数情况下高优先级队列也会被低优先级队头阻塞。比如端口 A 的高优先级报文需要去端口 B但端口 B 的出口链路本身拥塞此时端口 A 的低优先级报文如果也是去端口 B它们会一起排队低优先级反而先占用了端口 B 侧有限的输出缓冲。解决这类问题要靠出端口的 Strict Priority 调度和足够的 per-priority 缓冲不能只靠入侧 VOQ 隔离。写到这里我把交换芯片数据通路的几个核心主题都过了一遍。下一篇文章如果继续写应该会侧重调度器内部的设计细节包括仲裁算法在硬件里怎么落地、组播调度怎么和单播共存、信用的维护与恢复机制以及 P4 可编程芯片对数据通路带来的新约束。这些内容比今天这篇更偏实践也更绕等我把素材再整理整理继续聊。
返回列表