AI芯片软硬件协同设计:数据流架构与算子映射的工程实践
1. AI芯片软硬件协同设计的整体思路拆解AI芯片这个领域做了几年下来最大的感受就是硬件指标再漂亮软件栈跟不上实际跑起来照样拉胯。这次想聊的软硬件设计系列第五篇核心聚焦在一个问题上——当算法模型迭代速度远超芯片流片周期时软硬件团队到底该怎么配合才能让一颗AI加速芯片从定义到落地不跑偏。先说清楚这个内容适合谁看。如果你是从算法转过来做芯片定义的或者是在芯片团队里负责软件栈、编译器、runtime的工程师再或者你是系统架构师需要评估AI加速方案那这篇东西应该能给你一些直接能用的参考。纯数字IC前端设计的朋友可能觉得偏系统层但里面关于数据流和存储层次的分析同样有借鉴意义。核心关键词其实就几个软硬件协同设计、数据流架构、算子映射、编译栈、量化部署。这几个词贯穿全文也是我在实际项目中反复踩坑总结出来的关键节点。为什么软硬件协同这么难打个比方传统CPU设计像是先修好一条高速公路然后让各种车软件自己上来跑路的标准很成熟大家按规矩来就行。但AI芯片不一样它更像是为某几类特定车型神经网络算子专门修的路如果修路的时候不知道车长什么样、载重多少、跑多快那修出来的路大概率没法用。更麻烦的是车型模型结构每隔几个月就换一代路却要修两三年。所以整个设计的核心思路必须围绕一个原则在硬件可编程性和能效之间找到平衡点同时让软件栈能够覆盖未来2-3代主流模型的演进。这句话听起来像废话但实际操作中大部分团队要么过度偏向硬件专用化导致灵活性归零要么过度通用导致能效打不过GPU。1.1 从模型特征反推硬件架构的关键决策我在实际项目中总结的一个方法论是先做算子画像再做架构选型。具体来说拿目标场景下Top 20的模型跑一遍profiling统计各类算子的计算量占比、访存占比、并行度特征。比如Transformer类模型矩阵乘占比通常在70%以上但LayerNorm和Softmax虽然计算量小访存却很密集如果硬件只优化矩阵乘这些小算子反而会成为瓶颈。这里有个实际数据可以参考某次我们做推理芯片定义时统计了目标场景下10个主流模型的算子分布发现矩阵乘GEMM平均占计算量的82%但占端到端延迟的只有55%左右剩下的45%延迟来自激活函数、归一化、数据搬运等非计算密集操作。这个数据直接影响了我们后来的架构决策——不能只堆MAC阵列必须把向量处理单元和片上网络带宽同步做上去。另一个关键决策是数据流架构的选择。常见的有三种权重 stationary、输出 stationary、行 stationary。选哪种取决于模型的权重复用率和激活复用率。以卷积神经网络为例如果batch size大激活复用率高输出stationary可能更合适如果batch size小但通道数多权重stationary能减少权重搬运。实际芯片通常需要支持多种数据流模式通过编译器根据算子特征动态切换。注意数据流模式不是越多越好。每增加一种模式硬件控制逻辑的复杂度指数上升编译器后端的验证工作量也会翻倍。我的经验是初期支持2-3种核心模式即可后续通过微码或可配置寄存器扩展。1.2 软硬件接口的抽象层次设计软硬件协同设计里最容易扯皮的地方就是接口定义。硬件团队希望软件告诉它要算什么软件团队希望硬件暴露能怎么算。我的做法是定义一个三层抽象接口算子层硬件提供一组基础算子指令如矩阵乘、卷积、激活、池化软件编译器负责将模型图拆解为这些算子的组合。这一层要求硬件算子覆盖度高但每个算子的灵活性可以有限。指令层硬件暴露VLIW或RISC风格的指令集软件可以手写汇编做极致优化。这一层是性能调优的最终手段但开发效率低通常只用于核心热点算子。运行时层提供内存管理、任务调度、同步原语等API软件框架通过这一层与硬件交互。这一层的稳定性直接影响上层框架的适配成本。这三层的边界划分需要在实际项目中反复调整。我经历过的一个教训是初期把算子层做得太细导致编译器需要处理上百种算子变体后端开发苦不堪言。后来合并了一批相似算子虽然单算子效率略降但编译器复杂度大幅下降整体交付时间反而提前了。2. 核心细节解析与实操要点2.1 数据流架构的量化分析与参数计算数据流架构的选择不能拍脑袋需要做量化分析。我通常用数据复用率和带宽需求两个指标来评估。以矩阵乘C A × B为例假设A是M×KB是K×NC是M×N。如果采用权重stationaryB固定则B的复用次数为MA的复用次数为N。片上缓存需要容纳B的一个分块大小取决于分块策略。具体计算过程假设MAC阵列大小为P×Q每个周期能计算P×Q个乘加。为了保持阵列利用率需要保证每个周期有足够的数据供给。如果B分块大小为K×QA分块大小为P×K则每个周期需要从片上缓存读取P×K K×Q个数据计算量为P×Q。计算访存比 P×Q / (P×K K×Q)。当PQK时该比值为P/3意味着每3次访存支持1次计算。如果片上缓存带宽不足阵列就会饿死。这个公式在实际选型时非常有用。比如目标算力是128×128 MAC阵列主频1GHz峰值算力32 TOPSINT8。如果计算访存比要求达到10以上则片上缓存带宽至少需要3.2 TB/s。这个数字直接决定了SRAM的位宽和bank划分方案。实操心得很多团队在架构评审时只关注峰值算力忽略了带宽匹配。我建议在架构定义阶段就做一个带宽-算力平衡表把每个算子的计算量、访存量、计算访存比列出来确保硬件带宽能覆盖90%以上算子的需求剩下的长尾算子通过编译器优化或降级处理。2.2 算子映射与编译器后端的关键技术编译器后端是软硬件协同的翻译官它的核心任务是把高层IR中间表示映射到硬件指令。这里有几个关键技术点循环分块与重排。以卷积为例原始循环是batch、输出通道、输出高、输出宽、输入通道、卷积核高、卷积核宽七层嵌套。编译器需要根据片上缓存大小做tiling把循环切分成适合硬件执行的块。分块大小的选择直接影响数据复用率和缓存命中率。我通常用搜索算法自动寻找最优分块参数而不是手工指定。搜索空间虽然大但通过代价模型剪枝后实际搜索时间可以控制在分钟级。算子融合。把多个小算子合并成一个kernel执行减少中间结果的访存。比如ConvBNReLU是经典融合模式。但融合不是越多越好过度融合会导致寄存器压力过大反而降低 occupancy。我的经验是融合后的kernel寄存器使用量不超过硬件上限的70%否则编译器会spill到本地内存性能急剧下降。量化与反量化插入。INT8量化是AI芯片的标配但量化误差会累积。编译器需要在合适的位置插入反量化操作把INT32累加结果转回浮点或高精度定点。插入位置的选择影响精度和性能。通常建议在累加完成后统一反量化而不是每个乘加后都转。# 一个简化的量化卷积伪代码示例 # 假设输入、权重均为INT8累加器为INT32 def quantized_conv(input_int8, weight_int8, scale_input, scale_weight, bias_int32): # 累加过程保持INT32精度 acc np.zeros(output_shape, dtypenp.int32) for k in range(kernel_size): acc input_int8[..., k] * weight_int8[k] # 加上bias已量化为INT32 acc bias_int32 # 统一反量化到浮点 output_float acc * (scale_input * scale_weight) return output_float这段伪代码展示了量化卷积的核心逻辑。实际硬件中乘加阵列直接处理INT8输入累加器位宽通常为32位防止溢出。反量化可以在向量单元中完成也可以由专用硬件模块处理。2.3 存储层次设计与数据搬运优化AI芯片的存储层次通常包括寄存器文件、片上SRAM、片外DRAM。数据搬运的能耗远高于计算本身有研究数据显示从DRAM读取一次数据的能耗是进行一次乘加运算的200倍以上。所以减少数据搬运是设计的第一原则。具体做法包括权重常驻对于推理场景权重是固定的可以一次性加载到片上SRAM后续计算不再访问DRAM。这要求SRAM容量足够大或者通过分时复用支持多模型切换。激活值压缩激活值通常有稀疏性可以通过稀疏编码压缩存储和传输。但稀疏编码的解码逻辑会增加硬件复杂度需要权衡。数据预取通过DMA引擎提前把下一层需要的数据搬到片上缓存隐藏DRAM访问延迟。预取策略需要编译器在生成代码时插入预取指令。注意片上SRAM的bank划分要避免bank conflict。如果多个计算单元同时访问同一个bank的不同地址会产生冲突降低有效带宽。常见的做法是地址交错address interleaving把连续地址分散到不同bank。3. 实操过程与核心环节实现3.1 从模型到硬件的完整映射流程这一节我把一个典型的模型部署流程拆开讲从ONNX模型到硬件执行中间经过哪些环节每个环节的关键操作是什么。第一步模型导入与图优化。把训练框架导出的模型通常是ONNX格式加载进来做常量折叠、死代码消除、算子融合等图级别优化。这一步不涉及硬件特性纯粹是计算图简化。我通常会用Netron可视化一下优化前后的图结构确认没有误删关键节点。第二步量化校准。用一批校准数据跑一遍浮点模型统计各层激活值的动态范围计算量化参数scale和zero_point。校准数据的分布要尽量覆盖实际推理场景否则量化误差会偏大。我一般建议校准集不少于500个样本且要包含边界case。第三步算子映射与调度。把优化后的计算图拆解为硬件支持的基础算子然后为每个算子选择调度策略数据流模式、分块参数、并行度。这一步是编译器后端的核心工作通常需要结合硬件代价模型做搜索。第四步内存分配与指令生成。为所有张量分配片上或片外地址生成DMA搬运指令和计算指令。内存分配要尽量减少碎片同时考虑bank conflict和缓存行对齐。第五步运行时调度与执行。runtime按照依赖关系调度各算子的执行管理同步和内存复用。多算子可以流水线执行计算和搬运重叠。整个流程走下来一个中等规模的模型如ResNet-50从导入到生成可执行代码在工具链成熟的情况下大约需要几分钟到十几分钟。如果工具链不成熟可能需要人工介入调优时间会拉长到几天甚至几周。3.2 性能调优的实操记录与参数选择性能调优是软硬件协同设计中最耗时的环节。我记录了一个实际项目的调优过程目标是在某AI加速卡上把BERT-base的推理延迟降到10ms以内。初始版本跑下来延迟是23ms远未达标。用性能分析工具抓取各层耗时发现矩阵乘占了60%的时间但计算利用率只有35%。进一步分析发现矩阵乘的K维度隐藏层维度768被分成了4块每块192导致累加器频繁溢出到本地内存。调整策略把K维度分块改为2块每块384同时增大片上缓存中权重分块的大小。调整后计算利用率提升到58%延迟降到15ms。继续分析发现LayerNorm和Softmax的延迟占比从初始的15%上升到25%因为矩阵乘变快了这些访存密集的算子成了新瓶颈。优化方案是把LayerNorm和Softmax融合到前一个矩阵乘的epilogue中减少中间结果的写回和读取。融合后延迟降到11ms。最后一步检查DMA搬运是否与计算重叠。发现权重加载没有预取导致每层开始时有等待。插入预取指令后延迟降到9.2ms达标。这个过程中用到的关键参数和调整记录如下表调优阶段关键参数初始值调整值延迟变化初始版本K维度分块4-23ms矩阵乘优化K维度分块4215ms算子融合融合策略无ConvLNSoftmax11ms预取优化预取深度029.2ms实操心得性能调优要遵循先大后小的原则。先优化占比最大的算子再处理长尾。每次只改一个参数观察效果避免多变量同时变化导致无法归因。另外性能分析工具一定要在项目初期就准备好不要等到调优阶段才发现没有数据支撑。3.3 软硬件联合验证的方法与工具软硬件协同设计离不开联合验证。硬件流片前软件团队需要在仿真器或FPGA原型上验证功能正确性和性能。我通常采用三层验证策略单元级验证针对每个硬件模块如MAC阵列、DMA引擎、向量单元用定向测试用例验证功能。这一层在RTL仿真阶段完成。算子级验证在FPGA原型上跑单个算子对比软件参考实现的输出确保数值精度在可接受范围内。量化算子要特别关注溢出和舍入误差。模型级验证在FPGA原型或仿真器上跑完整模型对比端到端精度和延迟。这一层最接近真实场景但速度慢通常只跑少量样本。验证过程中最常见的问题是数值精度不一致。硬件定点运算和软件浮点运算的结果必然有差异关键是要控制差异在模型可接受的范围内。我的做法是定义一个精度容忍度指标比如Top-1准确率下降不超过0.5%然后逐层分析误差来源定位是量化误差、累加溢出还是舍入模式的问题。4. 常见问题与排查技巧实录4.1 性能不达标的典型原因与排查路径性能问题是AI芯片落地中最常见的拦路虎。我整理了一个排查清单按优先级排序问题现象可能原因排查方法解决思路计算利用率低数据供给不足检查片上缓存带宽和DMA吞吐增大预取深度优化分块延迟波动大任务调度不均抓取各算子耗时分布调整调度策略负载均衡精度下降明显量化误差累积逐层对比浮点和定点输出调整量化参数混合精度功耗超标数据搬运过多统计DRAM访问次数增大片上缓存算子融合编译时间过长搜索空间过大分析编译器各阶段耗时剪枝搜索空间缓存中间结果这个表格是我在实际项目中反复使用的工具。每次遇到问题先对照表格定位可能原因然后按排查方法收集数据最后选择解决思路。大部分性能问题都能通过这个流程找到根因。4.2 编译器后端开发的避坑经验编译器后端是软硬件协同设计中最复杂的软件模块开发周期长bug隐蔽。我踩过的坑包括坑一代价模型不准确导致调度次优。初期我们用静态代价模型估算算子执行时间但实际硬件有流水线冲突、缓存miss等动态因素估算误差经常超过30%。后来引入在线校准机制在FPGA上跑一批基准算子用实测数据修正代价模型参数调度质量明显提升。坑二寄存器分配不考虑硬件约束。硬件寄存器文件有读写端口限制如果编译器分配的寄存器访问模式超出端口数会产生结构冲突。这个问题在RTL仿真阶段很难发现直到FPGA原型上才暴露。后来我们在寄存器分配阶段增加了端口约束检查提前规避。坑三算子融合过度导致指令缓存溢出。融合后的kernel指令数过多超出指令缓存容量导致频繁指令miss。解决方法是设置融合上限超过阈值就拆分成多个kernel。注意编译器后端的开发一定要有回归测试集。每次修改后跑一遍全量测试确保没有引入新bug。测试集要覆盖各种算子组合、边界shape、量化配置否则很容易在客户场景下翻车。4.3 软硬件团队协作的沟通要点最后聊一个非技术但极其重要的问题软硬件团队的协作。我见过太多项目因为沟通不畅导致返工。硬件团队和软件团队的关注点天然不同。硬件团队关心面积、功耗、时序软件团队关心灵活性、开发效率、可调试性。这两个目标经常冲突。比如硬件团队想砍掉一些不常用的算子来省面积软件团队却认为这些算子在未来模型中会用到。我的经验是建立联合评审机制在关键节点架构定义、指令集定稿、流片前组织双方一起评审用数据说话。软件团队提供算子覆盖率统计和模型演进预测硬件团队提供面积功耗预算双方共同决策取舍。另外文档和接口定义要先行。软硬件接口一旦定稿修改成本极高。我建议在项目初期就写好接口规范文档包括指令编码、寄存器映射、内存布局、同步机制等双方签字确认后再开始各自开发。还有一个实用技巧软件团队尽早介入硬件验证。不要等硬件流片回来才开始适配软件而是在FPGA原型阶段就让软件团队参与验证提前发现接口不匹配、性能不达标等问题。越早发现修复成本越低。这个系列写到第五篇越来越觉得AI芯片的软硬件协同设计是一门平衡的艺术。没有完美的方案只有在约束条件下找到的最优解。每次项目结束复盘总能发现一些当时可以做得更好的地方这些经验积累下来就是下一次项目的底气。

相关新闻

JetBrains官宣全新AI IDE:从插件到整库理解,重构AI编程工作流

JetBrains官宣全新AI IDE:从插件到整库理解,重构AI编程工作流

最近圈子里的消息传得很快:Jetbrains 正式官宣了一款全新的 AI IDE。说实话,刚看到这个消息的时候,我第一反应不是"又来了一个新工具",而是"Jetbrains 终于不再只是往 IDE 里塞 AI 插件了"。过去两三年&#…

2026/10/14 1:02:03 阅读更多 →
PaperSpine5 期刊学习指南:从目标期刊文献到可落地的写作与排版迁移

PaperSpine5 期刊学习指南:从目标期刊文献到可落地的写作与排版迁移

AI 技能AI 写作人工智能深度研究AI 应用 【免费下载链接】PaperSpine PaperSpine5 — local-first, evidence-bound paper research, writing, figures, review and delivery. Download: https://wubing2023.github.io/PaperSpine/v5/ 项目地址: https://gitcode.co…

2026/10/14 1:02:03 阅读更多 →
双塔模型query侧个性化融合:原理、路径与工程实践

双塔模型query侧个性化融合:原理、路径与工程实践

1. 为什么双塔模型的query侧“长不大”:从结构缺陷看个性化融合的必然性在推荐系统工程实践中,双塔模型(Dual-Tower Model)早已不是新鲜概念。它把用户行为序列和候选物品分别编码进两个独立子网络,最后用内积或余弦相…

2026/10/14 1:01:03 阅读更多 →

最新新闻

组合导航中的杆臂效应与外参标定:从原理到工程实践

组合导航中的杆臂效应与外参标定:从原理到工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 1:43:13 阅读更多 →
单相逆变器下垂控制原理与工程实践

单相逆变器下垂控制原理与工程实践

1. 项目概述:下垂控制在单相逆变器并机中的核心价值在分布式发电系统中,多台逆变器并联运行是提升系统容量和可靠性的关键技术。传统控制方法在并联运行时往往面临环流问题、功率分配不均等挑战。下垂控制通过模拟同步发电机的调频特性,实现了…

2026/10/14 1:43:13 阅读更多 →
医美CRM客户生命周期管理:从咨询到复购的数据主干网

医美CRM客户生命周期管理:从咨询到复购的数据主干网

简介:宏脉医美系统使用手册是一份面向医疗美容机构运营管理人员、前台接待、咨询顾问及信息化实施人员的标准化操作指南,聚焦客户数据规范化采集、新老客户精准识别与全流程业务协同管理,切实解决医美行业客户信息分散、填写不全、跨部门衔接…

2026/10/14 1:43:13 阅读更多 →
串口通信稳定性实践:分帧、超时与断线重连机制详解

串口通信稳定性实践:分帧、超时与断线重连机制详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 1:43:13 阅读更多 →
支腿反力与静态稳定性:吊装安全计算模块全解析

支腿反力与静态稳定性:吊装安全计算模块全解析

干吊装这行越久,越明白一个道理:绝大多数事故都不是“胆大胡来”造成的,而是“以为没问题”造成的。尤其是支腿反力和整车静态稳定性这两件事,现场最容易漏算,也最容易在临界工况上出问题。支腿反力大一点、重心偏一点…

2026/10/14 1:43:13 阅读更多 →
北航机试工程思维:输入解析、缓存优化与格式鲁棒性

北航机试工程思维:输入解析、缓存优化与格式鲁棒性

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 1:42:11 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →