news 2026/9/18 18:08:27

CANN pto-isa A2/A3 MoE Combine Kernel:基于 PTO TPUT 的变长 Return 与 Gate 加权还原实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN pto-isa A2/A3 MoE Combine Kernel:基于 PTO TPUT 的变长 Return 与 Gate 加权还原实现

CANN pto-isa A2/A3 MoE Combine Kernel:基于 PTO TPUT 的变长 Return 与 Gate 加权还原实现

【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa

本文以 pto-isa 仓库中 moe_combine 算子文档 为主体,结合 kernel 源码、布局计算 与 运行脚本 逐层剖析这一 AIV-only 通信算子的完整契约:读懂它之后,你能掌握 PTO 通信指令(TPUT/TNOTIFY/TWAIT)如何在 HCCL peer window 上实现变长 all-to-all-like 的 MoE combine return,并用显式路由账本完成 token 级加权还原。

一、算子定位:dispatch-compute-combine 流水的 return 半段

本算子运行在 Ascend A2/A3 系列芯片(已在 Atlas 910B1 验证),是 MoE 推理流水 dispatch-compute-combine 三段中的最后一段 combine。本地 expert 完成计算后,combine kernel 承担两个职责:

  1. Return:把expertOutput中的 expert 输出行,按上游整理好的路由账本routeMeta返还给 token 所在 rank;
  2. Restore:用 gate 权重probstopK路 expert 输出加权求和,还原出每个 token 的最终输出outputC

文档给出的数据流概览:

expertOutput[local expert rows, K] -> 通过 HCCL peerWindow.ptrD 做变长 return -> 通过 TNOTIFY/TWAIT 做跨 rank 完成同步 -> 加权还原: outputC[token, :] = sum(topK probs * returned rows)

需要注意的是,当前 kernel 是一个独立 combine kernel,入口消费的是显式低层路由账本routeMetaexpert_idsassist_info_for_combineep_send_counts等路由信息由上游或 host 侧按本算子的账本布局整理后显式写入routeMeta传入,kernel 本身不做路由决策。

目录结构与文件分工

kernels/manual/a2a3/moe_combine/ ├── CMakeLists.txt # Bisheng CCE + host 构建配置 ├── run.sh # 一键构建和运行脚本,发现 MPI,估算 HCCL_BUFFSIZE ├── common.h # 共享 ABI: shape, routeMeta layout, peerWindow layout, HCCL context ├── layout.h # Host 侧 layout 计算和 HCCL_BUFFSIZE 估算 ├── kernel_launchers.h # Host 侧 kernel launcher 声明 ├── moe_combine_kernel.cpp # PTO AIV kernel: return + wait + weighted restore ├── main.cpp # Host 编排: MPI, ACL, HCCL window, fixture, verify, profile ├── golden.h / golden.cpp # CPU golden 数据结构和路由构造、输出校验 ├── hccl_context.h # HCCL window 初始化和 peer-window 地址交换 ├── comm_mpi.h # MPI 动态加载封装 ├── README.md # 英文 README └── README_zh.md # 中文 README(本文主体)

从源码结构看,common.h 中的MoeCombineShapeWorkspaceLayoutCombineRouteMetaLayoutPeerWindowLayout四组 struct 是 host 与 device 共享的 ABI:host 侧 layout.h 用它们计算字节布局,device 侧 moe_combine_kernel.cpp 中的MakeWorkspaceLayout/MakeCombineRouteMetaLayout/MakePeerWindowLayout用同一套规则在 kernel 内部重算偏移,两侧字段名严格对齐,保证任何一边改动都会立刻暴露 ABI 不一致。

二、算子语义:从 routeMeta 到加权还原

2.1 输入与计算流程

对每个 rank,算子消费已经按本地 expert 和来源 rank 排布好的 expert 输出。kernel 内部四步:

  1. 读取routeMeta,得到每个 source rank 给每个 expert 的行数,以及这些行在expertOutput中的位置;
  2. 使用 PTOTPUT将每行 expert 输出返还到 token owner rank 的 HCCL peer window;
  3. 使用TNOTIFY/TWAIT等待所有 peer 完成 return 写入;
  4. 读取routeMeta.expandedRowIdxprobs,还原outputC[M, K]

2.2 加权还原公式

对本 rank 的第t个 token,dispatch 阶段会产生topK条 expert route。combine return 完成后,这些 route 对应的 expert 输出行已写回本 rank 的peerWindow.ptrD。其中expandedRowIdx[t * topK + slot]记录第slot条 route 在ptrD中的行号,probs[t * topK + slot]是该 route 的 gate 权重。对输出每一列c

outputC[t, c] = 0 for slot in 0..topK-1: row = expandedRowIdx[t * topK + slot] if row >= 0: outputC[t, c] += probs[t * topK + slot] * peerWindow.ptrD[row, c]

即把同一个 token 的topK路 expert 输出按 gate 权重加权求和,得到outputC[t, :]row < 0表示该 route 无效(例如未命中该 rank 的 expert),直接跳过。这一语义在 kernel 源码 的LoadRestoreRoute中以*ptrDRow < 0分支精确实现,host 侧 CPU golden 在 golden.cpp 中用同一公式独立计算,供逐元素比对。

2.3 覆盖范围

包含不包含
EP 域内基于 HCCL window 的 combine returnDispatch pack/gather kernel
使用TPUT实现变长 all-to-all-like returnHCCL collectiveAllToAllVAPI
使用probs做加权还原Expert FFN/GMM 计算
显式低层routeMeta契约量化、TP ReduceScatterV、shared/copy/const expert
A2/A3 HCCL peer-window 路径上层公共 ABI 适配层

三、入口契约:Kernel Launcher ABI 与缓冲区布局

3.1 Launcher ABI

void LaunchMoeCombineKernel(MoeCombineShape shape, uint32_t myRank, uint8_t *expertOutput, uint8_t *probs, uint8_t *outputC, uint8_t *routeMeta, uint8_t *peerWindow, uint8_t *hcclCtx, uint8_t *workspace, void *stream, uint32_t launchBlockCount);

该函数在 moe_combine_kernel.cpp 末尾 以标准MoeCombineKernel<<<launchBlockCount, nullptr, stream>>>(" 三参数 launch 形式提交,launchBlockCount` 决定 kernel 使用的逻辑 AIV block 数。

3.2 运行时输入

参数方向存储含义
shape输入值传递静态 shape 和 AIV block 数,如ep,m,k,topK,expertPerRank,aivBlocks
myRank输入值传递EP 域内 rank id
expertOutput输入aclrtMallocGM本地 expert 输出行,形状[maxOutputSize, K],fp16
probs输入aclrtMallocGMgate 权重,形状[M, topK],fp32
outputC输出aclrtMallocGM还原后的 token 输出,形状[M, K],fp16
routeMeta输入aclrtMallocGM显式 combine 路由账本
peerWindow输入/输出HCCL RDMA window远端可见的ptrDreturn buffer 和 signal
hcclCtx输入aclrtMallocGM设备侧所有 rank 的 HCCL window 地址
workspace临时aclrtMallocGM本地 AIV soft sync 区
stream输入ACL streamkernel launch stream
launchBlockCount输入值传递kernel 使用的 AIV block 数

3.3MoeCombineShape字段

字段含义
epEP rank 数
m每 rank token 数
khidden size
topK每 token 的 expert 路由数
expertPerRank每 rank 本地 expert 数
expertNum全局 expert 数,通常为ep * expertPerRank
maxOutputSize每 rank expert 输出最大行容量
aivBlocks逻辑 AIV block 数;A3 默认24,可传参覆盖

aivBlocks为 0 时的降级行为在源码中有明确定义:layout.h 的 EffectiveAivBlocks 将其视为 1,而 run.sh 会把命令行传入的 0 替换为默认值 24,两条路径的默认值不同,分别服务于"host 布局计算"和"实际运行"两个场景。

3.4peerWindow内容

localWindowBase是 HCCL window 的起始地址。A2/A3 上传给 kernel 的peerWindow指向localWindowBase处的 live payload;本 layout 不额外保留 A5 的 4096B head guard。

A2/A3 localWindowBase peerWindow live payload: ptrD countReadySignal[ep] combineDoneSignal[ep]
字段位置内容
ptrDHCCL window live payloadreturn 目标行,被远端TPUT写入
countReadySignal[ep]HCCL window live payloadper-rank ready 计数区
combineDoneSignal[ep]HCCL window live payloadper-rank 完成计数器;远端 rank 完成写入本 rankptrDTNOTIFY对应槽位

3.5routeMeta布局

routeMeta是显式低层 combine 路由账本,属于本地 GM,不属于 HCCL window:

字段形状含义
peerTokenPerExpert[ep, expertNumPadded]int32每个 source rank 到每个 global expert 的行数
expandedRowIdx[M * topK]int32token route 到peerWindow.ptrD的行映射;-1表示无效 route
cumsumPerExpert[ep, expertNumPadded]int32每个 source rank 内按 global expert 的 inclusive prefix:cumsum[src,e] = sum(peerTokenPerExpert[src,0..e])
dispatchOffset[expertPerRank]int32每个本地 expert 在expertOutput中的基地址行
prevSumBeforeRank[ep, expertPerRank]int32某 source rank 在本地 expert 行段中的前缀偏移

expertNumPaddedexpertNum向上对齐到 metadata pad 粒度 16(kMoeCombineMetadataPad)后的值,用于让peerTokenPerExpert/cumsumPerExpert两行的行宽固定,方便设备侧以整行 stride 寻址。

3.6 布局计算与 HCCL_BUFFSIZE 估算

三份布局(workspace / routeMeta / peerWindow)的 host 实现集中在 layout.h。关键规则:

  • 64 字节字段对齐:每个字段追加前先把游标对齐到 64B(AppendField),与 device 侧AppendFieldDevice完全一致;
  • workspace:只含localSync软同步区,槽位数是aivBlocks * (8 + expertNumPadded),不足 64 槽时取 64;
  • peerWindowptrDM * topK * K * 2字节(fp16),后接两个[ep]int32 信号数组;
  • 溢出保护:所有乘法经CheckedMul检查,避免 shape 参数异常导致 uint64 溢出。

run.sh会先用同样的 bash 逻辑预估三类布局字节数,再按EstimateHcclBuffSizeMb的规则(layout.h L122-L128)估算 HCCL 缓冲:peerWindow 总字节 + 64 MiB 安全余量,向上对齐到 MiB 后导出HCCL_BUFFSIZE;用户也可用--hccl-buffsize-mb手动覆盖。

四、Kernel 细节:三阶段实现

Kernel 入口 MoeCombineKernel 的骨架非常清晰:

ReturnExpertRowsToOwners(...); // 阶段 1: return WaitCombinePhase(...); // 阶段 2: 跨 rank 等待 SoftSyncAiv(...); // 软同步:进入 restore 前全体 block 会合 RestoreOutputRows(...); // 阶段 3: 加权还原 SoftSyncAiv(...); // 软同步:收尾

其中SoftSyncAiv包装的是 PTO 软同步指令pto::SYNCALL<Soft>(源码 L251-L255),它只在同一个 kernel 的多个 AIV block 之间建立栅栏,代价远低于 kernel 级同步——这也是文档中"Soft AIV sync"优化项的落地方式。

4.1 阶段 1:ReturnExpertRowsToOwners

kernel 遍历所有本地 expert segment(共ep * expertPerRank个):

segment = src_rank * expertPerRank + localExpert globalExpert = myRank * expertPerRank + localExpert rows = routeMeta.peerTokenPerExpert[src_rank, globalExpert]

对每个非空 segment(LoadReturnSegment):

  1. srcStart = dispatchOffset[localExpert] + prevSumBeforeRank[src_rank, localExpert],即本 source rank 的行在本 expert 行段内的起点;
  2. dstStart = cumsumPerExpert[src_rank, globalExpert - 1]globalExpert == 0时为 0),即该行段在 owner rank 的ptrD中的目标行;
  3. 按固定 8 行 chunk 切分(kMoeCombineRowChunk),以chunkBase % blockNum轮转分配给各 AIV block,实现 L471-L483 的 load balance;
  4. src_rank == myRank时走本地路径CopyLocalRowsToPeerWindow:用TLOAD -> TSTORE把行经 UB tile 复制到本 rank 的peerWindow.ptrD(L371-L405);否则走远端路径PutRemoteRowsToOwner

远端路径的核心是一次 PTO 通信指令调用(L407-L427):

pto::comm::TPUT(remoteDst, localSrc, ping, pong);

remoteDst不是本 rank 的地址,而是先用 RemotePtr 把本地指针相对本 rank window base 的偏移,平移到hcclCtx->windowsIn[peerRank]上得到远端 window 内同布局的ptrD地址——这正是 HCCL window "同偏移寻址"契约的体现。ping/pong是分配在 UB 两个地址(0x0/0x1000)上的双缓冲 tile,让 MTE2 load 与 MTE3 远端 store 形成流水。该指令的语义可参考仓库通信 ISA 文档 TPUT。

所有 return 完成后,每个 AIV block 对分配给它的 source rank 集合执行通知(NotifyCombineOwners):

pto::comm::TNOTIFY(sig, 1, pto::comm::NotifyOp::AtomicAdd); // sig = 远端 combineDoneSignal[myRank]

语义可参考 TNOTIFY。

4.2 阶段 2:WaitCombinePhase

每个 rank 等待所有 peer 的写入完成(源码 L263-L273):

TNOTIFY(remotePeer.combineDoneSignal[myRank], AtomicAdd) // 阶段 1 尾部发出 TWAIT(localPeer.combineDoneSignal[peer] >= 1) // 本阶段等待

等待谓词是WaitCmp::GE、阈值kMoeCombineSignalValue = 1。Host 会在每轮迭代前清零combineDoneSignal(main.cpp 的 ClearDeviceState 中aclrtMemset整块 peerWindow),因此 kernel 固定等待每个 peer 恰好一次 notify。TWAIT的语义参见 TWAIT 文档。

4.3 阶段 3:RestoreOutputRows

每个 AIV block 负责一段连续 token(TokenShardBegin/End做余数感知的均匀切分)。对每个 token 的每个 1024 列 tile:

  1. TEXPANDS(outTile, 0.0)把输出 tile 清零;
  2. 对每个有效 route,TLOAD加载ptrD[expandedRowIdx]的一行片段;
  3. TAXPY(outTile, ptrTile, prob)完成y = A*x + y形式的累加;
  4. 全部 route 累加完毕后TSTORE把 fp16 tile 写回outputC

这一步有两条值得注意的实现细节:

  • Restore route cache + DCCI 批量 acquire(PrepareRestoreRouteReads):当topK <= 16时,token 的全部 route 行号与 prob 先缓存进标量数组;每条 route 的ptrD行用dcci逐 cache line 刷新后只做一次dsb(DSB_DDR),把内层 restore loop 中逐条 route 的 GM acquire 开销摊薄为每 token 一次。
  • TLOAD -> TAXPY event chain(AccumulateRestoreTile):利用 PTO 的 event 依赖机制,pto::Event<Op::TAXPY, Op::TLOAD>串联上一轮 AXPY 与下一轮 LOAD,pto::Event<Op::TLOAD, Op::TAXPY>串联本轮 LOAD 与 AXPY,形成 load/axpy 交替流水,避免显式 flag 等待打断指令发射。

五、优化手段汇总

该 kernel 是 AIV-only combine kernel。对于K=7168这类 hidden size,一行 fp16 数据是 14 KiB,整体主要受 GM/HCCL window 搬运带宽影响。优化目标是让数据搬运尽量流式化,同时降低控制面元数据开销:

优化说明源码佐证
显式 routeMeta路由元数据作为独立 GM buffer 传入;peerWindow只保留远端可见 return 数据和信号,workspace只保留本地 AIV soft sync 区common.h ABI
chunk 化 return 分片return 阶段遍历src_rank x local_expertsegment,按chunkBase % blockNum把 8 行 chunk 分给 AIV blockL470-L483
PTO TPUT ping/pong 路径TPUT(remoteDst, localSrc, ping, pong)通过 UB 双缓冲让 MTE2 load 与 MTE3 store 流水L422-L426
Restore route cachetopK <= 16时 route 行号与 prob 缓存到标量数组,减少内层 loop 对 route metadata 的重复读取L488-L531
DCCI 批量 acquire每 token 消费ptrD行前按 64B cache line 刷新对应 GM range,本轮 cached routes 只做一次dsb(DSB_DDR)L202-L228
TLOAD 到 TAXPY event chainrestore 阶段通过 PTO event 依赖完成加载与计算衔接L533-L558
Soft AIV sync同一 kernel 内用pto::SYNCALL<Soft>分隔 return、wait、restore 阶段L251-L255

六、Tiling 与默认参数

参数默认值说明
PES/ep2EP rank 数
M64每 rank token 数
K7168hidden size
topK8每 token expert 路由数
expertPerPe2每 rank 本地 expert 数
expertNum4PES * expertPerPe
maxOutputSizePES * M * topK默认容量;默认 shape 下为1024
aivBlocks24A3 默认逻辑 AIV block 数
内部 Vector tile 列宽1024示例实现固定值(kMoeCombineTileCols
内部 return chunk8 rows固定的 return 阶段行 chunk(kMoeCombineRowChunk
内部 metadata pad16expert metadata 对齐粒度(kMoeCombineMetadataPad

使用PES=2, M=64, K=7168, topK=8, expertPerPe=2, aivBlocks=24时,各布局大小为:

Layout字节数
workspace2304
routeMeta2432
peerWindow7340160

这三个数字可以由 layout.h 的公式 直接推导验证:例如peerWindow = 64*8*7168*2 + 2*2*4 + 对齐 = 7340032 + 信号区 ≈ 7340160(对齐到 64B)。run.sh启动时会打印workspace_bytes / route_meta_bytes / peer_window_bytes,与上表一致。

七、Host 侧整体架构

Host: ParseArgs -> ComputeWorkspaceLayout / ComputeCombineRouteMetaLayout / ComputePeerWindowLayout -> PrepareHostData and CPU golden -> Init HCCL peer window -> AllocateLocalBuffers(routeMeta/workspace/expertOutput/probs/outputC) -> loop(warmup + measured): ClearDeviceState PrepareCombineFixture -> 写入 routeMeta + expertOutput LaunchMoeCombineKernel Verify outputC Device: ReturnExpertRowsToOwners -> WaitCombinePhase -> RestoreOutputRows
Return phase: routeMeta(peerToken/cumsum/offset) + expertOutput -> local or remote peerWindow.ptrD -> TNOTIFY peer combineDoneSignal[myRank] Restore phase: routeMeta.expandedRowIdx + probs + peerWindow.ptrD -> outputC

从 main.cpp 的编排看,几个环节值得注意:

  • HCCL window 初始化:rank 0 通过 hccl_context.h 中的InitHcclRootInfo获取 root 信息,经 MPI broadcast 给所有 rank,再由InitHcclWindowContext完成 window 分配与 peer-window 地址交换,最终所有 rank 的 window base 填入设备侧HcclDeviceContext.windowsIn/windowsOut(结构定义见 common.h L58-L66),供 kernel 内RemotePtr寻址;
  • MPI 封装:rank 映射默认从mpirun环境获取(--rank-from-mpi 1),MPI 符号通过 comm_mpi.h 动态加载,run.sh会先检查mpirun是否存在;
  • 每轮迭代清理ClearDeviceStateaclrtMemset清零 workspace、routeMeta 与整个 peerWindow(含combineDoneSignal),保证 TWAIT 的"等待一次 notify"语义每轮都成立;
  • Profile 聚合:rank 0 用MpiGatherBytes收集各 rank 的prepare_fixture / combine_e2e / total_e2e计时,按迭代取跨 rank 最大值后打印,避免个别慢 rank 掩盖统计。

八、构建与运行

8.1 环境

source /usr/local/Ascend/cann-8.5.0/set_env.sh

执行run.sh前需要先在 shell 中加载 CANN 环境。如果 shell 中没有mpirun,请先配置 MPI 环境(脚本会直接报错退出)。run.sh还会把Ascend910B1simulator 库目录加入LD_LIBRARY_PATH,并按--keep-hccl-shm决定是否清理/dev/shm/sem.hccl*与共享内存段。

8.2 仅编译

cd kernels/manual/a2a3/moe_combine bash run.sh --skip-build 0 --clean-build 1

脚本内部流程是cmake -DRUN_MODE=npu -DSOC_VERSION=Ascend910B1 .. && make -j16,产物为build/moe_combine可执行文件,再以mpirun -n ${PES}拉起全部 rank。

8.3 快速验证

cd kernels/manual/a2a3/moe_combine bash run.sh -pes 2 -M 8 -K 64 -topK 2 -expertPerPe 1 --aiv-blocks 2

8.4 默认 shape

cd kernels/manual/a2a3/moe_combine bash run.sh -pes 2 -M 64 -K 7168 -topK 8 -expertPerPe 2 --aiv-blocks 24

8.5 主要命令行参数

参数默认值含义
-pes2rank 数
-M64每 rank token 数
-K7168hidden size
-topK8每 token route 数
-expertPerPe2每 rank expert 数
--max-output-sizePES * M * topKexpert output 行容量
--aiv-blocks0 -> 24逻辑 AIV block 数,用于匹配不同硬件资源规划
--device-base0rank 到 device 映射使用的起始 device id
--ndevicesPES示例 launcher 使用的可见 device 数

脚本还会强制两条约束(run.sh L133-L154):所有 shape 字段必须为正数,且maxOutputSize >= PES * M * topK(不支持容量不足导致的丢行)。此外run-mode第一版只支持npu--case预设不被接受,必须显式传 shape 参数。

8.6 实测性能与 profile 指标

工程可直接在 A2/A3 机器(Atlas 910B1)上运行,脚本输出如下 profile:

[PROFILE] CombineTile M=64 K=7168 ranks=2 topK=8 expertPerPe=2 warmup=3 measured=5 samples=5 prepare_fixture: avg=... us max=... us combine_e2e: avg=... us max=... us verify=PASS

关键指标含义:

指标含义
combine_e2ecombine kernel launch 到 stream sync;不包含 clear、fixture、verify,也不包含 kernel launch 窗口之外的 MPI barrier
verify=PASSdeviceoutputC与 CPU golden 一致

计时口径上,warmup 迭代被排除,每个 measured 样本取跨 rank 最大值(对应 main.cpp 的 PrintProfileSummary)。

九、验证方法

Host 会构造确定性的 CPU golden 路由账本(含无效 route 覆盖),将其写入routeMeta,拷贝expertOutput,启动 kernel,再将outputC与 CPU golden 输出逐元素对比(相对/绝对容差默认均为1e-2)。默认开启验证,预期成功输出:

verify=PASS

十、小结与延伸阅读

moe_combine 展示了 PTO 通信指令集在 A2/A3 上的一个完整落地范式:

  1. 数据面:以 HCCL window 同偏移寻址 +TPUT变长远端写入,绕开 collective 库,直接实现 all-to-all-like 的 return 数据流;
  2. 控制面routeMeta显式账本(peerToken / cumsum / offset 三类元数据)把 dispatch 阶段的路由决策与 combine 执行解耦;
  3. 同步面TNOTIFY(AtomicAdd) + TWAIT(GE 1)完成跨 rank 屏障,pto::SYNCALL<Soft>完成 rank 内 AIV block 之间的阶段分隔;
  4. 性能面:UB ping/pong 双缓冲、event chain、DCCI 批量 acquire 等技巧把控制面开销压到最低,让 14 KiB/行的大数据搬运尽量流式化。

延伸阅读:

  • 英文文档:kernels/manual/a2a3/moe_combine/README.md
  • 通信 ISA 指令参考:TPUT、TNOTIFY、TWAIT
  • 完整 kernel 实现:kernels/manual/a2a3/moe_combine/moe_combine_kernel.cpp
  • HCCL window 初始化:kernels/manual/a2a3/moe_combine/hccl_context.h

【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

CIP与OPC UA协议转换:PLC标签数据转发到寄存器全攻略

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

作者头像 李华
网站建设 2026/9/18 18:08:01

基于Spring Boot+SSM的网上书城系统开发实战全解析

最近刚把一套基于SSM架构的网上书城系统完整跑通&#xff0c;从数据库设计到前后端实现&#xff0c;再到部署上线&#xff0c;整个过程踩了不少坑&#xff0c;也沉淀了不少经验。这个项目最初的定位就是典型的Java Web课程设计/毕业设计课题&#xff0c;核心需求是图书展示、用…

作者头像 李华
网站建设 2026/9/18 18:04:32

LangChain 调 Ling-3.0-flash-Fin,TaoToken 接到 ChatOpenAI

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

作者头像 李华
网站建设 2026/9/18 18:03:19

MIND特征实现多模态医学图像配准的Python实战指南

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

作者头像 李华