【cutlass】cuTe layout操作

news2024/11/15 13:33:21

简介

cuTe提供了对Layout操作的算法,可以混合执行来构建更复杂的Layout操作,比如在其他layout之间切分和平铺layout

在host或者device上打印cuTe

cuTe的打印函数可以在host和device端打印。cute::print 重载了几乎所有 CuTe 类型,包括指针、Layout、形状、步幅和张量,thread0() 函数仅对内核的全局线程 0 返回 true, 打印 CuTe 对象的典型习惯用法是仅在块 0 的线程 0 上打印。

if (thread0()) {
  print(some_cute_object);
}

有些算法在不同的threads或者blocks上执行不同的算法,为了在非零thread或者block上打印,需要加入一些在“cute/util/debug.hpp”中的debug单元。但是需要在使用之前需要判断“bool thread(int tid, int bid)”,只有在true的时候才可以使用。有的cuTe类型有特殊的打印函数可以使用不同的输出形式。它有一个采用rank-2 matrix layout 和 thread layout 重载,thread layout 是一个包含thread 和value 之间映射的表格。如果想打印latex格式,需要使用cute::print函数。

基本类型

Tuple

CuTe以tuple为启示,tuple是由零个或多个元素组成的有限有序列表。cuTe在device上也设计了"cute::tuple",其行为与std::tuple类似但是为了简化对模板参数进行了限制。

IntTuple

然后cuTe定义了一个IntTuple作为一个整数或者一个IntTuple的Tuple类型。这个递归定义允许我们构建任意嵌套的布局。因此,以下任何一个都是IntTuple的有效模板参数:

  1. “Run-time integers” (或者 “static integers”)只是像“int”或“size_t”这样的普通整数类型。
  2. ”Compile-time integers" 或者cuTe定义的子类,比如Int。这些类型都有一个共同点,即值是在类型本身中编码的(作为公共的“static constexpr value”成员)。CuTe将别名_1_2_3等定义为Int<1>Int<2> Int<3>等类型。
  3. 带有任何模板参数的IntTuple
    cuTe将IntTuple服用做很多不同的事情,包括Shape, Stride, Step和Coord。

Layout

Layout是一种tuple:(Shape, Stride)。从语义上讲,它实现了从“逻辑”形状(多维)索引到数组中的“物理”一维索引的映射。这里是一个具有静态步幅(3,1)的2x3阵列的示例:

Layout layout = make_layout(make_shape (_2{}, _3{}),
                            make_stride(_3{}, _1{}));
print_layout(layout);
for (int i = 0; i < size(layout); ++i) {
  print(layout(i));
  print(", ");
}
print("\n");
print(layout(1, 1));
print("\n");

输出结果(stride的意义可以参照 https://blog.csdn.net/qq_33146555/article/details/130551201),其中下划线标点符号“_”是Underscore的一个常量实例。它的作用类似于Python或Fortran数组切片中的“:”(冒号标点符号):

(_2,_3):(_3,_1)
      0   1   2
    +---+---+---+
 0  | 0 | 1 | 2 |
    +---+---+---+
 1  | 3 | 4 | 5 |
    +---+---+---+
0, 3, 1, 4, 2, 5,
4

Tile

Tile不是Layout,而是Layout或者Tiles或者Underscores的一个tuple.下面讨论的代数Layout操作是在“Layout”上定义的,但“Tile”允许这些操作递归并应用于给定Layout的子Layout或特定模式。这些被称为按模式操作。

Layout的定义和运算

Layout是从整数(逻辑一维坐标)到整数(一维索引)的函数

上述打印示例中的“for”循环显示了CuTe如何用逻辑2-D坐标的column-major layout识别1-D坐标。从“i=0”到“size(Layout)”(即6)进行迭代,并用单个整数坐标“i”索引到我们的Layout中,以column-major 的方式遍历Layout,即使这是row-major的Layout。您可以从“for”循环(0,3,1,4,2,5)的输出中看到这一点。CuTe将该索引“i”称为“1-D坐标”,而“自然坐标”则是logical 2-D坐标。
如果您熟悉C++23的功能“mdspan”,这是“mdspan"布局映射”和CuTe“布局”之间的一个重要区别。“mdspan” Layout映射是单向的:它们总是采用多维逻辑坐标,并返回一个整数偏移量。根据步幅,偏移可以跳过物理1-D阵列的元素。因此,“mdspan”的偏移量与上面“for”循环中的一维逻辑坐标“i”的含义并不相同。您可以使用一维逻辑坐标在任何CuTe “Layout”上正确迭代。“mdspan” 没有一维逻辑坐标的概念。

Rank, depth, size, cosize

Rank: Layout型状的tuple尺寸
Depth: Layout形状的深度。单个整数的深度为0。元组的深度为1+其组件的最大深度。
Size: 形状的大小;函数的域的大小。这是Layout形状中所有范围的乘积。
Cosize: 函数共域的大小(不一定是范围);对于Layout A,A(size(A) - 1) +1。(这里,我们使用size(A) - 1作为一维逻辑坐标输入。)

Layout 兼容

如果布局A和布局B的形状是兼容的,那么它们就是兼容的。如果A的任何自然坐标也是B的有效坐标,则形状A与形状B兼容。

Flatten

“Flatten”操作“un-nest”可能嵌套的Layout。例如:

Layout layout = Layout<Shape <Shape <_4, _3>, _1>,
                     Stride<Stride<_3, _1>, _0>>{};
Layout flat_layout = flatten(layout);

展平后的结果是:

Layout<Shape<_4, _3, _1>, Stride<_3, _1, _0>>
Layout layout = Layout<Shape <_4, Shape <_4,  _2>>,
                     Stride<_4, Stride<_1, _16>>>{};
Layout flat_layout = flatten(layout);

展平后的结果是:

Layout<Shape<_4, _4, _2>, Stride<_4, _1, _16>>

分层布局和平坦化使我们能够将tensor重新解释为matrix、matrix为vector、vector为matrices等。这使我们能够通过将收缩模式组合为单个模式,并根据需要组合A、B、C和“batch”模式,以达到所需的形式,将任意张量收缩实现为批处理矩阵乘法。

Coalesce

“coalesce”操作首先使Layout展平,然后组合所有可能组合的模式,从模式0(最左边的模式)开始,向右移动。如果所有模式都可以组合,那么这将产生一个一维Layout,表示原始Layout访问的数组元素。
“combine”模式意味着什么?在上面的示例中,展平的Layout是(2,1,6):(1,6,2)。

  1. 如果我们看最左边的两个模式,这只是一个长度为2、步长为1的向量。中间模式具有范围1,因此无论如何都不会观察到相应的步幅6。这就给我们留下了(2,6):(1,2)。
  2. 中间结果(2,6):(1,2)只是一个2 x 6列的主矩阵,它可以合并为长度为12和步长为1的向量。
    更正式地说,“组合所有模式”意味着左折叠,其中组合两种模式的二进制运算有三种情况。
  3. 如果最左边的Layout是s1:d1,下一个Layout是1:d0,则合并为s1:d1。这概括了上面的步骤1。如果一个模式的范围为1,我们就无法观察到它的步幅,所以我们可以跳过该模式。
  4. 如果最左边的Layout是1:d1,而下一个Layout是s0:d0,则合并为s0:d0。同样,如果一个模式的范围为1,我们无法观察到它的步幅,所以我们可以跳过该模式。
  5. 如果最左边的Layout是s1:d1,而下一个Layout是s0:s1d1,则合并为s0s1:d1。这概括了上面的步骤2。可以称之为“注意列的主要Layout顺序”
    例如,合并行主布局 (2,2) : (2,1)的结果是 (2,2) : (2,1),相同的布局,因为以上三种情况都不适用。

Complement

Definition

Layout A相对于整数M的补码B满足以下性质:

  1. A A A B B B不相交的: 在A的范围内,对于所有的 x ≠ 0 x \neq 0 x=0 A ( x ) ≠ B ( x ) A(x) \neq B(x) A(x)=B(x) .

  2. B 是 有序的: 对于所有 { 0 , 1 , … , s i z e ( B ) − 1 } \{0, 1, \dots, size(B) - 1\} {0,1,,size(B)1}中的 x x x B ( x − 1 ) < B ( x ) B(x-1) \lt B(x) B(x1)<B(x) .

  3. B 的 是 M: s i z e ( B ) ≥ M / s i z e ( A ) size(B) \geq M / size(A) size(B)M/size(A), 并且 c o s i z e ( B ) ≤ f l o o r ( M / c o s i z e ( A ) ) ∗ c o s i z e ( A ) cosize(B) \leq floor(M / cosize(A)) * cosize(A) cosize(B)floor(M/cosize(A))cosize(A).

关于不相交:我们需要指定 x ≠ 0 x\neq 0 x=0,因为CuTe Layout是线性的。也就是说,如果域是非空的,则该范围始终包含零。
关于有序性质:CuTe Layout是分层stride,因此这意味着如果size(B)为非零,那么B的stride都是正的。

Examples

complement(4:1, 24) 是6:4.

  1. 结果是4:1的不相交,因此它必须具有至少4的步幅(因为它包括0,但必须跳过1、2、3)。
  2. 结果的大小为 ≥ 24 / 4 = 6 \geq 24/4=6 24/4=6。(这加上步骤(1)意味着cosize至少为24。)
  3. 结果的cosize ≤ ( 24 / 4 ) ∗ 4 = 24 \leq(24/4)*4=24 24/44=24。(这加上步骤(2)意味着cosize是24。)
  4. size为6、cosize为24的唯一一维Layout为6:4。

complement(6:4, 24) 是4:1.

  1. 4:1与6:4不相交,但对于任何s>0和d>20,s:d也是如此。
  2. 结果的大小为 ≥ 24 / 6 = 4 \geq24/6=4 24/6=4
  3. 结果的cosize ≤ ( 24 / 21 ) ∗ 21 = 21 \leq(24/21)*21=21 24/2121=21
  4. stride不能大于20(否则(2)将与(3)相矛盾),因此必须小于4。
  5. 因此剩下4:1。

Composition

Layout是函数,所以Layout的组合就是函数的组合。组合 A ∘ B A\circ B AB的意思是“首先应用Layout B,然后将结果视为Layout A的一维逻辑坐标输入,并对其应用A” 通常,此组合可以表示为另一个Layout。

Rules for computing composition

CuTe都使用以下规则计算Composition。

  1. A ∘ B A \circ B AB 的形状与B兼容。在函数组合中,最右边的函数定义了域。对于,这意味着B的任何有效坐标也可以用作 A ∘ B A \circ B AB.
  2. 连接:Layout 可以表示为其子Layout的连接。我们用括号表示连接: B = ( B 0 , B 1 , … ) B=(B_0,B_1,…) B=B0B1。当给定零个或多个“layout”时,CuTe函数“make_layout”会连接它们。
  3. 组合是(左-)分布的,带有级联: A ∘ B = A ∘ ( B 0 , B 1 , . . . ) = ( A ∘ B 0 , A ∘ B 1 , . . . ) A \circ B = A \circ (B_0, B_1, ...) = (A \circ B_0, A \circ B_1, ...) AB=A(B0,B1,...)=(AB0,AB1,...)
  4. “Base case”:对于具有整体形状和步长的布局 A = a : b A = a : b A=a:b B = c : d B = c : d B=c:d A ∘ B = R = c : ( b ∗ d ) A \circ B = R = c : (b * d) AB=R=c:(bd).
  5. 按模式组合:让 ⟨ B , C ⟩ \langle B, C \rangle B,C(尖括号,而不是圆括号)表示两个布局B和C的元组,而不是它们的串联。设 A = ( A 0 , A 1 ) A=(A_0,A_1) A=A0A1。然后, A ∘ ⟨ B , C ⟩ = ( A 0 , A 1 ) ∘ ⟨ B , C ⟩ = ( A 0 ∘ B , A 1 ∘ C ) A \circ \langle B, C \rangle = (A_0, A_1) \circ \langle B, C \rangle = (A_0 \circ B, A_1 \circ C) AB,C=(A0,A1)B,C=(A0B,A1C)

这允许组合独立地应用于 A A A的子层。

Examples: Reshape a vector into a matrix

本节给出两个Composition 示例。两者都以布局为 20 : 2 20:2 20:2的向量开始(也就是说,向量有20个元素,每个元素之间的步长为2)。他们用4 x 5矩阵布局组成这个矢量。这有效地将矢量“重塑”为矩阵。

Example 1

这描述了将向量 20 : 2 20:2 20:2解释为4 x 5列主矩阵。结果Layout的形状为 ( 4 , 5 ) (4,5) 4,5,因为在函数组合中,最右边的函数定义了域。Stride 是什么?

  1. 布局可以表示为其子布局的串联,因此 ( 4 , 5 ) : ( 1 , 4 ) (4,5) : (1,4) (4,5):(1,4) ( 4 : 1 , 5 : 4 ) (4:1, 5:4) (4:1,5:4)
  2. 组成是分布式的,所以 20 : 2 ∘ ( 4 : 1 , 5 : 4 ) 20:2 \circ (4:1, 5:4) 20:2(4:1,5:4)就是 ( 20 : 2 ∘ 4 : 1 , 20 : 2 ∘ 5 : 4 ) (20:2 \circ 4:1, 20:2 \circ 5:4) (20:24:1,20:25:4)
  3. 20 : 2 ∘ 4 : 1 20:2\circ4:1 20:24:1的形状为4(最右边的函数定义域),步长 2 = 2 ⋅ 1 2=2\cdot1 2=21
  4. 20 : 2 ∘ 5 : 4 20:2 \circ 5:4 20:25:4具有形状5和步长 8 = 2 ⋅ 4 8=2\cdot4 8=24
  5. 结果: (4:2, 5:8),串联为(4,5) : (2,8)。

Example 2

20 : 2 ∘ ( 4 , 5 ) : ( 5 , 1 ) 20:2 \circ (4,5) : (5,1) 20:2(4,5):(5,1).
这描述了将向量20:2解释为4 x 5行主矩阵。与之前一样,生成的Layout具有形状 ( 4 , 5 ) (4,5) 4,5。Stride是什么?

  1. 通过去连锁, ( 4 , 5 ) : ( 5 , 1 ) (4,5) : (5,1) (4,5):(5,1) ( 4 : 5 , 5 : 1 ) (4:5, 5:1) (4:5,5:1)
  2. 合成是分配的,所以 20 : 2 ∘ ( 4 : 5 , 5 : 1 ) 20:2 \circ (4:5, 5:1) 20:2(4:5,5:1)就是 ( 20 : 2 ∘ 4 : 5 , 20 : 2 ∘ 5 : 1 ) (20:2 \circ 4:5, 20:2 \circ 5:1) (20:24:5,20:25:1)
  3. 20 : 2 ∘ 4 : 5 20:2\circ4:5 20:24:5的形状为 4 4 4,步长为 10 = 2 ⋅ 5 10 = 2 \cdot 5 10=25
  4. 20 : 2 ∘ 5 : 1 20:2\circ5:1 20:25:1具有形状 5 5 5和步长 2 = 2 ⋅ 1 2 = 2 \cdot 1 2=21
  5. 结果:(4:10, 5:2),串联为(4,5) : (10,2)。

Example: Reshape a matrix into another matrix

Composition ( ( 20 , 2 ) : ( 16 , 4 ) ∘ ( 4 , 5 ) : ( 1 , 4 ) ) ((20,2):(16,4) \circ (4,5):(1,4)) ((20,2):(16,4)(4,5):(1,4))表示将布局为(20,2):(16:4)的矩阵以列为主的方式重塑为4 x 5矩阵。

  1. 通过去连锁, ( 4 , 5 ) : ( 1 , 4 ) (4,5) : (1,4) (4,5):(1,4) ( 4 : 1 , 5 : 4 ) (4:1, 5:4) (4:1,5:4)
  2. 组成是分配的,所以 ( 20 , 2 ) : ( 16 , 4 ) ∘ ( 4 : 1 , 5 : 4 ) (20,2):(16,4) \circ (4:1, 5:4) (20,2):(16,4)(4:1,5:4) ( ( 20 , 2 ) : ( 16 , 4 ) ∘ 4 : 1 , ( 20 , 2 ) : ( 16 , 4 ) ∘ 5 : 4 ) ((20,2):(16,4) \circ 4:1, (20,2):(16,4) \circ 5:4) ((20,2):(16,4)4:1,(20,2):(16,4)5:4)
  3. ( 20 , 2 ) : ( 16 , 4 ) ∘ 4 : 1 (20,2):(16,4) \circ 4:1 (20,2):(16,4)4:1具有形状 4 4 4和Stride 16 16 16。(4:1表示选取(20,2):(16,4)的前4个连续元素。这些元素沿着Layout的第0列(最左边的模式)向下延伸,其Stride为16。)
  4. ( 20 , 2 ) : ( 16 , 4 ) ∘ 5 : 4 (20,2):(16,4) \circ 5:4 (20,2):(16,4)5:4具有形状 5 5 5和Stride 64 = 4 ⋅ 16 64=4\cdot 16 64=416
  5. 结果: ( 4 : 16 , 5 : 64 ) (4:16, 5:64) (4:16,5:64),通过串联为 ( 4 , 5 ) : ( 16 , 64 ) (4,5) : (16,64) (4,5):(16,64)

如果我们使用编译时的形状和步幅,我们就可以得到CuTe的确切结果。以下C++代码打印(_4,_5):(_16,_64)

using namespace cute;
auto a = make_layout(make_shape(Int<20>{}, _2{}), make_stride(_16{}, _4{}));
auto b = make_layout(make_shape(     _4{}, _5{}), make_stride( _1{}, _4{}));
auto c = composition(a, b);
printf("\n");
print(c);

如果我们使用运行时整数,结果可能会不同(但在数学上是相同的)。以下C++代码打印((4,1),(5,1)):((16,4),(64,4))

using namespace cute;
auto a = make_layout(make_shape(20, 2), make_stride(16, 4));
auto b = make_layout(make_shape( 4, 5), make_stride( 1, 4));
auto c = composition(a, b);
printf("\n");
print(c);

((4,1),(5,1)) : ((16,4),(64,4)) 实际上与(4,5) : (16,64)的Layout相同,因为形状中的1不会影响Layout(作为从一个整数到一个整数的数学函数)。CuTe选择不尽可能多地使用运行时值来简化Layout计算,因为涉及运行时值的简化会产生运行时成本。

Product

CuTe包括四种不同的Layout Product:

  1. logical_product
  2. blocked_product
  3. raked_product
  4. tiled_product
    logical_product(A,B)导致LayoutB的每个元素都被LayoutA的“副本”所取代。其他三种Product提供了这种方法的变体。

Example: Tiled matrix

假设我想设计一个由row-major中的3 x 4个tile组成的matrix,其中每个tile是2 x 2列的column-major matrix。每块tile的Layout有形状(2,2)和stride(1,2)。` matrix_of_tiles '的Layout有形状(3,4)和阶梯(4,1)。

Blocked product: the intuitive tiling

如果我用手推断出平铺矩阵的Layout应该是什么,它会是这样的:

(0,0)(1,0)(0,1)(1,1)(0,2)(1,2)(0,3)(1,3)
(0,0)02468101214
(1,0)13579111315
(0,1)1618202224262830
(1,1)1719212325272931
(0,2)3234363840424446
(1,2)3335373941434547

行和列标签使用1-D逻辑坐标和2-D列主坐标的等价性。每一对中的左索引是tile的行和列坐标,而每一对的右索引是行和列的坐标。tile矩阵的列坐标。得到的Layout具有Shape ((2, 3), (2, 4)) 和 Stride ((1, 16), (2, 4)),并且第二种模式可以合并。Shape ((2, 3), (2, 4))是分层的,但它仍然是rank-2,并且可以如上所述以2D绘制。请注意tile的行模式如何保留为产品的行模式的一部分,tile的列模式如何保留产品的列模式。上面的Layout是“blocked_product(tile,matrix_of_tiles)”生成的。blocked product 的一个关键用例是在矩阵上“tiling”一个“atom”(与硬件功能相关的一些tile)。

Layout tile            = Layout<Shape <_2,_2>,
                                Stride<_1,_2>>{};
Layout matrix_of_tiles = Layout<Shape <_3,_4>,
                                Stride<_4,_1>>{};

print_layout(blocked_product(tile, matrix_of_tiles));
Logical product

逻辑乘积logical_product(tile,matrix_of_tiles)产生Shape ((2, 2), (3, 4))和Stride ((1, 2), (16, 4))。

(0,0)(1,0)(2,0)(0,1)(1,1)(2,1)(0,2)(1,2)(2,2)(0,3)(1,3)(2,3)
(0,0)016324203682440122844
(1,0)117335213792541132945
(0,1)2183462238102642143046
(1,1)3193572339112743153147

请注意tile如何出现在最左边的列中,并在每列中以与tile矩阵相同的顺序进行复制。也就是说,可以通过结果的第一模式对tile进行索引,并且可以通过第二模式对tile矩阵进行索引。

Layout tile            = Layout<Shape <_2,_2>,
                                Stride<_1,_2>>{};
Layout matrix_of_tiles = Layout<Shape <_3,_4>,
                                Stride<_4,_1>>{};

print_layout(logical_product(tile, matrix_of_tiles));
Raked product

raked_product(tile, matrix_of_tiles)的结果是Shape ((3, 2), (4, 2)) 和 Stride ((16, 1), (4, 2))。

(0,0)(1,0)(2,0)(3,0)(0,1)(1,1)(2,1)(3,1)
(0,0)04812261014
(1,0)1620242818222630
(2,0)3236404434384246
(0,1)15913371115
(1,1)1721252919232731
(2,1)3337414535394347

tile现在与tile的另一个3x4矩阵interleave或“ranked”,而不是显示为block。其他参考文献称之为“循环分布”。如果你曾经使用过ScaLAPACK,这可能看起来很熟悉。它表示在2x2“过程网格"中的4个过程上的6 x 8矩阵的二维块循环分布,可以参考:“The Two-dimensional Block-Cyclic Distribution” 和 “Local Storage Scheme and Block-Cyclic Mapping”
通常,“logical_product”和这些变体可以产生任何interleaving,包括阻塞、循环、按模式阻塞/循环以及没有通用名称的中间交织。

Layout tile            = Layout<Shape <_2,_2>,
                                Stride<_1,_2>>{};
Layout matrix_of_tiles = Layout<Shape <_3,_4>,
                                Stride<_4,_1>>{};

print_layout(raked_product(tile, matrix_of_tiles));

Division

上一节介绍了Layout product,它们在另一个Layout上复制一个Layout。本节介绍Layout Division。将Layout划分为组件的函数作为tiling和partitioning Layout 的基础非常有用。例如,考虑将向量折叠成矩阵。我们可以想象一种称为“logical_diff”的运算,

Layout vec = Layout<_16,_3>{};           //  16 : 3
Layout col = Layout< _4,_1>{};           //   4 : 1
Layout mat = logical_divide(vec, col);   // (4,4) : (3,12)

它将向量的前4个元素“带入”第一种模式,并将“其余元素”留在第二种模式。这是“vec”中数据的column-major矩阵视图。如果我们想要一个row-major矩阵视图怎么办?

Layout vec = Layout<_16,_3>{};           //  16 : 3
Layout col = Layout< _4,_4>{};           //   4 : 4
Layout mat = logical_divide(vec, col);   // (4,4) : (12,3)

现在,向量的每四个元素都处于第一种模式,其余元素处于第二种模式。多维分层索引使我们可以将此操作扩展到任何“分割”矢量的布局。

Layout vec = Layout<_16,_3>{};           //  16 : 3
Layout col = Layout< _4,_2>{};           //   4 : 2
Layout mat = logical_divide(vec, col);   // (4,(2,2)) : (6,(3,24))
Layout vec = Layout<_16,_3>{};           //  16 : 3
Layout col = Layout<Shape <_2,_2>,
                    Stride<_4,_1>>{};    // (2,2) : (4,1)
Layout mat = logical_divide(vec, col);   // ((2,2),(2,2)) : ((12,3),(6,24))

上面所有的例子都产生了一个4x4矩阵,可以像普通的4x4矩阵一样进行索引和处理,但每个矩阵都有不同的底层Layout。因此,我们的算法可以使用逻辑坐标编写,而无需解决每个Layout所需的详细索引。CuTe包括3种不同类型的Layout划分操作:

  1. logical_divide
  2. zipped_divide
  3. tiled_divide

Logical divide

Example worked in detail
Layout a = make_layout(24, 2);
Layout b = make_layout( 4, 2);
Layout c = logical_divide(a, b);

逻辑除法产生一个秩-2的“Layout”,其中模式0(最左边的模式)对应于除数“b”,模式1(最右边的模式)相应于“余数”。直观地说,24除以4的余数是6,所以我们知道模式1有6个元素。我们只是还不知道它的形状。
CuTe将logical_divide(a, b)定义为composition(a, make_layout(b, complement(b, size(a))))。这里,size(a)是24。直观地说,complement(b, 24)的意思是“余数”,即将b应用于0,1,2, … \dots ,23后剩下的余数。Layout 4:2的意思是“以偶数索引取4个元素。”下表覆盖了补码的共域0,1, … \dots ,23上4:2的范围。

Range of 4:20246
Codomain0123456789 … \dots 23

Layout是线性的,因此它们的范围必须包括零。因此,4:2相对于24的补码是一种布局,其范围

  • 包括零;
  • 不包括4:2范围内的任何其他元素(即满足不相交性质;见上文);
  • 包括尽可能多的0,1, … \dots ,23(从而形成4:2相对于24的“余数”)。

直觉上,补码的范围必须是这样的:0,1,8,9,16,17。将对生成的Layout进行排序。它的大小为6,cosize为18,因此它满足有界性质(见上文)。这是布局(2,3):(1,8)。这从这种对补码的直观理解到知道如何直接计算补码超出了本教程这一部分的范围。)下表显示了4:2及其补码(2, 3) : (1, 8):

Range of 4:20246
Codomain01234567891011121314151617 … \dots 23
Range of complement01891617

现在我们知道,logical_divide(24:2, 4:2)是composition(24:2, make_layout(4:2, (2,3):(1,8)))。两个Layout的组合具有第二(最右边)Layout的形状,因此得到的形状是(4, (2, 3))。我们看到,最左边的模式4对应于除数4:2,而最右边的模式(2, 3)描述了原始形状24的“遗留”部分。
Stride是什么?我们可以从最左边的模式开始。4:2取24:2的每一个其他元素(偶数元素)。这是第二步,跨过第二步。由此产生的步幅为4。类似地,24:2的步幅2是最右侧模式的两个步幅的两倍。得到的布局是(4, (2, 3)) : (4, (2, 16))。

Tiling example

假设我有来自Raked Product部分的6 x 8矩阵,并且想要“收集”“tile”,将Raked Product变成Blocked Product。要做到这一点,我们希望从列中收集两个元素并保留其余元素,然后从行中收集两种元素并保留剩余元素。因此,我们希望将“logical_diff”独立应用于行和列,以便检索适当的元素。在代码中,我们从Raked Product部分的结果中复制Layout,然后在要收集的行和列中指定元素。

Layout raked_prod = Layout<Shape <Shape < _3,_2>,Shape <_4,_2>>,
                           Stride<Stride<_16,_1>,Stride<_4,_2>>>{};
Tile   subtile    = make_tile(Layout<_2,_3>{},    // Gather elements 2 : 3 from mode 0
                              Layout<_2,_4>{});   // Gather elements 2 : 4 from mode 1

print_layout(logical_divide(raked_prod, subtile));
(0,0)(1,0)(0,1)(1,1)(0,2)(1,2)(0,3)(1,3)
(0,0)02468101214
(1,0)13579111315
(0,1)1618202224262830
(1,1)1719212325272931
(0,2)3234363840424446
(1,2)3335373941434547

Zipped divide

“zipped_dive”函数应用“logical_diff”,然后将“子文件”聚集到单个模式中,将“其余文件”聚集在单个模式中。

Layout raked_prod = Layout<Shape <Shape < _3,_2>,Shape <_4,_2>>,
                           Stride<Stride<_16,_1>,Stride<_4,_2>>>{};
Tile   subtile    = make_tile(Layout<_2,_3>{},    // Gather elements 2 : 3 from mode 0
                              Layout<_2,_4>{});   // Gather elements 2 : 4 from mode 1

print_layout(zipped_divide(raked_prod, subtile));

例如,如果我们在上面的例子中应用“zipped_dive”而不是“logical_diffe”,那么我们得到的结果

(0,0)(1,0)(2,0)(0,1)(1,1)(2,1)(0,2)(1,2)(2,2)(0,3)(1,3)(2,3)
(0,0)016324203682440122844
(1,0)117335213792541132945
(0,1)2183462238102642143046
(1,1)3193572339112743153147

这与Logical Product部分中的结果是相同的Layout。也就是说,第一种模式是我们的原始tile(可以解释为2x2矩阵本身),第二种模式是其在raked layout中的logical layout。

More Examples of Divide

为了简洁起见,shape可以与logical_dividetiled_divide一起使用,以快速拆分和平铺张量的模式。例如,这个C++代码

Layout layout     = Layout<Shape <_12, _32,_6>,
                           Stride< _1,_128,_0>>{};
Shape  tile_shape = make_shape(_4{},_8{});
Layout logical_divided_tile = logical_divide(layout, tile_shape);
Layout zipped_divided_tile  =  zipped_divide(layout, tile_shape);

print("layout               :  "); print(layout);               print("\n");
print("tile_shape           :  "); print(tile_shape);           print("\n");
print("logical_divided_tile :  "); print(logical_divided_tile); print("\n");
print("zipped_divided_tile  :  "); print(zipped_divided_tile);  print("\n\n");

产生的输出是:

full_layout          :  (_12,_32,_6):(_1,_128,_0)
tile_shape           :  (_4,_8)
logical_divided_tile :  ((_4,_3),(_8,_4),_6):((_1,_4),(_128,_1024),_0)
zipped_divided_tile  :  ((_4,_8),(_3,_4,_6)):((_1,_128),(_4,_1024,_0))

full_layout          :  (_12,(_4,_8),_6):(_1,(_32,_512),_0)
tile_shape           :  (_4,_8)
logical_divided_tile :  ((_4,_3),((_4,_2),_4),_6):((_1,_4),((_32,_512),_1024),_0)
zipped_divided_tile  :  ((_4,(_4,_2)),(_3,_4,_6)):((_1,(_32,_512)),(_4,_1024,_0))
Layout layout = make_layout(Shape<_8,_8>{},
                            Stride<_8,_1>{});
Layout tile = make_tile(make_layout(Shape<_4>{}),
                        make_layout(Shape<_2>{}));
print("layout: ");
print_layout(layout);
print("\n");
print("tile: ");
print(tile);
print("\n");
print("logical_divide: ");
print_layout(logical_divide(layout, tile));
print("zipped_divide: ");
print_layout(zipped_divide(layout, tile));

的结果是
在这里插入图片描述

Layout layout = make_layout(Shape<_8,_8>{},
                            Stride<_8,_1>{});
Layout tile = make_tile(make_layout(Shape<_2>{}),
                        make_layout(Shape<_4>{}));
print("layout: ");
print_layout(layout);
print("\n");
print("tile: ");
print(tile);
print("\n");
print("logical_divide: ");
print_layout(logical_divide(layout, tile));
print("zipped_divide: ");
print_layout(zipped_divide(layout, tile));

的结果是
在这里插入图片描述

Tiled divide

tiled_divide函数的工作原理与zipped_divide类似,只是它取消打包第二种模式。例如,当您有一个Tile来描述特定操作的所有元素,并且希望将这些元素聚集在一起,但在原始布局中保留这些平铺的逻辑形状时,这很有用。也就是说,

Layout Shape : (M, N, L, ...)
Tile Shape   : <M', N'>
Tiled Result : ((M', N'), m, n, L, ...)

其中mM / M' 同时 nN / N'。我们可以将m看作MTile的个数”,将n看作NTile的数目”。这种操作风格在应用MMA Atoms和Copy Atoms时很常见。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/655996.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MT8168/MTK8168核心板,4G安卓核心板

MT8168是一款集成度很高的高性能应用处理器&#xff0c;具有低功耗特性&#xff0c;并且提供卓越的多媒体体验&#xff0c;适用于平板电脑、智能手持终端以及智能家居和物联网应用等嵌入式设备。这款芯片采用了先进的12纳米工艺&#xff0c;将四核Arm-Cortex A53 MPCore TM CPU…

关于JAVA中 方法中无法改变String的分析

package com.atguigu.String01;public class String01 {public static void main(String[] args) {// 字符串不变性String str "hello";// 对象成员数组是finalchange(str);System.out.println("change后的str:"str);int[] a {1,3,5,7,9};int[] b {2,3,…

【V4L2】 v4l2框架分析之v4l2_fh

一、v4l2_fh简介 &#x1f53a;相关源码文件&#xff1a; /drivers/media/v4l2-fh.c /drivers/media/v4l2-fh.h 在V4L2中&#xff0c;struct v4l2_fh结构用于保存V4L2框架中使用的文件句柄&#xff08;File Handle&#xff09;的数据&#xff0c;即每个打开的视频设备都会对…

微信小程序开发入门学习01-TDesign模板解读

目录 1 使用模板创建小程序2 app.json3 页面布局总结 原来我们使用微信开发者工具&#xff0c;比较困难的是前端框架的选择上&#xff0c;官方也没有提供一套框架供我们使用&#xff0c;最近开发者工具已经提供了一套前端框架&#xff0c;后续我们开发的效率会因为使用模板提高…

Linux-线程的同步与互斥

线程的同步与互斥 进程/线程间的互斥相关背景概念互斥量互斥量接口互斥量的初始化互斥量的销毁加锁和解锁 改善抢票系统互斥量原理 可重入与线程安全重入和线程安全的概念常见线程不安全情况常见线程安全的情况常见不可重入情况常见可重入情况可重入与线程安全的关系可重入与线…

Spring Security系列之认证(Authentication)架构

文章目录 架构主要组件SecurityContextHolderAuthenticationAuthenticationManagerProviderManagerAuthenticationProviderAuthenticationEntryPointAbstractAuthenticationProcessingFilter 架构主要组件 SecurityContextHolder - SecurityContextHolder 是 Spring Security …

【tensorflow】连续输入的神经网络模型训练代码

【tensorflow】连续输入的神经网络模型训练代码 全部代码 - 复制即用 训练输出 代码介绍 全部代码 - 复制即用 from sklearn.model_selection import train_test_split import tensorflow as tf import numpy as np from keras import Input, Model, Sequential from keras.l…

try-catch-finally中的四大坑

目录 1.坑1&#xff1a;finally中使用return 2.坑2&#xff1a;finally中的代码好像“不执行” 3.坑3&#xff1a;finally中的代码“非最后”执行 4.坑4&#xff1a;finally中的代码真的“不执行” 在 Java 语言中 try-catch-finally 看似简单&#xff0c;但想要真正的“掌…

对现在的生活不满意?《围城》给你个人,婚姻,爱情的启示

杨绛先生在100岁感言的时候说&#xff0c;我们曾如此期盼外界的认可&#xff0c;到最后才知道&#xff1a;世界是自己的&#xff0c;与他人毫无关系&#xff01;百岁老人的感言&#xff0c;清晰透彻地道出了人生的真相。我们每个人都是生活于关系之中的&#xff0c;在错综复杂的…

华为OD机试真题 JavaScript 实现【找车位】【2023 B卷 100分】,附详细解题思路

一、题目描述 停车场有一横排车位&#xff0c;0代表没有停车&#xff0c;1代表有车。至少停了一辆车在车位上&#xff0c;也至少有一个空位没有停车。 为了防剐蹭&#xff0c;需为停车人找到一个车位&#xff0c;使得距停车人的车最近的车辆的距离是最大的&#xff0c;返回此…

【tensorflow】连续输入的线性回归模型训练代码

【tensorflow】连续输入的感知机模型训练 全部代码 - 复制即用 训练输出 代码介绍 全部代码 - 复制即用 from sklearn.model_selection import train_test_split import tensorflow as tf import numpy as np from keras import Input, Model, Sequential from keras.layers …

2023ciscn初赛 Unzip

参考&#xff1a; 奇安信攻防社区-2021深育杯线上初赛官方WriteUp-Web篇 1.打开环境 2.上传一个文件&#xff0c;得到以下源码&#xff0c;分析一下8 <?php error_reporting(0); highlight_file(__FILE__);$finfo finfo_open(FILEINFO_MIME_TYPE); //使用 PHP 内置函数…

【软考系统规划与管理师笔记】第7篇 IT服务运营管理

这章也基本上是纯概念&#xff0c;整体上来说系统规划与管理师考试&#xff0c;以概念记忆为主。课本上的知识点往往告诉我们该怎么做&#xff0c;但是如何确保执行到位&#xff0c;如何主动激发员工、客户执行到位&#xff0c;往往还是空白&#xff1f;&#xff0c;作为考试记…

Android ANR分析

ANR(Application Not Responding&#xff0c;即应用程序无响应)。在Android中&#xff0c;当应用程序在规定时间内没有处理完毕相应的事件,系统就会报出ANR。 ANR类型 InputDispatchingTimedOut&#xff1a;应用程序主线程在5s内没有完成用户的input事件ServiceTimeout&#…

小型企业社交完整指南(2023年)

目录 如何制定成功的社交媒体营销策略 1. 设定预算和目标 2. 确定你的目标受众 3.研究竞争对手 4.选择社交媒体网站 5.定义你的社交媒体品牌 6.制定内容策略 社交媒体营销指标 如何制定成功的社交媒体营销策略 有效的内容营销始于计划。以下是通过六个步骤制定企业…

数据备份- rclone ,Duplicity, restic,rsync

目录 Duplicity 安装 语法&#xff1a; 案例&#xff1a; Rclone 简介 安装 设置 命令语法 备份 同步 常用功能选项 常用参数 日志 文件过滤 常用命令 查看远程文件夹的体积占用大小 Restic Restic 支持的存储类型 安装 备份至本地 创建备份仓库 备份至云…

《安富莱嵌入式周报》第315期:开源USB高速分析仪,8GHz示波器开发, 600行C编写RISC-V内核,英特尔推出用于开发人员等宽字体,便携物联网监测器

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; https://www.bilibili.com/video/BV1gV4y117UD/ 《安富莱嵌入式周报》第315期&#xff1a;开源USB…

618期间该如何入手数码好物,列举几款618值得入手的数码好物

跟不少数码爱好者一样&#xff0c;在618、双十一这种大型电商促销节日中&#xff0c;都会选择入手几款心仪且实用的数码好物毕竟产品的热度可以说是相当的在线&#xff0c;而明天就是618的最后一天了&#xff0c;为了赶紧抓住最后的脚步&#xff0c;下面我将给大家分享一些618值…

CefSharp89-winform浏览器(支持H264,MP3,MP4等音视频)x86体验

cef89.*-cefsharp89.*版本应用体验-浏览器 欢迎体验cefsharp-winform-浏览器版本更新方法**测试视频播放**正常![播放视频测试正常](https://img-blog.csdnimg.cn/263f7199ef3c4f1fba59302355e5372d.png)兼容性测试(html5test.com)下载地址其他版本参阅:欢迎体验cefsharp-wi…

华为OD机试之整数对最小和

整数对最小和 题目描述 给定两个整数数组array1、array2&#xff0c;数组元素按升序排列。 假设从array1、array2中分别取出一个元素可构成一对元素&#xff0c;现在需要取出k对元素&#xff0c; 并对取出的所有元素求和&#xff0c;计算和的最小值。 注意&#xff1a; 两…