数据竞争检测(下)——扫描线与四种战场
系列二 · 第 8 篇
上篇解决了"谁先谁后"(并发),这篇补齐另一半:“有没有踩到同一块内存”(重叠),再把两种判定合起来看个全景。
判断重叠,最朴素的想法是两两比较:把每个访问和所有其他访问都问一遍"你俩重不重、并不并发"。小算子里这能转,可一旦访问事件成千上万,两两配对就是天文数字。msSanitizer 用的是"扫描线算法"。
想象一条地址轴,从低地址扫到高地址。每个内存访问都拆成两个端点——开始的左端点和结束的右端点——按地址排好队。扫描线从左往右扫,扫到一个开始点,就把这个访问"挂"进当前活跃的集合;扫到一个结束点,就把它摘下来。这样,任何时刻还待在活跃集合里的访问,都是"地址上互相重叠"的。冲突检测于是从"全量两两比较"变成了"每个新事件只跟当前活跃的那一小撮比"——工作量陡然降了下来。
这里有个昇腾特有的细节得提一句:一条指令访问的内存往往不是连续的一段,而是"重复多次 + 分块跳跃"的二维形状,比如 DMA 一次搬一堆 tile。工具专门用一个叫 MemSeries 的结构把这些碎片合并成若干连续区间,扫描线才有"线"可扫。
有了并发判定和重叠判定,最后就看在什么"战场"上开战。竞争检测按范围分四层:
- 核内(流水间):同一个核里不同流水线互相抢,用向量时钟判顺序;
- 流水内:同一条流水里指令天然按序,不需要向量时钟,只看两条指令之间隔没隔同步点、离得够不够近;
- 核间:不同核抢同一块 GM,用向量时钟;
- 跨卡:不同 NPU 之间抢共享内存,把卡号也纳入时间戳,还要求两边跑的是同一个 kernel。
四层战场,算法骨架却始终是同一套扫描线,差异只在"用不用向量时钟、收哪些内存事件、处理哪些同步"。这份"一套引擎适配多种场景"的克制,比堆四套独立实现高明得多。
竞争检测就到这。下一篇,我们把镜头转向同步本身——SetFlag 和 WaitFlag 这对接头暗号,自己也会出错。
- 点赞
- 收藏
- 关注作者
评论(0)