CHAPTER 42 / Tenstorrent · 显式数据流

TTNN 与 Metalium:不同抽象保留同一份数学合同

高层一次 add 与底层三个kernel,怎样验证做的是同一件事?

阅读与推演约 55 分钟练习时间另计

这一章要弄清楚

  • 选择合适的抽象层
  • 分开shape、dtype、layout与memory配置
  • 用独立oracle检查高低层语义

先备知识:Tensix 与 tiles:先安排数据,再安排计算

C++20 本机逻辑模型;不是设备仿真或性能测量。厂商语法片段未在设备或 SDK 执行,实际 API 以匹配版本的公开文档为准。

先问要控制什么,再选接口

假设A为1、2、3、4,B为10、20、30、40,目标是逐项加法。高层接口让你表达“对两个tensor做add”,库负责选择并安排实现;低层接口让你更直接控制kernel、数据搬运、buffer与工作分布。两者的数学合同可以相同,开发和调试所需处理的细节却不同。

TTNN提供较高层的tensor操作;Metalium暴露更低层的Tensix编程方式。初学不需要为了显得底层而绕过现成算子。标准算子、模型验证和快速建立baseline适合从高层入手;需要新操作、特殊布局或具体瓶颈控制时,再向下深入。选择低层意味着多承担实现正确性,并不自动意味着更快。

Tensor 不只是一段数

Tensor同时涉及shape、dtype、layout及存储位置。Shape描述逻辑维度;dtype决定元素表示与数值精度;layout描述数据排列;memory配置关系到放在哪里、怎样分布。一个转换可能只改元数据,也可能真的复制、打包或改变数值精度,不能仅凭函数名假定零成本。

例一的高层路径用单循环,低层教学路径用两项一组的分块循环。逐元素结果都应为11、22、33、44。这个比较只说明两种原创C++表达实现同一算式,没有运行TTNN或Metalium。例二检查矩阵加法shape合同,主动拒绝两个元素数量相同但行列含义不同的输入。

把真实语法放回生命周期

四次调用,三个 API(补充,另估25分钟)

先读 Python 的外壳:a_tt = 调用(...) 把返回的对象绑定到名字 a_tt;它不是 C++ 的类型声明,也不保证复制对象。ttnn.from_torch 中的 ttnn 是模块名,点号选择其中的函数。device=device 左边是形参名字,右边是先前已有的变量;layout=ttnn.TILE_LAYOUT 同样通过名字传参,其中 TILE_LAYOUT 是模块提供的布局选项。

本片段假定 a_hostb_host 是已准备好的、形状和 dtype 兼容的 torch 张量,device 是可用的目标设备对象。这里只跟踪逻辑上逐项相加的四个值,不据此推断所有设备都支持某个特定 shape 或 dtype。

输入与动作 返回值归属
1、2 分别把 host 张量转成指定 device、tile layout 的 TT 张量 a_ttb_tt 是 Python 名字,引用的 TT 张量数据位于指定设备
3 对这两个 TT 张量做 add c_tt 接收结果 TT 张量
4 把结果转回 torch 张量 c_host 接收可在 host 检查的结果

**纸上走一次:**给定逻辑输入 A=[1,2,3,4]、B=[10,20,30,40],先写出两次转换后各张量应代表哪些数,再预测加法和回读应代表 [11,22,33,44]。前两行没有相加,第三行也没有把结果自动赋给 a_host;少了第四行,就没有得到这里命名为 c_host 的回读结果。

转换可能涉及复制、打包、补齐或精度变化,不保证零拷贝。本章 CPU 例二自行采用“shape 必须相等”的窄合同;真实 ttnn.add 还支持规定的广播形式,不能用这个模型替代 API 规则。当前 latestfrom_torch 将 device 类型列为 MeshDeviceadd 默认 BF16 路径可能有最多 1 ULP 的误差(一个可表示数值间距);因此手算值是比较目标,真实结果须按选定 dtype 与版本检查。

本次只核对公开读法,未运行 SDK。来源为 from_torchaddto_torch,均为滚动 latest,核对日期 2026-09-08;实验时另记安装的版本。

# TTNN公开API语法示意,未在设备执行。
# 已有兼容device与torch张量;具体dtype/layout支持以安装版本为准。
a_tt = ttnn.from_torch(a_host, device=device, layout=ttnn.TILE_LAYOUT)
b_tt = ttnn.from_torch(b_host, device=device, layout=ttnn.TILE_LAYOUT)
c_tt = ttnn.add(a_tt, b_tt)
c_host = ttnn.to_torch(c_tt)

这个片段隐藏了设备创建与关闭、异常清理、输入dtype选择和误差检查,所以它不是复制即运行的完整程序。真正实验应从官方对应版本完整示例开始,记录环境,然后用自己的小输入建立reference。若存在低精度转换,比较必须针对实际执行的数值合同,而不是要求和无限精度实数运算完全一致。

高层结果可以当什么证据

高层库输出可以作为交叉检查,但最好另有简单而独立的CPU oracle。若两个路径共享同一错误的reshape或同一输入打包函数,它们可能一起算错。使用非对称矩阵、负数、零、边缘shape和易手算值,能比随机大矩阵更快地暴露合同混淆。

一个有效降层过程是先固定高层输入输出,再一次替换一个环节:数据布局、单核实现或分布方式。每一步都重新比较正确性,最后才比较性能。不要同时改dtype、tile布局、kernel和batch大小,否则很难解释差异来自哪里。

什么问题才值得写低层kernel

例如trace显示某标准操作的前后反复转换布局,可能考虑减少转换或融合;某特殊操作没有合适实现,则需要自定义kernel。但如果瓶颈是host准备数据或反复读取结果,重写计算kernel未必有收益。面试讨论时应先说测量观察、假设和选择,再描述API细节。

TTNN、Metalium及其设备接口持续演进,部分历史示例使用旧设备管理方式。阅读时以相同版本的文档、headers和示例为一组,不能混搭旧CreateDevice代码与新mesh对象接口。教材讲的是职责与合同,不将某一版本的函数表当永久架构。

观察 · 推演

同一add的两种组织

A1 2 3 4B10 20 30 4001 / 03 · PIPELINEA1 2 3 4B10 20 30 4001 / 03 · PIPELINE
输入

两条实现共享明确数学输入。

1 / 3
阅读完整推演文字
  1. 输入

    A:1 2 3 4;B:10 20 30 40

    两条实现共享明确数学输入。

  2. 分块

    tile0:11 22;tile1:33 44

    教学低层按两项一块计算。

  3. 比较

    candidate:11 22 33 44;oracle:11 22 33 44

    逐元素比对独立循环。

跟着例子,走完一遍

例题 01C++20 · 本机可运行

整段操作与分块操作

A=[1,2,3,4],B=[10,20,30,40]。

  1. 单循环产生oracle
  2. 分两块产生candidate
  3. 逐元素比较
38-a.cpp
下载
// Original CPU teaching model; not a device simulator or benchmark.
#include <iostream>
#include <vector>
#include <stdexcept>

void require(bool ok) { if (!ok) throw std::runtime_error("model check failed"); }

int main() {
 const std::vector<int> a{1,2,3,4},b{10,20,30,40};
 std::vector<int> oracle(4),candidate(4);
 for(std::size_t i=0;i<4;++i) oracle[i]=a[i]+b[i];
 for(std::size_t tile=0;tile<2;++tile) for(std::size_t j=0;j<2;++j) {
  const auto i=tile*2+j; candidate[i]=a[i]+b[i];
 }
 require(candidate==oracle);
 std::cout<<"result=";
 for(std::size_t i=0;i<4;++i) std::cout<<(i?" ":"")<<candidate[i];
 std::cout<<" match=1\n";
}

如何编译和运行下载的 .cpp 文件 →

结果与解释

11 22 33 44,两路径一致。

抽象层改变组织,不改变逻辑算式。

在本机运行这个例子

下载后,在文件所在目录执行。需要支持 C++20 的编译器;POSIX 示例还需要章节说明中的系统条件。

clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -pthread 38-a.cpp -o example && ./example

预期标准输出:

result=11 22 33 44 match=1
例题 02C++20 · 本机可运行

元素数相同不等于shape相同

A为2×3,B为3×2;本例只允许相同shape逐项加。

  1. 计算两者元素数均为6
  2. 逐维比较
  3. 拒绝不匹配shape
38-b.cpp
下载
// Original CPU teaching model; not a device simulator or benchmark.
#include <iostream>
#include <array>
#include <stdexcept>

void require(bool ok) { if (!ok) throw std::runtime_error("model check failed"); }

int main() {
 const std::array<int,2> a{2,3},b{3,2};
 const bool count=a[0]*a[1]==b[0]*b[1];
 const bool compatible=a==b;
 require(count && !compatible);
 std::cout<<"same_count="<<count<<" compatible="<<compatible<<'\n';
}
结果与解释

same_count=1,compatible=0。

本题没有广播或自动reshape合同。

在本机运行这个例子

下载后,在文件所在目录执行。需要支持 C++20 的编译器;POSIX 示例还需要章节说明中的系统条件。

clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -pthread 38-b.cpp -o example && ./example

预期标准输出:

same_count=1 compatible=0
常见错误

把抽象层当性能排名

认为Metalium必然快于TTNN。

修正思路:先固定数学和环境合同,再通过trace与同范围测量比较。

轮到你动手

先写预测或代码,再按需打开提示。完整答案用于对照自己的推理。

练习 1

为2×2加法挑一组能发现转置错误的数据。

给我一点提示
  1. 不要全部填一
  2. 让行列方向差异明显
查看答案与推理

例如A=[[1,2],[7,11]],B=[[10,20],[30,40]],期望[[11,22],[37,51]]。逐元素检查能发现B转置,单纯比较总和不能。

练习 2

高层与自写kernel差异同时涉及dtype和layout,怎样重做实验?

给我一点提示
  1. 一次改一项
  2. 保存每一步中间结果
查看答案与推理

先固定dtype、shape与同一输入,只验证layout打包回环;再比较同一数值合同的两种实现;最后单独评估dtype变化。分别记录正确性与性能,避免无法归因的组合改动。

把理解说出来

先用中文讲清因果,再用英文回答。问题依据技能主题编写,并非公司内部题库。

解释

什么时候先选TTNN?

参考回答 / English answer

标准算子、模型功能验证或baseline;需要明确低层控制时再评估Metalium。

I start with TTNN for supported tensor operations and a baseline. I move lower when a specific operation or measured bottleneck requires control.
预测

同为六个元素,2×3和3×2一定能相加吗?

参考回答 / English answer

取决于操作合同;本例要求逐维相同,数量相等不够。

Equal element counts do not establish shape compatibility. I follow the operation's explicit shape and broadcasting rules.
追问

from_torch是否必然零拷贝?

参考回答 / English answer

不能假定。device、layout、dtype转换可能带来搬运和转换成本。

I do not assume conversion is free. Device placement, layout, and dtype changes can require data movement.
调试

两个实现输出相同就足够吗?

参考回答 / English answer

仍可能共享输入打包错误;需独立oracle及可手算非对称输入。

Two implementations can share the same preparation bug. I also use an independent oracle and diagnostic inputs.
解释

降低精度后有小误差,如何判断?

参考回答 / English answer

明确实际dtype与容差,检查非有限值和最大误差,不能随意放宽直到通过。

I define the numerical contract for the actual dtype. I inspect non-finite values and error magnitudes rather than weakening tolerances blindly.
追问

低层kernel为何可能更慢?

参考回答 / English answer

数据移动、布局转换、资源分配和调度仍有成本,低层实现也可能不如已有优化。

Lower-level code still pays transfer and scheduling costs. It may also be less optimized than the library implementation.

继续查证

公开资料用于查证;本章图解和例题是独立教学内容。CPU 逻辑模型不能证明设备性能。