NCCL 原地操作(In-place Operations)完全指南:从 API 语义到内核实现
高性能计算通信深度学习分布式训练【免费下载链接】ncclOptimized primitives for collective multi-GPU communication项目地址https://gitcode.com/gh_mirrors/nc/nccl点击查看免费下载导读本文聚焦 NCCL 中一个容易混淆但直接影响显存占用与性能的核心特性——In-place Operations原地操作。与 MPI 通过专门的MPI_IN_PLACE哨兵值标记原地语义不同NCCL 不定义特殊指针值而是根据调用者传入的sendbuff与recvbuff指针之间的实际地址关系自动识别并优化原地场景ncclBroadcast、ncclReduce、ncclAllReduce在sendbuff recvbuff时原地执行ncclReduceScatter、ncclAllGather则在 per-rank 指针恰好指向全局缓冲区对应偏移时原地执行。读完本文你将掌握六类集合通信 API 的原地判定条件、正确使用方法、缓冲区尺寸约束以及从 API 声明 到 Ring 算法内核 的底层实现原理从而在多卡训练与推理场景中安全地省去一份缓冲区拷贝。一、设计哲学为什么 NCCL 不采用 MPI 的MPI_IN_PLACE1.1 MPI 的做法哨兵值替换指针在 MPI 中集合通信的原地操作通过一个特殊的保留值MPI_IN_PLACE来表达调用者把本该传入的发送缓冲区指针替换成该哨兵值MPI 实现据此知道“就地使用接收缓冲区”。这种设计需要一条额外的判断路径并且要求调用者对 MPI 的原地语义非常熟悉否则极易误用。1.2 NCCL 的做法指针相等即原地NCCL 的官方设计原则见 docs/userguide/source/usage/inplace.rst截然不同Contrary to MPI, NCCL does not define a special in-place value to replace pointers. Instead, NCCL optimizes the case where the provided pointers are effectively in place.即 NCCL不定义任何特殊的 in-place 哨兵值而是直接优化“提供的指针实际上就是原地”的情形。判定完全由指针地址关系决定调用者不需要记忆任何魔法常量。这一设计在 API 头文件中有直接印证。打开 src/nccl.h.in 可以看到每个集合通信函数原型上方都明确注明了原地判定条件例如ncclReduce的注释为 In-place operation will happen ifsendbuff recvbuffncclAllGather的注释为 In-place operations will happen ifsendbuff recvbuff rank * sendcount。用户文档 docs/userguide/source/api/colls.rst 的每个小节也复述了同样的条件。原地语义是一等公民而不是 API 的“附加功能”。1.3 为什么选择指针相等而非哨兵值从工程角度看指针相等判定有几大好处零额外开销sendbuff recvbuff是一次平凡的指针比较不需要像MPI_IN_PLACE那样对每个指针参数做哨兵值匹配无歧义用户缓冲区中不可能出现与哨兵值冲突的地址不存在误判风险与 CUDA 生态自然衔接GPU 内存指针本身就是唯一的身份标识指针相等即意味着“同一块显存”语义清晰。二、两类原地模式直接相等 与 偏移定位根据集合通信的数据布局特征NCCL 的原地操作分为两种判定模式。2.1 模式一sendbuff recvbuffBroadcast / Reduce / AllReduce对于ncclBroadcast、ncclReduce和ncclAllReduce这三个操作输入输出都是“单块、全量”数据每个 rank 提供的sendbuff与recvbuff指向同尺寸的缓冲区。因此原地条件最简单——传入的sendbuff与recvbuff指向同一地址即可// 原地 AllReduce结果写回原缓冲区 ncclAllReduce(buff, buff, count, ncclFloat, ncclSum, comm, stream);此时最终结果会存储在初始数据被读取的同一位置调用者无需再准备一份输出缓冲区。注意ncclReduce的原地判定对所有 rank 都适用但只有 root rank 的recvbuff会被使用src/nccl.h.in非 root rank 上recvbuff可以为NULL原地条件自然只对 root 有意义。历史遗留 APIncclBcast头文件中还保留了一个已被标记 deprecated 的函数ncclBcast其注释明确写道 This operation is implicitely in place隐式原地。查看 src/collectives.cc 的实现可以看到它本质上就是原地调用的ncclBroadcastncclResult_t ncclBcast(void* buff, size_t count, ncclDataType_t datatype, int root, ncclComm_t comm, cudaStream_t stream) { return ncclBroadcast(buff, buff, count, datatype, root, comm, stream); }即ncclBcast(buff, ...)等价于ncclBroadcast(buff, buff, ...)用户文档 docs/userguide/source/api/colls.rst 也明确说明这一等价关系。它类似于 MPI 的MPI_Bcast调用习惯但 NCCL 官方建议新代码直接使用ncclBroadcast并显式传入相同指针以获得同样的原地语义。2.2 模式二per-rank 指针位于全局缓冲区的 rank 偏移处ReduceScatter / AllGatherncclReduceScatter与ncclAllGather是“分块”操作每个 rank 处理全局缓冲区中的一块数据。此时“原地”意味着每个 rank 的输入或输出指针恰好指向全局缓冲区中属于该 rank 的那一块而不是简单的前后指针相等。ncclReduceScatter的原地条件recvbuff sendbuff rank * recvcount即每个 rank 的接收指针指向发送缓冲区中自己那一块ncclReduceScatter(data, data rank * recvcount, recvcount, datatype, op, comm, stream);ncclAllGather的原地条件sendbuff recvbuff rank * sendcount即每个 rank 的发送指针指向接收缓冲区中自己那一块ncclAllGather(data rank * sendcount, data, sendcount, datatype, op, comm, stream);上面两段示例原样出自 docs/userguide/source/usage/inplace.rst是本文档给出的权威用法。其中rank是当前 rank 号recvcount/sendcount是每个 rank 的块大小元素个数不是字节数。2.3 缓冲区尺寸约束理解nranks * count由于原地模式下发送与接收共用同一块全局缓冲区必须保证缓冲区容量足够。NCCL 的尺寸约束见 docs/userguide/source/api/colls.rst 与 src/nccl.h.inncclReduceScattersendcount nranks * recvcount即sendbuff至少需要nranks * recvcount个元素的空间ncclAllGatherrecvcount nranks * sendcount即recvbuff至少需要nranks * sendcount个元素的空间与 docs/userguide/source/mpi.rst 中的说明一致AllGather/ReduceScatter 的 count 参数取“每 rank 的最小尺寸”另一侧自动放大为nranks * count。这一点和 MPI 允许 send/recv 不同类型不同长度的宽松规则不同NCCL 始终只使用单一 count 与单一数据类型。一个容易踩的坑原地模式下全局缓冲区虽然总容量为nranks * count但每个 rank 的发送/接收指针必须是data rank * count元素指针算术即块在缓冲区中的物理位置必须与该 rank 的编号严格对应。若某 rank 把自己的块放在错误偏移处指针关系不满足上述等式NCCL 将把它视为普通非原地操作——结果仍然正确但可能引入额外的内部拷贝。2.4 更多操作的原地规则Gather / Scatter虽然关联文档聚焦于上述五类操作但 src/nccl.h.in 与 docs/userguide/source/api/colls.rst 还给出了 Gather/Scatter 的对应规则一并列出以形成完整对照操作原地判定条件说明ncclBroadcastsendbuff recvbuff结果写回原缓冲ncclReducesendbuff recvbuff仅在 root rank 有意义ncclAllReducesendbuff recvbuff每个 rank 结果写回原缓冲ncclReduceScatterrecvbuff sendbuff rank * recvcount每 rank 输出落在自己那块ncclAllGathersendbuff recvbuff rank * sendcount每 rank 输入来自自己那块ncclGathersendbuff recvbuff root * count仅 root rank 判定ncclScatterrecvbuff sendbuff root * count仅 root rank 判定ncclAlltoAll不支持原地需用两个缓冲区特别提醒ncclAlltoAll目前在 NCCL 中不支持原地操作docs/userguide/source/api/colls.rst 明确写 In-place operation is currently not supported即使传入重叠的缓冲区也不会获得原地优化调用者必须准备独立的发送与接收缓冲区。三、实战示例如何正确使用原地操作3.1 原地 AllReduce单缓冲区参照 docs/examples/03_collectives/01_allreduce/c/README.md 的 AllReduce 框架非原地版本需要sendbuff[i]与recvbuff[i]两份缓冲float *buff; // 每 GPU 一份既是输入也是输出 size_t size; // 元素个数 ncclComm_t *comms; // 每 GPU 一个 communicator cudaStream_t *streams; ncclGroupStart(); for (int i 0; i num_gpus; i) { ncclAllReduce(buff[i], buff[i], size, ncclFloat, ncclSum, comms[i], streams[i]); } ncclGroupEnd();由于每个 rank 的sendbuff recvbuffNCCL 会将其识别为原地 AllReduce各 GPU 读取自己的buff参与归约最终所有 GPU 的buff都得到相同的结果。相比sendbuff/recvbuff分离的版本显存占用直接减半省掉一份size * sizeof(float)的输出缓冲且数据仍在原处无需额外拷贝。3.2 原地 ReduceScatter / AllGather全局块缓冲区对于nranks个 GPU每个 rank 的块大小为recvcountfloat *data; // 容量至少 nranks * recvcount 个元素 int rank; // 当前 rank 号 // 原地 ReduceScatter每 rank 的输出落在自己的块 ncclReduceScatter(data, data rank * recvcount, recvcount, ncclFloat, ncclSum, comm, stream); // 原地 AllGather每 rank 的输入来自自己的块 ncclAllGather(data rank * sendcount, data, sendcount, ncclFloat, comm, stream);两种操作配合起来正是“先 ReduceScatter 再 AllGather”的分解式 AllReduce这种组合在如 docs/examples/07_kernel_fusion/README.md 中用于在融合内核里实现逐层归约与广播。需要特别强调的是这里的data必须是同一块连续的全局缓冲rank 偏移rank * recvcount或rank * sendcount是块在全局缓冲中的逻辑位置这与 2.3 节的尺寸约束直接相关——若块位置错位等式不成立NCCL 会走非原地路径。3.3 通过ncclBcast兼容旧代码// 传统写法已标记 deprecated ncclBcast(buff, count, ncclFloat, root, comm, stream); // 等价于显式原地写法 ncclBroadcast(buff, buff, count, ncclFloat, root, comm, stream);两者行为完全一致见 src/collectives.cc 的转发实现新代码建议使用后者语义更显式。四、源码级原理解析NCCL 如何在底层处理原地操作4.1 宿主端指针直接透传不做显式标记以ncclBroadcast为例src/collectives.cc 的实现把所有参数包括sendbuff、recvbuff原样装入struct ncclInfo再调用ncclEnqueueCheck(info)。注意其中没有任何“是否原地”的标记位——原地与否完全靠后续内核在运行时对指针地址的比较来感知。这与“不定义哨兵值”的设计哲学一脉相承原地是一个由指针关系自然导出的性质而不是需要显式传递的状态。在 src/enqueue.ccdevWork.sendbuff与devWork.recvbuff被直接写入设备侧工作描述符同样不做归一化处理。4.2 设备端内核中按地址关系分派数据通路真正体现原地优化的是设备端内核。以 AllGather 的 Ring 算法为例src/device/all_gather.h 中内核在向下一 rank 推送数据时会做一次指针比较offset dataOffset rankDest * count; if ((inputBuf dataOffset outputBuf offset) || isNetOffload) { // In place or onePPN prims.directSend(dataOffset, offset, nelem); } else { prims.directCopySend(dataOffset, offset, nelem); }当inputBuf dataOffset outputBuf offset即发送源地址恰好就是接收目标地址时判定为原地或单进程单卡等可直接发送的场景走directSend直接发送数据跳过拷贝否则走directCopySend先拷贝到目标位置再发送。ReduceScatter 的 Ring 内核src/device/reduce_scatter.h同样按offset dataOffset rankDest * count组织各 rank 的块位置并在最后一步通过recvReduceCopy(offset, dataOffset, nelem, /*postOp*/true)把归约结果落到对应 rank 自己的块上——原地布局下这个落点正是全局缓冲中该 rank 的专属位置。从内核结构可以看出原地模式并非“少做一次计算”而是让数据直接落在最终位置、省去中间拷贝这对带宽敏感的大型集合通信尤为关键同时它也让调用者少维护一份输出缓冲区降低显存峰值。4.3 边界情况与注意事项从源码结构推断缓冲区注册与重叠NCCL 的 Buffer Registration 机制对原地场景有专门考量例如 src/register/coll_reg.cc 注释提到 Disable buffer registration for TREE in-place and for cross-clique due to buffer conflicts说明在 TREE 算法 原地、以及跨 clique 场景下缓冲区注册会被禁用以避免地址冲突。如果你的程序启用了用户缓冲区注册CUDA Graphs 或 NVLS 场景原地操作与注册之间的交互需要按实际拓扑验证。Send/Recv 点对点ncclSend/ncclRecv不涉及原地概念src/enqueue.cc 有 Skip send to self in-place 的注释指的是一对一自环发送的跳过逻辑与本文的集合通信原地语义不是一回事。Gather/Scatter 的 root 特例Gather/Scatter 的原地条件只在 root rank 上判定sendbuff recvbuff root * count或recvbuff sendbuff root * count非 root rank 的对应缓冲区不使用。Device API 的原地约束NCCL 设备侧 API如 ReduceCopy 系列对重叠有更严格的约定——docs/userguide/source/api/device_reducecopy.rst 明确要求源与目标区域不得重叠except when exactly in-place: same buffer and same offset即只有“完全同缓冲同偏移”才算合法的原地部分重叠属于未定义行为。这再次印证了 NCCL 对“原地”的定义是精确的指针/偏移相等而非“大致重叠即可”。五、与 MPI 的对照总结附迁移建议结合 docs/userguide/source/mpi.rst 的说明将原地语义对照整理如下维度MPINCCL原地标记方式MPI_IN_PLACE哨兵值替换指针无哨兵值指针地址相等即原地BroadcastMPI_Bcast(MPI_IN_PLACE, ...)ncclBroadcast(buff, buff, ...)或ncclBcast(buff, ...)AllReduceMPI_Allreduce(MPI_IN_PLACE, ...)ncclAllReduce(buff, buff, ...)AllGatherMPI_Allgather(MPI_IN_PLACE, ...)ncclAllGather(data rank*sendcount, data, ...)ReduceScatter对应MPI_Reduce_scatter_blockncclReduceScatter(data, data rank*recvcount, ...)迁移建议从 MPI 迁移到 NCCL 时不要把MPI_IN_PLACE照搬成某个指针值——直接让sendbuff与recvbuff指向同一地址或按偏移指向全局缓冲中的自身块即可原地 AllGather/ReduceScatter 务必保证块偏移rank * count与 rank 编号严格对应且全局缓冲容量至少为nranks * count个元素需要同时做“归约 广播”时优先考虑用原地 AllReduce 一次完成而不是显式拆分 ReduceScatter AllGather 两份缓冲若涉及ncclAlltoAll记住它目前不支持原地必须准备独立双缓冲。六、速查原地操作一句话规则Broadcast / Reduce / AllReducesendbuff recvbuff结果写回原缓冲最省显存的用法是单缓冲全程复用ReduceScatterrecvbuff sendbuff rank * recvcountsendbuff容量 ≥nranks * recvcountAllGathersendbuff recvbuff rank * sendcountrecvbuff容量 ≥nranks * sendcountGather / Scatter仅 root rank 上按root * count偏移判定AlltoAll不支持原地判定完全由指针地址关系决定无需任何特殊标记值未命中原地条件时操作仍正确只是可能引入额外拷贝。延伸阅读完整的集合通信函数签名与原地语义见 docs/userguide/source/api/colls.rstAPI 头文件注释见 src/nccl.h.inAllReduce 入门示例见 docs/examples/03_collectives/01_allreduce/c/README.mdNCCL 与 MPI 的整体差异对照见 docs/userguide/source/mpi.rst。赞分享高性能计算通信深度学习分布式训练【免费下载链接】ncclOptimized primitives for collective multi-GPU communication项目地址https://gitcode.com/gh_mirrors/nc/nccl点击查看免费下载相关推荐Linux 内核原子位操作Atomic Bitops完全指南API、语义与内存排序Linux 内核原子位操作Atomic Bitops完全指南API、语义与内存排序 导读 Linux 内核的 bitmap_ 系列函数是非原子的但在内核操作系统内核驱动驱动开发虚拟化嵌入式网络存储RxJS v4 Rx.Observable.range 操作符完全指南从参数语义到调度器实现原理RxJS v4 Rx.Observable.range 操作符完全指南从参数语义到调度器实现原理 本文是 RxJS v4Reactive Extension后端NCCL 集体通信CollectiveAPI 全面解析八大集合操作的签名、语义与实现原理NCCL 集体通信CollectiveAPI 全面解析八大集合操作的签名、语义与实现原理 本指南以 NCCL 用户指南的 Collective Commu高性能计算通信深度学习分布式训练上一篇3个步骤轻松掌握Windows和Office智能激活技巧下一篇Slang 整数类型规约一致性测试指南溢出环绕语义与类型别名的验证创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

KNN算法实战:从手写分类器到约会网站用户兴趣预测

KNN算法实战:从手写分类器到约会网站用户兴趣预测

刚接触机器学习时,很多人第一个研究的分类算法就是 KNN(K 最近邻)。原因很简单:它不依赖复杂的数理公式,原理一句话就能讲清楚,代码几十行就能跑通。但真正把它放到真实业务场景里,尤其是像约会…

2026/10/14 2:58:30 阅读更多 →
仓颉 stdx.crypto.keys 密钥包全解析:RSA / ECDSA / SM2 密钥生成、签名验签与 PEM / DER 编解码实战

仓颉 stdx.crypto.keys 密钥包全解析:RSA / ECDSA / SM2 密钥生成、签名验签与 PEM / DER 编解码实战

标准库网络密码学并发编程 【免费下载链接】cangjie_stdx 仓颉编程语言提供了 stdx 模块,该模块提供了网络、安全等领域的通用能力。 项目地址: https://gitcode.com/Cangjie/cangjie_stdx 点击查看 免费下载 stdx.crypto.keys 是仓颉标准库扩展&#x…

2026/10/14 2:58:30 阅读更多 →
FTP文件的上传

FTP文件的上传

1.获取FTP主机地址、端口、账号、密码 2. 打开FTP客户端,填入信息,点击连接 3.在本地电脑中,找到你网页文件打开目标存放文件夹 4.选中本地文件,点击到右侧远程目录,开始上传 5.底部查看传输进度 6.上传结束&#xff…

2026/10/14 2:58:30 阅读更多 →

最新新闻

一张图微调YOLOv11:自然栖息地鸟类检测的流程与扩充

一张图微调YOLOv11:自然栖息地鸟类检测的流程与扩充

简介:面向 YOLOv11 模型验证需求,这份精简版鸟类目标检测数据集包含一张真实野外鸟类图像及配套标准标注,可用于快速原型验证、轻量级部署测试与小样本检测基线建立。压缩包共五个文件,包含三张 JPG 图像、一个数据配置文件与一个…

2026/10/14 3:39:38 阅读更多 →
ASP.NET + SQLServer葡萄酒商城源码:从数据库还原到下单事务全攻略

ASP.NET + SQLServer葡萄酒商城源码:从数据库还原到下单事务全攻略

简介:一套基于ASP.NET与SQL Server的葡萄酒网上商城销售系统,面向ASP.NET WebForm学习者、毕业设计学生以及需要搭建在线商城教学演示的开发者。系统围绕普通会员、商户会员和管理员三类角色展开,覆盖商品管理、购物车、订单处理、发货、商户…

2026/10/14 3:39:38 阅读更多 →
LangChain项目代码拆解:Chain、Agent、Memory与Retrieval四大核心组件实战指南

LangChain项目代码拆解:Chain、Agent、Memory与Retrieval四大核心组件实战指南

简介:这是一份LangChain入门与实践的项目代码包,面向正在学习大模型应用开发的Python开发者,旨在帮助读者快速理解LangChain的模块化设计与核心用法。包内共3个文件,包含一个HTML格式的入门讲解页、一个项目运行配置文件以及一个版…

2026/10/14 3:39:38 阅读更多 →
C++学习日记 Day21:C++11(下)lambda表达式和function包装器

C++学习日记 Day21:C++11(下)lambda表达式和function包装器

## 今天学了什么今天学习C11的lambda表达式和function包装器。## 可调用对象概念:在C中有一类对象可像函数一样调用;分类:①函数指针using prt void(*)(int, string); prt f1 fun; f1(1, "函数指针");②仿函数 --有 operator&am…

2026/10/14 3:39:38 阅读更多 →
一人公司AI工作流:6步实现内容生产去体力化

一人公司AI工作流:6步实现内容生产去体力化

1. 一人公司的内容困局与破局思路一个人做内容,最怕的不是没灵感,而是灵感来了却卡在“执行”上。写一篇长文要三小时,做一张配图要半小时,剪一条短视频要一整个下午,排版发布又要来回切换四五个平台。一天下来&#x…

2026/10/14 3:39:38 阅读更多 →
8765平台前后摄DVDD电压不一致排查与修复指南

8765平台前后摄DVDD电压不一致排查与修复指南

/* 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 3:38:38 阅读更多 →

日新闻

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/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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 阅读更多 →