ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Mojo 布局(Layout)系统实战:从行主序到分块(Tiled)布局的完整示例解析

Mojo 布局(Layout)系统实战:从行主序到分块(Tiled)布局的完整示例解析 Mojo 布局Layout系统实战从行主序到分块Tiled布局的完整示例解析【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo导读本文基于 Modular MAX 仓库中的 max/examples/layouts 示例目录系统讲解 Mojo 语言中Layout类型内存布局抽象的核心概念与实战用法。你将掌握如何用Layout描述张量的内存访问模式、如何在逻辑坐标与线性内存索引之间互相转换、以及如何通过分块tiling与布局代数构建 GPU/CPU 友好的缓存高效访问模式。文末还给出了 Pixi 与 Bazel 两种运行示例的方式并深入源码 layout.mojo 剖析底层实现原理。一、示例概览这个目录讲什么max/examples/layouts目录是官方 Introduction to layouts 教程的配套代码包含两个可直接运行的 Mojo 程序文件主题演示的核心能力basic_layouts.mojo基础布局行主序/列主序、坐标与索引互转、嵌套层次化布局tiled_layouts.mojo分块布局布局构造函数、tile_to_shape、blocked_product、make_ordered_layout、make_layout、zipped_divide目录中同时提供了运行环境声明 pixi.toml依赖max包和 Bazel 构建文件 BUILD.bazel将两个示例编译为mojo_binary并注册了modular_run_binary_test测试说明这套示例既可用于本地学习也被仓库 CI 作为二进制运行测试的一部分。两个示例文件均以# DOC: max/layout/layouts.mdx注释标明它们与官方文档的对应关系是教程内容最直接的可运行落点。二、环境准备与运行方式2.1 使用 Pixi 运行推荐仓库在 pixi.toml 中预定义了运行任务。只要本机安装了 Pixi进入该目录后执行pixi run mojo basic_layouts pixi run mojo tiled_layoutspixi.toml中的任务定义如下[tasks] basic_layouts mojo run basic_layouts.mojo tiled_layouts mojo run tiled_layouts.mojo环境方面该 workspace 声明了conda-forge与 Modular MAX nightly 频道支持osx-arm64、linux-64、linux-aarch64三个平台唯一的运行依赖是max *。首次运行 Pixi 会自动解析 pixi.lock 锁定文件并创建包含 Mojo 编译器的环境。2.2 使用 Bazel 运行仓库是 Bazel 管理的 monorepo在仓库根目录可直接构建并运行示例./bazelw run //max/examples/layouts:basic_layouts ./bazelw run //max/examples/layouts:tiled_layouts对应的 BUILD.bazel 展示了 Mojo 目标的组织方式每个示例是一个mojo_binary依赖//max:layoutMAX 的布局模块与mojo//:std标准库并配套一个modular_run_binary_test用于在 CI 中验证程序可正常执行。2.3 直接使用 mojo CLI如果不使用包管理器也可直接调用 Mojo 编译器mojo run basic_layouts.mojo mojo run tiled_layouts.mojo需要保证环境中已安装可用的 Mojo 编译器并能解析from layout import ...的导入路径。三、基础布局row-major 与 column-major3.1 两个核心工厂方法basic_layouts.mojo 首先演示了两种最经典的线性存储顺序def row_and_column_major(): print(row major and column major) var l2x4row_major Layout.row_major(2, 4) print_layout(l2x4row_major) print() var l6x6col_major Layout.col_major(6, 6) print_layout(l6x6col_major) print()Layout.row_major(2, 4)行主序最后一个维度在内存中变化最快得到 shape(2, 4)、stride(4, 1)Layout.col_major(6, 6)列主序Fortran/MATLAB 风格第一个维度变化最快得到 stride(1, 6)。3.2 源码层面的实现证据在 MAX 布局模块源码 layout.mojo 中row_major(shape: IntTuple)的实现是return Layout(shape, reverse(prefix_product(reverse(shape))))即对 shape 从右向左做前缀积再反转从而得到(4, 1)这类最后一维 stride 为 1的行主序步长。而col_major的默认构造路径见 layout.mojo在未显式提供 stride 时直接使用prefix_product(self.shape)计算列主序步长——这正是文档注释里empty stride 表示列主序的约定。细节提示Layout的shape与stride字段都是IntTuple类型见 layout.mojostride表示沿每个维度移动一个单位时需要在内存中跳过的元素数。四、坐标与索引的互相转换4.1 正向映射坐标 → 线性索引Layout的__call__方法layout.mojo实现坐标到线性索引的核心映射def coords_to_index(): print(coordinates to index) var l3x4row_major Layout.row_major(3, 4) print_layout(l3x4row_major) var coords: IntTuple [1, 1] var idx l3x4row_major(coords) print(index at (1, 1): , idx) print(coordinates at index 7:, l3x4row_major.idx2crd(7)) print()对(3, 4)行主序布局(1, 1)的线性索引为1*4 1 5。底层通过crd2idx计算核心公式即坐标与步长的内积inner_product。4.2 反向映射索引 → 坐标idx2crdlayout.mojo是__call__的逆运算把线性索引还原为逻辑坐标。示例中idx2crd(7)对(3, 4)行主序布局返回(1, 3)。这两个方法被标注为always_inline(nodebug)属于零成本抽象——在源码注释中明确写出其设计目标之一是建立 logical/physical index 之间的零开销映射见 layout.mojo。crd2idx的具体实现在 int_tuple.mojo它对单元素坐标、未知值UNKNOWN_VALUE、自定义 stride 等场景做了专门的快速路径处理。4.3 可视化工具print_layout示例中大量使用的print_layout来自布局模块layout.mojo它会把 2D 布局打印成带内存索引的数字表格方便直观看到每个逻辑坐标落在内存的哪个位置。注意其实现会abort非 2D 布局因此只适合可视化二维场景。五、嵌套层次化布局Layout 的直接构造5.1 显式 shape/stride 构造布局不限于二维Layout支持任意层次的嵌套 shape 与 stride。basic_layouts.mojo 演示了两种嵌套方式def nested_modes(): print(nested modes) var layout_a Layout([4, 4], [4, 1]) print_layout(layout_a) print() var layout_b Layout( [[2, 2], [2, 2]], [[1, 4], [2, 8]], ) print_layout(layout_b) print()layout_ashape 为([4], [4])、stride 为([4], [1])即二维行主序布局layout_bshape 为([[2, 2], [2, 2]])、stride 为([[1, 4], [2, 8]])是一个两层嵌套的布局——外层 2×2每个元素内部又是 2×2 的子块。这种层次化结构是分块矩阵运算的基础。从源码看Layout的构造器layout.mojo接受(shape, stride)两个IntTupleIntTuple的元素本身也可以是元组从而天然支持嵌套维度。六、分块Tiled布局的五种构建手法tiled_layouts.mojo 通过五个函数系统演示了布局代数layout algebra的核心操作这些操作共同服务于把大张量拆成 cache 友好的小块。6.1 用构造函数直接创建分块布局def use_layout_constructor(): print(layout constructor) var tiled_layout Layout( [[3, 2], [2, 5]], # shape [[1, 6], [3, 12]], # strides ) print_layout(tiled_layout) print()[[3, 2], [2, 5]]表示一个两层嵌套 shape外层 2×2每个元素是一个子块内层子块 3×2 与 2×5。对应 stride[[1, 6], [3, 12]]定义了每个层级的内存步长。6.2tile_to_shape把 tile 平铺到目标 shapedef use_tile_to_shape(): print(tile to shape) var tts tile_to_shape(Layout.col_major(3, 2), [6, 10]) print_layout(tts) print()tile_to_shape(tile, target_shape)将3×2的列主序 tile 重复平铺填满6×10的目标形状即 2 行 × 5 列的 tile 网格。源码实现layout.mojo会逐维计算target_shape / tile_shape得到 tiler 的尺寸并要求目标形状必须能被 tile 整除否则直接abort报错tiler 的存储顺序默认列主序也可通过order参数指定。6.3blocked_producttile × tiler 的分块乘积def use_blocked_product(): print(blocked product) # Define 2x3 tile var tile Layout.col_major(3, 2) # Define a 2x5 tiler var tiler Layout.col_major(2, 5) var blocked blocked_product(tile.copy(), tiler.copy()) print(Tile:) print_layout(tile) print(\nTiler:) print_layout(tiler) print(\nTiled layout:) print(blocked) print()blocked_product(layout_a, layout_b)将内层 tilelayout_a与外层 tilerlayout_b组合成层次化分块布局外层每个元素被替换为一块 tile。在 layout.mojo 的实现中它先做logical_product再zip_modes把两组 mode 交织成(tile_mode, tiler_mode)的嵌套结构coalesce_output参数可控制是否合并输出维度。源码文档给出了一个直观例子blocked_product(row_major(2,2), row_major(2,3))生成 4×6 的 2×2 分块布局其内存索引表清楚地展示了 2×2 块内地址连续、块间跳变的特征。6.4make_ordered_layout按指定顺序生成紧凑布局def use_make_ordered_layout(): print(make ordered layout) var ordered make_ordered_layout( [[3, 2], [2, 5]], # shape [[0, 2], [1, 3]], # order ) print(ordered)make_ordered_layout(shape, order)根据遍历优先级order数值越小优先级越高生成**紧凑bijective**布局即每个索引恰好对应一个坐标。源码实现layout.mojo调用compact_order(shape, order)计算 stride。例如文档示例中 shape(2,3,4,5)搭配 order(1,4,3,2)会得到 stride(1,40,10,2)——维度 0 变化最快stride 1维度 3 次之stride 2依此类推。6.5make_layout与zipped_divide拼接与除法def use_make_layout(): print(make layout) var layout1 Layout([2, 3], [3, 1]) var layout2 Layout([4, 5], [5, 1]) var combined make_layout(layout1, layout2) print_layout(combined)make_layout将多个布局的 shape/stride首尾拼接成一个更高阶的布局layout.mojo是构建层次化布局的基础组装原语。def use_zipped_divide(): print(zipped divide) # Create layouts var base Layout.row_major(6, 8) var pattern Layout([2, 2]) var result zipped_divide(base, pattern) print_layout(result)zipped_divide(layout_a, layout_b)是hierarchical_unzip的别名layout.mojo把6×8的行主序布局按2×2的 pattern划分为 3×4 个分块的层次化布局——可以理解为blocked_product的逆操作。它同样支持传入LayoutList以应用多个分块模式layout.mojo。七、这些布局能力用在何处Layout在 MAX 生态中是张量访存的核心抽象。从 max/kernels/src/layout/init.mojo 的模块说明可以看到它的两种典型定位张量坐标 → 线性内存索引的映射用于把逻辑张量坐标映射到内存地址GPU 线程 → 数据块的映射用于把 GPU 线程映射到数据的 tile 上。分块相关函数blocked_product、tile_to_shape、zipped_divide等的源码注释均明确指向矩阵乘法等张量运算中的 cache 高效利用这一目标。在实际仓库中Layout被广泛用于LayoutTensor带显式内存布局的高性能张量类型见 layout_tensor.mojo并贯穿于max/kernels/src下各类 kernel 的实现中例如 conv.mojo、tile_layout.mojo 等。如果你希望继续深入官方还提供了更高阶的配套示例 max/examples/layout_tensor/layout_tensor_examples.mojo演示LayoutTensor与 GPU 内核的结合使用。八、小结通过本目录的两个示例你可以完整掌握 MojoLayout的核心使用链路基础存储顺序Layout.row_major/Layout.col_major覆盖行主序与列主序两种经典存储索引映射__call__坐标→索引与idx2crd索引→坐标构成双向转换是零成本抽象的核心嵌套布局(shape, stride)直接构造支持任意层级嵌套为分块做准备布局代数tile_to_shape、blocked_product、make_ordered_layout、make_layout、zipped_divide分别对应平铺、组合、有序紧凑化、拼接与划分五种变换是构建 cache 友好访存模式、支撑高性能张量运算的基础工具集。动手实践时推荐先pixi run mojo basic_layouts跑通基础示例再用pixi run mojo tiled_layouts观察分块布局的内存索引表格配合 layout.mojo 源码中每个函数自带的示例与输出即可从会调用进阶到懂原理。【免费下载链接】mojoThe Modular Platform (includes MAX Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进