Skip to content

test(tlsu): 端到端 memory op 压测用例(S/C/R 三组) - #59

Open
liujiang833 wants to merge 10 commits into
mainfrom
feat/tlsu-e2e-cases
Open

liujiang833 wants to merge 10 commits into
mainfrom
feat/tlsu-e2e-cases

Conversation

@liujiang833

Copy link
Copy Markdown
Collaborator

内容

TLSU 端到端 memory op 用例,判据是架构内存 dump,gfrun 为黄金参考,gfsim 新旧两条 TLSU 路径三方比对。驱动脚本在 SuperScalarModel 侧(scripts/run_tlsu_suite.sh)。

三组共 9 个用例,全部新增于 microbenchmark/tlsu/

  • S 组(数据通路)copy_i32_8x128s1_copy_i32_32x32(多行挤一条 cacheline)、s2_strided_i32_8x128(512 元素行距)、s2b_stride_elem_i32_8x128(160 元素行距,element/byte 判别)、s3_two_dst_i32(双目的区)
  • C 组(定序)c1_store_load_i32(RAW)、c2_load_store_i32(WAR)、c3_load_store_load_i32
  • R 组(深序列)r1_random_seq_i32,200 块随机 TLOAD/TSTORE,由 gen_random_case.py 按固定种子生成并提交

配套:

  • tlsu_bench.hpp —— 公共骨架,三条硬约束(数据烘进 ELF、收尾排空窗口、禁用退化形状)+ 自识别编码 0xT_rrr_cccc(每个元素自带来源标记与行列号,dump 里直接可读)
  • viz_random_case.py —— R 组用例的可视化 + 独立判据--check):期望值由源码与自识别编码闭式推出,不依赖任何模型。已实测与 gfrun 的 65536 字节 dump 逐字节一致

背景与结果

这些用例驱动了 SuperScalarModel 侧的一轮修复(见那边的 PR):

  • 定位并修复了 tile store → tile load 的 RAW 定序缺口
  • 定位了一个 ADDTPC 解码回归(两个模型共用解码器一起错,比对在全零缓冲上假通过 —— 这正是独立判据存在的原因)
  • GM 行距统一按逻辑元素解释(配套 Linx-TileOP-API@f35d3aa)

注意

用例需要 SuperScalarModel 侧的 run_tlsu_suite.sh 来驱动,且需要 linx 工具链(COMPILER_DIR)编译。单独拉这个 PR 不能直接跑。

🤖 Generated with Claude Code

liujiang833 and others added 5 commits August 13, 2026 18:33
The minimal testable unit for TLOAD is copy = TLOAD + TSTORE: TLOAD's
destination is a tile register, which never appears in a memory dump, so
with architectural memory as the criterion TLOAD alone produces nothing
observable. Case names use `copy` rather than `tload` to avoid implying
otherwise.

copy_128b moves 4x8 fp32 = 128 B, exactly one BEAT_SIZE of the Streaming
L2 backend, aligned and not crossing a cacheline.

The harness pieces this needed:

- cross_model_result lives in .bss. The ELF loader creates a bank for
  every SHF_ALLOC section, and only SHF_EXECINSTR sections are write
  protected, so .bss is both mapped and writable.
- cross_model_result_size must be an absolute symbol: the dump tooling
  reads the symbol's value, not its contents, which C cannot express.
  TLSU_STR needs two-level expansion -- stringifying the macro parameter
  directly emits `.set cross_model_result_size, RESULT_SIZE`, which
  silently leaves the symbol out of the table with no diagnostic.
- The finisher is a 32-bit store of exactly 0x5555 to 0x10009000. gfsim
  takes the low 16 bits, gfrun requires width==4 and the full value to
  match, so only that form satisfies both.
- A drain window before the finisher. The finisher terminates the
  simulation as soon as it retires, and writes not yet globally visible
  disappear from the dump: a scalar store placed immediately before it
  vanishes, while the same store moved ahead of the tile ops survives.

Do not use a degenerate M=1 shape -- the compiler emits a TLOAD whose
dstTile is empty and both models abort on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
以架构内存为唯一判据、gfrun 为黄金参考的 TLSU 端到端用例。

tlsu_bench.hpp 收拢三条硬约束(源数据烘进 ELF、收尾留排空窗口、不用 M=1
的退化形状)和自识别编码 0xT_rrr_cccc —— 每个元素编码来源图样与自己的行列
号,于是错位、行距、beat 次序、RAW/WAR 违例在 hex dump 里各有各的签名,且
全零恒等于"从未被写"。源对象按物理容量分配、有效区之外填 0xF,让过读可见
而不是未定义行为。

S 组(数据通路):
  s1_copy_i32_32x32     行宽 128 B,多行挤一条 cacheline
  s2_strided_i32_8x128  从 8x512 抠 8x128,行距 2048 B,真正用上 B.IOR src1
  s3_two_dst_i32        两条独立 copy 写两个目的区(C 组失败后的归因对照)

C 组(定序):
  c1_store_load_i32       RAW
  c2_load_store_i32       WAR
  c3_load_store_load_i32  load -> store -> load
  c1_store_gap_load_i32   c1 插延时的归因变体,-DTLSU_GAP 可扫

C 组的 witness 区在序列开始前先预填图样 D:没有它,"那条 store 没落地"和
"落地了但搬的是零"在 dump 里都是全零,两个不同的故障分不开。

结果缓冲补上 alignas(256),此前靠链接器碰巧对齐。

S 组全过,C 组全挂 —— 详见 SuperScalarModel docs/tlsu_e2e_findings_2026-08-13.md。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PTO 把 GM 行距定义为逻辑元素数(PTO-TILE-MODEL-MEMORY-STRIDE),
Linx-TileOP-API@f35d3aa 已把绑定处的 `* sizeof(DType)` 去掉。用例源码本来就
是按元素声明的(RowMajor 第三个模板参数),只是注释还在描述旧的字节编码,
一并更新;tlsu_bench.hpp 把这条单位契约写成第 4 条硬约束。

新增 s2b_stride_elem_i32_8x128。s2_strided 用 512 元素行距,int32 是 4 字节,
而 128 列的行长恰好也是 512 字节 —— "把 512 当字节"这个错误正好退化成一次稠密
搬运,虽然 PAD 标记仍能让它显形,但失败签名长得像"行距被忽略"而不是"单位搞
错了"。新用例取 160 元素行距(640 字节 vs 行长 512 字节),刻意与 sizeof 无
倍数关系:按字节误读则行距成了 40 个元素,比行长 128 还小,每行都会退回上一
行内部,dump 里出现大量重复的低列号元素,两种错误一眼可分。

删掉 c1_store_gap_load。它是归因探针不是用例:延时够长就过,PASS 只证明"等得
够久",不证明定序正确 —— 定序再坏一次它照样绿。它要区分的"定序错 vs 可见性
断"已经定论,任务结束了。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
S 组测数据通路,C 组测定序,但两组都是三五条指令的小序列,队列压力浅:c1 全程
只有 8 个 tile op,LIQ/STQ/SCB 根本填不满。R 组补的是这一段 —— 一次 200 个块,
让交错深度和在飞条目数都上到真实量级。

随机的只有序列本身:块数、load/store 的交错、每一步用哪个 tile 寄存器、读写哪
一块内存。形状、dtype、行距一律固定(8x128 int32,稠密行距),基址一律 256 字
节对齐(区域按 4096 B 紧排,是 256 的整数倍)。这样一旦失败,唯一的变量就是定
序与交错,不必再去排除形状或寻址的嫌疑。

序列里天然长出 RAW / WAR / WAW 和长距离 in-flight 交错:生成器有一半概率把写过
的区域读回来,这正是 RAW 的来源。

生成器按种子出确定结果,生成的 .cpp 一并提交 —— 失败可复现(记下种子即可)、
构建不依赖 Python、生成的源码人能读也能手改来缩例。

首轮(seed=20260814, 200 块 = 91 TLOAD + 109 TSTORE):

    gfrun         非零 49152    16 个区域全部写到(16 x 3072)
    gfsim_new     非零 49152    与 gfrun 逐字节一致
    gfsim_legacy  非零 29952    差 19520 字节,首处 byte 4097

legacy 错得比 C 组彻底得多,说明这组用例的鉴别力确实来自序列深度,不是
换个写法重测一遍 C 组。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
viz_random_case.py 两件事:

1. 把生成的 r1_random_seq_i32.cpp 画成可逐步推进的 GM 演进图(本地 html):
   每一条指令的数据从哪到哪、该步之后每个 GM 区域应该装什么(首字/末字、
   转手次数、上一跳来源)、TLOAD 明确标"GM 不变"。期望值由源码与自识别编码
   闭式推出,不依赖任何模型。

2. --check 把这套推导用作独立判据:

   python3 viz_random_case.py src/r1_random_seq_i32.cpp --check dump.bin

   三方比对以 gfrun 为黄金参考,而 gfrun 与 gfsim 共用 isa/ 解码器 —— 共用层
   错了两边一起错,比对照样 IDENTICAL。2026-08-14 的 ADDTPC 回归正是这样把
   整套用例伪装成全绿的。独立判据只依赖用例源码,因此能独立判死:

       gfrun          PASS  65536 字节逐字节一致
       gfsim_new      PASS
       gfsim_legacy   FAIL  首处不一致 @字节 4096(R1 第 0 行第 0 列)

   还能直接指出机制:legacy 在块 2/3 没拦住 RAW,读到零,零被搬进了 R1。

生成的 html 不进仓库(.gitignore)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 484104cc4b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// (ELF.cpp 对 SHT_PROGBITS 段逐字节 fn_load),完全不经过标量写路径。
// constinit 保证它是静态初始化而非运行期动态初始化。
struct SrcTile {
int32_t v[M * N];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pad the overridable copy source to the load footprint

When this case is built with the documented -DTLSU_M/-DTLSU_N override smaller than the physical tile load footprint, for example -DTLSU_M=4 -DTLSU_N=128, SrcTile allocates only M*N elements even though the comments above note that TLOAD may read rows inferred from the physical size. The load then consumes bytes past gSrc instead of deterministic embedded pattern/PAD data, so the gfrun/gfsim comparison can depend on adjacent ELF contents rather than isolating TLSU behavior.

Useful? React with 👍 / 👎.

liujiang833 and others added 5 commits August 18, 2026 09:24
TLSU 端到端用例做的是搬运语义的正确性验证(dump + 独立判据 + 跨模型差分),
不测 cycle,不应出现在 benchmark 的用例集里。原先放在 microbenchmark/tlsu/,
与 microbenchmark/memory/(25 个 TLSU 性能微基准)同名同级,极易被误认为是
第五个 benchmark family。

- 目录移到 microbenchmark/verification/tlsu/
- 本地 Makefile 补 -I$(CURDIR):Makefile.common 由路径推导 CATEGORY,嵌套一层后
  -I$(MICROBENCH_ROOT)/$(CATEGORY) 只到 verification/,否则找不到 tlsu_bench.hpp
- 新增 verification/tlsu/README.md,两处 README 说明它不参与 compile_all.sh
- 产物路径变为 output/microbenchmark/verification/elf/verification/

9 个用例在 linx_blockisa_llvm_musl 下清空 output 后全部重编通过。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
原 README 只提了一句"生成/可视化/校验",没说清参数。补两章:

- gen_random_case.py:CLI 三个参数(--seed / --blocks / -o)的含义与默认值;
  仓库内 r1 用例实际用的是 --seed 20260814 --blocks 200,展开为 91 TLOAD +
  109 TSTORE(同参数复跑与仓库文件逐字节一致,已实测);形状/区域数/tile 数
  等写死在脚本顶部的常量单列一表;序列的随机策略与提交约定。
- viz_random_case.py:-o 出可视化、--check 出判定两种模式;统计行三个数的
  含义;html 页面的控件与键盘操作;--check 要求 dump 正好 65536 字节,
  一致退 0、不一致或长度不符退 1,可直接接 CI。

顺带补上自识别编码 (tag<<28)|((row+1)<<16)|(col+1) 的说明,以及本地 Makefile
为何需要 -I$(CURDIR)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
原来随机体有一半概率去读只读图样,那一半 TLOAD 读的是永远不会被写的地址,
天然不与任何 store 冲突。现在改成两段:先用 32 块(16 TLOAD + 16 TSTORE)把
16 份图样各装进一个区,之后 --blocks 块随机体只在区与区之间搬运 —— 每次 TLOAD
读的都是可能刚被写过的区。

实测同为 200 块随机体:读只读图样的 TLOAD 46→16,近距 RAW(上次写该区后 ≤16
块就读回)28→42、占 TLOAD 31%→35%,区域→区域搬运 49→86。

代价是"错读了别的区"要靠内容区分,所以 16 份初始图样两两不同:区号编进元素值
空闲的 [27:24](有效行 r+1<=8,pad 行也只到 32,占到 [21:16] 为止,不相碰)。
0x15010001 = 图样 W、区 5、第 1 行、第 1 列,hex dump 按 nibble 直读。这让失败
可读 —— legacy 路径的 r1 dump 首字是 0x14010001,一眼看出串进了区 4 的内容。

配套:
- viz_random_case.py 的 oracle 从跟 tag 改成跟 pattern id;区数不再写死,
  从用例源码的 RESULT_SIZE 读出
- RAW 口径改为"近距":初始化后"读回写过的区"恒真、没有区分度,改成统计上次写
  该区之后 ≤RAW_WINDOW(16) 块就读回。仅供看图,不参与判据
- README 补新方案说明、改前后密度对比,以及一条已知代价:图样多样性随序列长度
  塌缩(16 → 9@75块 → 6@100块 → 3@末尾),判据只看终态,越靠后的错读越易被
  同内容掩盖;要分辨力就缩短 --blocks 多跑种子,别拉长单条序列

验证:SuperScalarModel dev_tlsu(PR#210)自建 gfrun/gfsim 跑全套 —— 9/9 用例
gfsim_new 与 gfrun 逐字节 IDENTICAL,r1 独立判据 PASS;legacy 在 c1/c2/c3/r1
四个定序用例 FAIL,与该 PR 描述的阴性对照一致。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
保留原来的混读策略(TLOAD 一半从只读图样装载、一半读回写过的区),只把只读图样
从 4 份增到 16 份,编号编进元素值空闲的 [27:24]。

16 份的作用有两层:

1. 可分辨。图样只有 4 份时,"搬错了源"有 1/4 概率被同内容掩盖,判据看不见。
2. 不塌缩。TSTORE 只复制不创造,区与区之间搬得越久、存活的图样种类越少(遗传
   漂变);src 装载持续把新编号注回工作区,序列再长也不收敛成少数几种内容。
   实测同为 200 块、只统计已写过的区:末态不同图样数 4→6 种,错读被掩盖的概率
   28.3%→12.5%,中途也不再出现 4 份源那种 46% 的尖峰。

这一版取代了上一个 commit 的 workspace-only 方案:那版冲突密度更高(近距 RAW 42
vs 27),但随机体不读 src 就没有新图样注入,多样性从 16 一路塌到 3、末态掩盖概率
31%。判据只看终态,可分辨性优先。

改名:TlsuWsTag/MakeTlsuWsPattern → TlsuIdTag/MakeTlsuIdPattern,语义从"区号"变
成"图样编号"。编码位段不变,S/C 组的基础编码不受影响。

验证:SuperScalarModel dev_tlsu(PR#210)自建 gfrun/gfsim 跑全套 —— 9/9 用例
gfsim_new 与 gfrun 逐字节 IDENTICAL,r1 独立判据 PASS(65536 字节),legacy 在
c1/c2/c3/r1 四个定序用例 FAIL,与该 PR 的阴性对照一致。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
run_tlsu_suite.sh 硬编码了本目录路径四处(ELFDIR / CASEDIR / 编译时 cd / VIZ)。
改了目录名却不同步那边,失败是静默的:脚本不报错,只在表格里整列打印"无 ELF",
看起来像用例没编出来。2026-08-18 的目录迁移踩过一次,所以把这条契约写进 README,
连同"目录名 / NUM_REGIONS / 用例文件名都是跨仓契约"这句提醒。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant