资讯动态

Mojo TileTensor 布局(Layout)实战:从 layouts 示例到 tile_layout 源码解析

发布时间:2026/9/11 15:29:16 来源:尧图企业网站定制
Mojo TileTensor 布局Layout实战从 layouts 示例到 tile_layout 源码解析【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo本文以 Modular Mojo 仓库中的 docs/code/tile-tensor/layouts 示例目录为入口系统讲解 MojoTileTensor的布局Layout体系如何用 Pixi 一键运行basic_layouts与tiled_layouts两个示例理解 shape/stride、模式modes、分块布局blocked_product / zipped_divide与嵌套布局row_major_nested / col_major_nested等核心概念并对照 max/kernels/src/layout/tile_layout.mojo 源码验证其底层实现。读完本文你将掌握 Layout 的创建、坐标与线性索引互转、运行期维度混用以及 GPU 友好的分块布局构造方法能直接运行示例并在自己的 Mojo 内核中落地这些技巧。一、示例目录速览两个文件讲清楚了什么该示例目录是官方指南 TileTensor layoutslayouts.mdx 的配套代码包含两个可独立运行的 Mojo 程序文件主题覆盖内容basic_layouts.mojo布局基础row_major/col_major创建、坐标与索引互转、Layout构造函数、编译期/运行期维度混用、嵌套Coordtiled_layouts.mojo分块与嵌套布局Layout直接构造分块布局、blocked_product、zipped_divide、row_major_nested/col_major_nestedbasic_layouts.mojo的main()依次调用六个函数row_and_column_major()、coords_to_index()、nested_modes()、specifying_coords()、dynamic_dimensions()、nested_coords()tiled_layouts.mojo的main()则调用use_layout_constructor()、use_blocked_product()、use_zipped_divide()、use_nested_layouts()。代码中大量使用# start-xxx/# end-xxx注释标记与官方文档中的代码片段一一对应方便对照阅读。两个文件顶部使用统一的导入语句涵盖本文讨论的全部 APIfrom layout import Coord, coord, Idx, print_layout from layout.tile_layout import ( Layout, blocked_product, col_major, col_major_nested, row_major, row_major_nested, zipped_divide, )这里的layout包位于仓库的 max/kernels/src/layout 目录其init.mojo 从tile_layout模块重导出了TensorLayout、Layout别名MixedLayout、RowMajorLayout、ColMajorLayout、row_major、col_major等符号从layout模块导出了传统的print_layout。从源码结构看layout包是 MAX 内核库max包的一部分因此示例的 BUILD 依赖中也直接引用了//max:layout。二、用 Pixi 一键运行示例关联文档给出的运行方式非常简洁——前提是已安装 Pixiconda 生态的包/环境管理器。安装后在示例目录下执行pixi run mojo basic_layouts pixi run mojo tiled_layouts这两个命令由 pixi.toml 中的[tasks]定义[tasks] basic_layouts mojo run basic_layouts.mojo tiled_layouts mojo run tiled_layouts.mojopixi.toml还声明了环境来源与依赖channels [conda-forge, https://conda.modular.com/max-nightly/]从 conda-forge 与 Modular 官方 nightly 频道解析包platforms [osx-arm64, linux-64, linux-aarch64]支持 macOSARM与 Linuxx86_64 / aarch64[dependencies]中max *拉取最新版 MAX 平台包内含 Mojo 编译器与layout等标准内核库。除了 Pixi示例目录还提供了 Bazel 构建方式BUILD.bazelbasic_layouts与tiled_layouts分别以mojo_binary形式构建依赖//max:layout即上文提到的layout包和mojo//:stdMojo 标准库并各注册了一个modular_run_binary_test把示例当作运行测试来执行验证输出与预期一致。这意味着在仓库内既可以用pixi run快速体验也可以通过 Bazel 目标做可重复的构建与测试。三、Layout 是什么从坐标到线性索引的映射一个 Layout 本质上是一个函数把一组逻辑坐标如二维矩阵的 (x, y)映射到唯一的线性内存索引。它由两组数构成shape形状描述逻辑坐标空间例如 3×4 矩阵的 shape 是 (3, 4)stride步长决定坐标如何映射到线性索引。一个简单布局可写成 (shape : stride)。例如连续向量 (4:1) 表示 4 个元素、步长 13×4 行主序矩阵写作 ((3, 4):(4, 1))其坐标 (i, j) 映射为线性索引i*4 j*1——即坐标与步长的点积。basic_layouts.mojo中的coords_to_index()演示了正逆两种映射var row_major3x4 row_major[3, 4]() print_layout(row_major3x4.to_layout()) var coords coord[1, 1] var idx row_major3x4(coords) print(index at (1, 1): , idx) print(coordinates at index 7:, row_major3x4.idx2crd(7))print_layout()会把任意二维布局渲染成 ASCII 图坐标标在外部、索引值填在格子内((3, 4):(4, 1)) 0 1 2 3 ---------------- 0 | 0 | 1 | 2 | 3 | ---------------- 1 | 4 | 5 | 6 | 7 | ---------------- 2 | 8 | 9 | 10 | 11 | ----------------运行输出确认坐标 (1, 1) 映射到索引 5索引 7 反解回坐标 (1, 3)。从源码看这些行为定义在 tile_layout.mojo 的TensorLayouttrait第 99 行起中__call__负责坐标→线性索引第 180 行idx2crd负责线性索引→坐标第 199 行后者对zipped_divide等分层布局会保留嵌套的坐标结构。Layout结构体第 301 行以_shape与_stride两个Coord存储形状与步长并暴露shape_coord()、stride_coord()、size()、cosize()等查询方法。注意print_layout()目前只支持传统Layout类型因此示例中总是先调用.to_layout()再做打印而内置的print()函数可以直接输出任意布局的 (shape : stride) 字符串表示。四、Coord 与 Idx编译期与运行期维度坐标、形状、步长统一用Coord表示——一个类元组的容器每个元素可以是整数也可以是嵌套元组。关键能力是同一布局内可以混用编译期与运行期维度# 全部编译期维度 var comptime_coords coord[4, 4] # 全部运行期维度 var runtime_coords Coord(Int32(a), Int32(b), Int32(c)) # 混合运行期 Int 编译期 Idx var mixed_shape Coord((rows, Idx[columns])) var mixed_layout row_major((rows, Idx[columns]))规则很直观coord[...]是编译期别名元素均为编译期整数Coord(...)构造器接受普通Int运行期值或任意CoordLike编译期整数显式写作Idx[N]例如Idx[columns]或Idx[4]。Coord还支持嵌套构造——传Idx元组或Coord元组均可var shape1 Coord((Idx[6], Idx[8])) var shape2 Coord((coord[2, 2], coord[3, 4])) var shape3 Coord((shape1, shape2)) print(shape3) # ((6, 8), ((2, 2), (3, 4)))对应实现位于 max/kernels/src/layout/coord.mojo该模块提供Coord、Idx、coord以及crd2idx/idx2crd等坐标工具函数是tile_layout的类型基础。之所以如此设计是为了在运行时才知道数据形状与编译期已知形状更高效尤其在 GPU 上之间取得平衡。五、创建布局的多种方式row_major()与col_major()是最简单的构造入口row_major让最右坐标变化最快行主序col_major让最左坐标变化最快列主序。basic_layouts.mojo展示了两种调用约定。方式一变参编译期维度var row_major2x4 row_major[2, 4]() var col_major6x6 col_major[6, 6]() comptime row_major3d row_major[4, 4, 4]() # 高维同样支持 comptime col_major3d col_major[4, 4, 4]()方式二传入Coord定义形状var from_coords row_major(coord[6, 8])第二种方式支持前面提到的混合维度因此可以构造出三类运行期布局对应dynamic_dimensions()函数# 全编译期维度 comptime row_major_comptime row_major(coord[16, 8]) # 全运行期维度 var a, b 4, 8 var row_major_runtime row_major(Coord(Int32(a), Int32(b))) # 混合一个运行期 一个编译期 var rows 4 comptime columns 8 var row_major_mixed row_major((rows, Idx[columns]))源码中row_major与col_major均有多个重载tile_layout.mojo 第 974/1026/1084 行的row_major第 1221/1242/1295/1350/1368 行的col_major分别处理变参整型参数、Coord参数等不同形态统一返回RowMajorLayout/ColMajorLayout类型别名。六、模式Modes、rank 与 flat rank一个布局由一个或多个**模式mode**组成每个模式是一个 shape:stride 对。例如一维向量 (8:1) 只有一个模式二维行主序矩阵 ((2, 4):(4, 1)) 有两个模式2:4行与 4:1列列主序 2×4 矩阵写作 ((2, 4):(1, 2))行号变化最快。模式还可以嵌套(((4, 2):(1, 4))) 表示一个 8 元素向量但单一维度被拆成内模式 4:1组内步长 1 外模式 2:4组重复 2 次、步长 4本质是分层步长。由此引出两个容易混淆的度量rank布局 shape 的维度数等价于顶层模式数——(8:1) 与 (((4, 2):(1, 4))) 都是 rank-1flat rank所有模式的总数——前者为 1后者为 2sizeshape 各维度乘积即布局函数定义域的规模寻址的元素总数。这些度量在Layout结构体中都有对应实现rank与flat_rank定义于 tile_layout.mojo 第 324-327 行static_product编译期形状乘积与static_cosize编译期内存跨度定义于第 354-360 行。nested_modes()示例直接用Layout构造函数演示了普通与嵌套两种模式形态var layout_a Layout(coord[4, 4], coord[4, 1]) var layout_b Layout( Coord(coord[2, 2], coord[2, 2]), Coord(coord[1, 4], coord[2, 8]), )七、分块布局blocked_product 与 zipped_divide当需要 tile-major分块主序内存布局——整体分成矩形 tile、tile 内部元素连续存放——以获得更好的访存效率时直接手算步长并不直观。示例目录提供了两条更优雅的路径。直接构造use_layout_constructor()用 shape 与 strides 直接构造一个 3×2 的块步长 (1,6)沿行方向重复、2×5 的块组步长 (3,12)沿列方向重复的 6×10 分块布局var tiled_layout Layout( Coord(coord[3, 2], coord[2, 5]), # shape Coord(coord[1, 6], coord[3, 12]), # strides )blocked_product块积把 tiler 布局中的每个元素替换为一个 tile即用 tile 平铺 tilervar tile col_major[3, 2]() # 3x2 的 tile var tiler col_major[2, 5]() # 2x5 的 tiler var blocked blocked_product(tile, tiler)打印结果显示三者关系tile 为 ((3, 2):(1, 3))tiler 为 ((2, 5):(1, 2))而组合结果正是上面的 (((3, 2), (2, 5)):((1, 6), (3, 12)))——tile 内元素0,1,2 / 3,4,5在内存中连续。实现上tile_layout.mojo 第 1663 行外层步长按tiler.stride * block.cosize缩放保证相邻 tile 之间按整块大小跳转第 1754 行还提供带coalesce_output关键字参数的重载可在连续的内外维度对满足条件时合并以降低布局 rank。zipped_divide拉链除法与blocked_product相反它把二维张量按 tile 形状拆分使每一列恰好装下一个 tile 的数据从而便于按 tile 寻址var base row_major[6, 4]() var result zipped_divide[coord[2, 2]](base) print_layout(base.to_layout()) print_layout(result.to_layout())base是 ((6, 4):(4, 1)) 的普通行主序result变为 (((2, 2), (3, 2)):((4, 1), (8, 2)))第一个 tile 取自 base 左上角 2×2值 0, 4, 1, 5tile 之间按 8 的步长排列。tiled_layouts.mojo的minimal_repro()进一步验证了嵌套坐标下的正逆映射一致性var base row_major[6, 8]() var result zipped_divide[coord[2, 2]](base) var linear_idx Int(result(coord[1, 1])) var natural_coords result.idx2crd(linear_idx) print(linear_idx, natural_coords) # 24, ((1, 0), (1, 0))源码层面zipped_divide第 1385 行返回ZippedDivideLayout别名第 1440 行其结构为inner_shape tile、outer_shape shape / tile、inner_stride 原步长、outer_stride 原步长 × tile。这套设计借鉴了 NVIDIA CUTLASS 中 CuTe 的布局抽象——用组合、补、积、除等代数操作推导高层分块操作。八、嵌套布局row_major_nested 与 col_major_nested除了从简单布局推导还可以用row_major_nested()/col_major_nested()直接由嵌套 shape 构造嵌套布局。它们的做法是先对展平后的 shape计算行或列主序步长再把步长按输入 shape 的嵌套结构重新嵌套回去。对嵌套形状 ((a, b), (c, d))row_major_nested()产生步长 ((b·c·d, c·d), (d, 1))col_major_nested()产生步长 ((1, a), (a·b, a·b·c))。use_nested_layouts()构造了一个 2×2 外层网格、每个格子是 3×4 内层分片fragment的布局# 行主序变体shape ((2, 3), (2, 4)) 展平为 (2, 3, 2, 4) # 行主序步长 (24, 8, 4, 1)再嵌套回 ((24, 8), (4, 1)) var nested row_major_nested( Coord(Coord(Idx[2], Idx[3]), Coord(Idx[2], Idx[4])) ) # 列主序变体步长 (1, 2, 6, 12)再嵌套回 ((1, 2), (6, 12)) var nested_col col_major_nested( Coord(Coord(Idx[2], Idx[3]), Coord(Idx[2], Idx[4])) )对应的 ASCII 输出分别为(((2, 3), (2, 4)):((24, 8), (4, 1)))与(((2, 3), (2, 4)):((1, 2), (6, 12)))。从row_major_nested的源码tile_layout.mojo 第 1103 行看其返回类型是RowMajorNestedLayout/ColMajorNestedLayout别名且目前限制为全静态编译期叶子维度、仅支持单层嵌套——扁平形状请继续使用row_major()/col_major()。嵌套布局的典型场景是张量的逻辑结构本身就是分片的网格例如 GPU 矩阵乘指令NVIDIA MMA、AMD MFMA消费的每线程寄存器 tile或blocked_product()的输出——外层子模式索引分片网格内层子模式索引分片内部。拿到嵌套布局后可以据此构建TileTensor再用tile()提取单个分片参见 TileTensor tensors 指南 的Extracting a tile小节。九、稀疏布局、cosize 与备选坐标前面的例子都是稠密布局元素在内存中连续但 Layout 也能描述稀疏逻辑数组。例如 (4:2) 是一个稀疏一维数组size 为 4定义域规模但内存跨度cosize值域规模即能容纳全部元素的最小连续数组大小为 7——cosize 等于布局产生的最大线性索引 1。Layout结构体在 tile_layout.mojo 第 555 行提供了cosize方法static_cosize则是其编译期版本。另一个容易被忽略的细节是备选坐标。布局除了可以用与 shape 同构的自然坐标寻址还可以用 1D 或 2D 坐标寻址维度按colexicographical广义列主序最左坐标变化最快顺序遍历。例如对 shape ((2, 2), (2, 2)) 的布局((0, 1), (0, 1))与(2, 2)都合法且映射到同一索引自然坐标表请参阅 layouts.mdx 的 Table 1。__call__接受任意这些坐标形式返回线性索引而idx2crd()恒返回该索引对应的自然坐标——tiled_layouts.mojo的use_zipped_divide()尾部恰好演示了用 1D、2D 与自然坐标三种写法寻址同一位置。十、小结把示例变成自己的内核技能回到起点pixi run mojo basic_layouts与pixi run mojo tiled_layouts两个命令背后是一套完整且自洽的布局体系概念层Layout 是坐标→线性索引的函数(shape : stride) 表达模式、rank/flat rank、size/cosize 定义了布局的结构度量构造层row_major/col_major编译期变参或Coord、Layout直接构造、blocked_product/zipped_divide/row_major_nested/col_major_nested组合出分块与嵌套布局类型层Coord/Idx让编译期与运行期维度共存TensorLayouttrait 统一接口Layout结构体按类型参数静态特化兼顾表达力与性能验证层print_layout的 ASCII 图、__call__/idx2crd的正逆映射以及 Bazel 的modular_run_binary_test运行测试都让布局行为可观测、可回归。建议动手路线先跑通两个示例观察输出 → 修改basic_layouts.mojo中的 shape/stride 体会映射规律 → 在tiled_layouts.mojo中更换 tile 尺寸体会blocked_product与zipped_divide的差异 → 最后对照 layouts.mdx 与 tile_layout.mojo 源码验证每一步的数学关系。布局是理解 MojoTileTensor高效访存的钥匙也是编写 GPU 内核时控制内存访问模式的核心抽象。【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价