矩阵类型

Clang 提供了一个 C/C++ 语言扩展,允许用户直接将固定大小的二维矩阵表示为语言值,并对它们执行算术运算。

此功能目前处于实验阶段,其设计和实现都在不断变化。

草案规范

矩阵类型

矩阵类型是一种标量类型,具有底层元素类型、恒定数量的和恒定数量的。具有相同元素类型、行和列的矩阵类型是相同类型。矩阵类型的值包含rows * columns元素类型值的存储空间。内部布局、总体大小和对齐方式是实现定义的。

行数和列数的乘积的最大值是实现定义的。如果超过该实现定义的限制,程序将是非法的。

目前,矩阵的元素类型仅允许为以下类型之一

  • 整数类型(如 C23 6.2.5p22 中所述),但不包括枚举类型和bool

  • 标准浮点类型floatdouble

  • 半精度浮点类型(如果目标支持)

将来可能支持其他类型。

矩阵类型属性

可以通过将matrix_type属性添加到typedef(或 C++ 别名声明)的声明来声明矩阵类型。typedef 的底层类型必须是有效的矩阵元素类型。该属性接受两个参数,它们都必须是整数常量表达式,并且计算结果为大于零的值。第一个参数指定行数,第二个参数指定列数。typedef 的底层类型变为具有给定维度的矩阵类型,其元素类型为以前的底层类型。

如果typedef-name 的声明具有matrix_type属性,则该typedef-name 的所有声明都应具有具有相同元素类型、行数和列数的 matrix_type 属性。

标准转换

标准转换扩展如下。请注意,这些转换有意不被列为满足赋值约束,也就是说,它们只允许作为显式强制转换,而不是作为隐式转换。

如果行数和列数相同,并且值元素可以转换为结果类型的元素类型,则可以将矩阵类型的值转换为另一种矩阵类型。结果是一个矩阵,其中每个元素都是转换后的对应元素。

如果任何实数类型(如 C23 6.2.5p14 中所述)的值可以转换为矩阵类型的元素类型,则可以将该值转换为矩阵类型。结果是一个矩阵,其中所有元素都是转换后的原始值。

如果原始类型和结果类型之间的行数或列数不同,程序将是非法的。

算术转换

通常的算术转换扩展如下。

在开头插入

  • 如果两个操作数都是矩阵类型,则不会执行任何算术转换。

  • 如果一个操作数是矩阵类型,而另一个操作数是实数类型,则根据标准转换规则将实数类型操作数转换为矩阵类型。

矩阵类型元素访问运算符

表达式形式为E1 [E2] [E3],其中E1 具有矩阵类型cv M,是一个矩阵元素访问表达式。令TM 的元素类型,令RC 分别为M 中的行数和列数。索引表达式应具有整数或无作用域枚举类型,并且不应是逗号运算符的使用,除非在括号内。第一个索引表达式应计算结果为小于R 的非负值,第二个索引表达式应计算结果为小于C 的非负值,否则表达式的行为未定义。如果E1 是右值,则结果是类型为T 的右值,并且是矩阵中给定行和列的元素的值。否则,结果是类型为cv T 的左值,具有与E1 相同的值类别,并引用矩阵中给定行和列的元素。

包含对矩阵的单一下标表达式的程序是非法的。

注意:我们考虑提供形式为postfix-expression [expression] 的表达式来访问矩阵的列。我们认为,一旦同时支持列主矩阵和行主矩阵,这种表达式就会有问题:根据内存布局,要么访问列要么访问行可以高效完成,但不能两者兼而有之。相反,我们建议提供内置函数来从矩阵中提取行和列。这使操作更加明确。

矩阵类型二元运算符

给定两个矩阵,+- 运算符执行逐元素加法和减法,而* 运算符执行矩阵乘法。+-*/ 也可用于矩阵和标量值,将操作应用于矩阵的每个元素。

此扩展的早期版本不支持除以标量。你可以使用__has_extension(matrix_types_scalar_division) 测试此功能的可用性。

对于表达式M1 BIN_OP M2,其中

  • BIN_OP+- 中的一个,M1M2 中的一个是矩阵类型,另一个是矩阵类型或实数类型;或者

  • BIN_OP*M1M2 中的一个是矩阵类型,另一个是

    实数类型;或者

  • BIN_OP/M1 是矩阵类型,M2 是实数类型

  • 将对M1M2 应用通常的算术转换。[ 注意:如果M1M2 是实数类型,则它们将在此处广播到矩阵。 — 结束注意 ]

  • M1M2 应为相同的矩阵类型。

  • 结果等效于以下 Res,其中 col 是矩阵类型中的列数,row 是矩阵类型中的行数

decltype(M1) Res;
for (int C = 0; C < col; ++C)
  for (int R = 0; R < row; ++R)
    Res[R][C] = M1[R][C] BIN_OP M2[R][C];

给定表达式M1 * M2,其中M1M2 是矩阵类型

  • 将对M1M2 应用通常的算术转换。

  • M1 的类型应具有与M2 的类型具有相同的列数。 M1M2 的元素类型应为相同的类型。

  • 结果类型MTy 是一种矩阵类型,具有公共元素类型、M1 的行数和M2 的列数。

  • 结果等效于以下Res,其中EltTyMTy 的元素类型,col 是列数,rowMTy 中的行数,innerM1 的列数

MTy Res;
for (int C = 0; C < col; ++C) {
  for (int R = 0; R < row; ++R) {
    EltTy Elt = 0;
    for (int K = 0; K < inner; ++K) {
      Elt += M1[R][K] * M2[K][C];
  }
  Res[R][C] = Elt;
}

矩阵类型的所有操作都与元素类型在有符号溢出方面的行为匹配。

在浮点收缩、舍入和环境规则方面,矩阵类型上的操作与上述相应展开中的逐元素操作的行为匹配。

浮点矩阵上的操作具有与表达式上下文中的普通浮点操作相同的舍入和浮点环境行为。出于浮点收缩的目的,矩阵操作中完成的所有计算都被视为中间操作,并且它们的计算结果不需要舍入到元素类型的格式,直到包含表达式中的最终结果。这受收缩的正常限制的约束,例如#pragma STDC FP_CONTRACT

对于 +=-=*= 运算符,语义与其展开形式一致。

矩阵类型内置运算

每个矩阵类型都支持一系列类似函数调用的内置表达式,但它们不构成重载集。以下是它们作为函数声明的描述,以及关于如何构建参数列表类型和返回值类型的规则,以及 C++ 标准中 [library.description.structure.specifications]/3 中的库描述元素。

定义

  • MM1M2M3 - 矩阵类型

  • T - 元素类型

  • rowcol - 分别代表行和列参数。

M2 __builtin_matrix_transpose(M1 matrix)

说明:返回值类型是一个 cv 无限定的矩阵类型,它与 M1 具有相同的元素类型,并且具有与 M1 列数相同的行数,以及与 M1 行数相同的列数。

返回值:一个矩阵 Res 等同于下面的代码,其中 col 指的是 M 的列数,row 指的是 M 的行数。

效果:等同于

M Res;
for (int C = 0; C < col; ++C)
  for (int R = 0; R < row; ++R)
    Res[C][R] = matrix[R][C];

M __builtin_matrix_column_major_load(T *ptr, size_t row, size_t col, size_t columnStride)

要求rowcol 应为大于 0 的整型常量。

先决条件columnStride 大于或等于 row

说明:返回值类型是一个 cv 无限定的矩阵类型,其元素类型为 T 的 cv 无限定版本,行数和列数分别等于 rowcol。参数 columnStride 是可选的,如果省略,则使用 row 作为 columnStride

返回值:一个矩阵 Res 等同于

M Res;
for (size_t C = 0; C < col; ++C) {
  for (size_t R = 0; R < row; ++K)
    Res[R][C] = ptr[R];
  ptr += columnStride
}

void __builtin_matrix_column_major_store(M matrix, T *ptr, size_t columnStride)

先决条件columnStride 大于或等于 M 中的行数。

说明:类型 T 是矩阵参数元素类型的 const 无限定版本。参数 columnStride 是可选的,如果省略,则使用 M 的行数作为 columnStride

效果:等同于

for (size_t C = 0; C < columns in M; ++C) {
  for (size_t R = 0; R < rows in M; ++K)
    ptr[R] = matrix[R][C];
  ptr += columnStride
}

待办事项

待办事项:允许使用 M::element_type、M::rows 和 M::columns,其中 M 是一个矩阵类型,是否合理?我们在其他地方都不支持这种做法,但它很方便。另一种选择是使用模板推断来提取这些信息。另外,为 C 添加拼写。

未来工作:初始化语法。

Clang 实现中的决策

本节详细介绍了在 Clang 实现中做出的决定,不属于草案规范的一部分。

矩阵类型值的元素以列主序排列,不含填充。

我们建议提供一个 Clang 选项来覆盖此行为,并允许对这些操作进行收缩(例如:-ffp-contract=matrix)。

待办事项:指定矩阵值如何传递给函数。