CHAPTER 41 / Tenstorrent · 显式数据流
Tensix 与 tiles:先安排数据,再安排计算
一张小矩阵怎样变成可分配的 tile 工作?
这一章要弄清楚
- 区分数学矩阵与物理布局
- 说明Tensix控制和计算单元的分工
- 计算padding与tile数量
先备知识:CPU cache、缓存一致性与 SIMD / 并行分解、分块与负载均衡 / Tensor shape、stride 与 GEMM 分块
C++20 本机逻辑模型;不是设备仿真或性能测量。厂商语法片段未在设备或 SDK 执行,实际 API 以匹配版本的公开文档为准。
Tile 是数据分块,不是新的数学运算
矩阵加法仍是对应元素相加。Tile把矩阵切成适合处理的小块,改变的是存储与工作安排,不改变数学定义。为了能手算,例一用二乘二的教学tile;Tenstorrent常见计算布局以三十二乘三十二tile为基础,具体支持形状、数据格式和API限制需查目标版本。二乘二模型没有模拟实际tile内部编码。
Tensix可以先理解成芯片上具有本地存储、控制和专用计算能力的节点。公开架构资料区分RISC-V控制处理器、矩阵/向量计算引擎、数据打包拆包和NoC通信。不要把控制代码中一个普通for循环想成矩阵单元所有计算;控制处理器可以向专用引擎发出工作,数据还需按相应格式准备。
从行主序到tile顺序
考虑四乘四矩阵,按行写入零到十五。若按二乘二tile打包,左上tile为0、1、4、5,右上tile为2、3、6、7,随后是下面两块。这里最重要的不是背下排列,而是先确定tile坐标,再确定tile内部坐标,最后回到原矩阵的行列索引。例一同时做打包和还原,验证每个位置都回到原处。
相同的一串字节,如果用不同shape或layout解释,可能产生完全不同的矩阵。输出总和相等也无法发现转置或错位,所以测试应检查每个元素,使用像0到15这样能辨认位置的输入。全部填一虽然方便,却很容易掩盖索引错误。
边缘数据需要padding合同
若逻辑矩阵是三十三乘三十三,使用三十二边长的tile网格需要二乘二共四块,物理覆盖六十四乘六十四。逻辑元素只有一千零八十九,覆盖区有四千零九十六个位置。二者差异影响存储开销,也提醒你最后一块存在无效元素。例二只计算这两个数量,没有声称某个产品必须按这个方式分配所有内存。
Padding值取决于操作。加法或求和常可使用零;max、softmax、归一化等操作需要更认真处理无效位置。最终回读也必须恢复逻辑shape,不能把物理padding当用户结果。逻辑维度、物理维度、dtype和layout应一起成为接口合同。
本地存储不是无限缓存
把输入搬到本地SRAM可以增加复用,但空间要同时容纳输入块、输出块、中间结果、通信缓冲以及程序所需资源。给每个buffer多放几块tile,也许便于流水,却可能减少同时驻留的其他工作。这里需要预算,不是简单认为buffer越大越快。
面试讨论“GPU思路如何迁移”时,可以迁移分块、复用、数值验证和测量方法,但不能把CUDA线程块直接等同Tensix core。两种模型暴露的执行和数据移动控制不同。先说共同的数学问题,再说各自如何组织资源,能避免把名词机械替换。
一个有效的阅读顺序
先用C++完成二维索引与打包回环,再读Tensix架构和公开编程模型,最后看真实tile数据类型及kernel API。暂时没有设备时,本章可验收索引、shape和内存预算的推理;专用引擎数值、NoC传输与设备性能则仍未验证。架构代际的核数、本地存储容量和支持格式不应从单一旧图推广到所有产品。
动手改变 · 观察因果
保持矩阵坐标,改变 tile 存储顺序
所选坐标进入哪个 tile?打包后在线性存储的哪里?
4×4 逻辑矩阵,教学 tile 边长可选 1、2、4。这不是 Tensix 指令模拟,也不声明产品支持这些硬件 tile。 对应 例题 37-a;图中的代码行是步骤提示,完整可编译源码见例题。
改输入后从第一步重新推演。Tab 选择控件,Enter/空格操作按钮;图内方向键平移,手机可横向滑动。
先预测,再前进一步
所选坐标进入哪个 tile?打包后在线性存储的哪里?
输入、边界、状态变化完整文字推演与当前数据
静态推演与完整文字(便于对照、打印)
tile 数值的文字摘要(矩阵坐标见上方实验)
输入按行存储。
每块取两行两列。
逆变换应还原每个坐标。
阅读完整推演文字
- 逻辑矩阵
row0:0 1 2 3;row1:4 5 6 7;row2:8 9 10 11;row3:12 13 14 15
输入按行存储。
- 上半两块
tile0:0 1 4 5;tile1:2 3 6 7
每块取两行两列。
- 下半两块
tile2:8 9 12 13;tile3:10 11 14 15
逆变换应还原每个坐标。
跟着例子,走完一遍
四乘四矩阵的tile打包回环
行主序0–15,教学tile边长2。
- 按tile行列遍历
- 每块内部按行复制
- 逆变换并逐元素比较
// 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() {
std::vector<int> source(16),packed,restored(16);
for(int i=0;i<16;++i) source[static_cast<std::size_t>(i)]=i;
for(int tr=0;tr<2;++tr) for(int tc=0;tc<2;++tc)
for(int r=0;r<2;++r) for(int c=0;c<2;++c)
packed.push_back(source[static_cast<std::size_t>((tr*2+r)*4+tc*2+c)]);
std::size_t k=0;
for(int tr=0;tr<2;++tr) for(int tc=0;tc<2;++tc)
for(int r=0;r<2;++r) for(int c=0;c<2;++c)
restored[static_cast<std::size_t>((tr*2+r)*4+tc*2+c)]=packed[k++];
require(source==restored);
std::cout<<"first_tile="<<packed[0]<<' '<<packed[1]<<' '<<packed[2]<<' '<<packed[3]<<" roundtrip=ok\n";
}
第一tile=0 1 4 5;回环成功。
空间分块改变存储顺序,未改变坐标含义。
在本机运行这个例子
下载后,在文件所在目录执行。需要支持 C++20 的编译器;POSIX 示例还需要章节说明中的系统条件。
clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -pthread 37-a.cpp -o example && ./example预期标准输出:
first_tile=0 1 4 5 roundtrip=ok
边缘tile的逻辑与覆盖数量
逻辑33×33,教学覆盖tile32×32。
- 每维向上取整为2块
- 覆盖64×64
- 比较逻辑与覆盖数量
// Original CPU teaching model; not a device simulator or benchmark.
#include <iostream>
#include <stdexcept>
void require(bool ok) { if (!ok) throw std::runtime_error("model check failed"); }
int main() {
constexpr int rows=33,cols=33,tile=32;
const int tr=(rows+tile-1)/tile, tc=(cols+tile-1)/tile;
const int logical=rows*cols, covered=tr*tc*tile*tile;
require(tr*tc==4 && logical==1089 && covered==4096);
std::cout<<"tiles="<<tr*tc<<" logical="<<logical<<" covered="<<covered<<'\n';
}
4 tiles,1089 logical,4096 covered。
只计算布局开销,不断言具体产品分配策略。
在本机运行这个例子
下载后,在文件所在目录执行。需要支持 C++20 的编译器;POSIX 示例还需要章节说明中的系统条件。
clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -pthread 37-b.cpp -o example && ./example预期标准输出:
tiles=4 logical=1089 covered=4096
把物理shape当逻辑shape
把padding位置一起输出或用于非中性运算。
修正思路:分别保存逻辑维度与布局维度,选择正确padding并裁剪。
轮到你动手
先写预测或代码,再按需打开提示。完整答案用于对照自己的推理。
练习 1
三乘三矩阵0–8按2×2教学tile补零,写左下tile。
给我一点提示
- 左下从第2行第0列开始
- 超出逻辑行填零
查看答案与推理
左下块为6、7、0、0;右下为8、0、0、0。回读需裁回3×3,不能把padding拼进逻辑结果。
练习 2
三个buffer分别2、2、1个tile,每tile2048字节,最低数据空间多少?
给我一点提示
- 先加tile数
- 程序与额外资源另计
查看答案与推理
数据buffer合计5×2048=10240字节。这只是给定buffer的预算,不是整个kernel的SRAM需求;还需计入其他中间存储和实现资源。
把理解说出来
先用中文讲清因果,再用英文回答。问题依据技能主题编写,并非公司内部题库。
tile化会改变矩阵加法定义吗?
参考回答 / English answer
不改变有效坐标的数学结果,但改变存储、padding和工作分配。
Tiling changes layout and scheduling, not the logical elementwise operation. Padding still needs an explicit contract.为什么全部填一不是好的layout测试?
参考回答 / English answer
错序仍全部为一,可能假通过;应使用坐标可辨识值并逐元素检查。
Uniform data can hide permutations. I use coordinate-dependent values and compare every element.33×33使用32×32覆盖tile,为什么是四块?
参考回答 / English answer
每一维都需两块,二维乘积为四,不是总元素数简单除1024。
Each dimension requires two tiles. The two-dimensional grid therefore contains four tiles.RISC-V控制处理器就是矩阵计算引擎吗?
参考回答 / English answer
不是。控制与专用计算分工不同,需理解指令派发和数据准备。
Control processors and specialized compute engines have different roles. Control code orchestrates work rather than replacing the matrix engine.buffer容量加倍总会更快吗?
参考回答 / English answer
不一定。占用SRAM增加,可能挤压其他驻留资源;需算预算并测量。
Larger buffers can consume scarce local memory. I evaluate the whole resource budget and measure the effect.本章2×2模型能证明真实tile编码正确吗?
参考回答 / English answer
不能,只验证原创索引变换;真实格式需对应API和设备验证。
The two-by-two model checks our indexing logic. It does not validate the hardware tile encoding.继续查证
- Metalium Architecture Guide ↗
Architecture, tiles and local memory; generation-specific details
- Tenstorrent matrix lab ↗
Tile layout and engine roles
公开资料用于查证;本章图解和例题是独立教学内容。CPU 逻辑模型不能证明设备性能。