繁体   English   中英

ARM Cortex-M4 C 代码中的高效嵌入式定点 2x2 矩阵乘法

[英]Efficient Embedded Fixed Point 2x2 Matrix Multiplication in ARM Cortex-M4 C code

我正在尝试在 C 代码中实现非常有效的 2x2 矩阵乘法,以便在 ARM Cortex-M4 中进行操作。 The function accepts 3 pointers to 2x2 arrays, 2 for the inputs to be multiplied and an output buffer passed by the using function. 这是我到目前为止...

static inline void multiply_2x2_2x2(int16_t a[2][2], int16_t b[2][2], int32_t c[2][2])
{
  int32_t a00a01, a10a11, b00b01, b01b11;

  a00a01 = a[0][0] | a[0][1]<<16;
  b00b10 = b[0][0] | b[1][0]<<16;
  b01b11 = b[0][1] | b[1][1]<<16;
  c[0][0] = __SMUAD(a00a01, b00b10);
  c[0][1] = __SMUAD(a00a01, b01b11);

  a10a11 = a[1][0] | a[1][1]<<16;
  c[1][0] = __SMUAD(a10a11, b00b10);
  c[1][1] = __SMUAD(a10a11, b01b11);
}

基本上,我的策略是使用 ARM Cortex-M4 __SMUAD() function 进行实际的乘法累加。 但这需要我提前构建输入 a00a01、a10a11、b00b10 和 b01b11。 我的问题是,鉴于 C 数组在 memory 中应该是连续的,是否有更有效的方式将数据直接传递到函数中而无需中间变量? 第二个问题,我是不是想太多了,我应该让编译器完成它的工作,因为它比我更聪明吗? 我经常这样做。

谢谢!

您可以打破严格的别名规则,并使用int16_t*int32_t*类型转换将矩阵行直接加载到 32 位寄存器中。 一个表达式,例如a00a01 = a[0][0] | a[0][1]<<16 a00a01 = a[0][0] | a[0][1]<<16只是从 RAM 中获取一些连续位并将它们排列到寄存器中的其他连续位中。 请查阅您的编译器手册以了解该标志以禁用其严格的别名假设,并使强制转换安全可用。

您也可以通过首先以转置格式生成b来避免将矩阵列转置到寄存器中。

了解编译器并了解它比您更聪明的情况的最佳方法是反汇编其结果并将指令序列与您的意图进行比较。

第一个主要问题是some_signed_int << 16为负数调用未定义的行为。 所以你到处都是错误。 然后两个int16_t的按位或,其中一个为负也不一定形成有效的int32_t 你真的需要这个标志还是可以放弃它?

ARM 示例使用unsigned int ,它又应该包含原始二进制形式的 2x int16_t 这也是你真正想要的。

此外,对于SMUAD ,您将哪个 16 位字放在哪里似乎并不重要。 所以a[0][0] | a[0][1]<<16; a[0][0] | a[0][1]<<16; 只是用于在 memory 中不必要地交换数据。 它会使无法很好地优化此类代码的编译器感到困惑。 当然,轮班等总是非常快,但这是毫无意义的开销。

(正如有人指出的那样,这整个事情可能更容易用纯汇编程序编写,而不用担心所有 C 类型规则和未定义的行为。)

为了避免所有这些问题,您可以定义自己的联合类型:

typedef union
{
  int16_t  i16 [2][2];
  uint32_t u32 [2];
} mat2x2_t;
  • u32[0]对应i16[0][0]i16[0][1]
  • u32[1]对应i16[1][0]i16[1][1]

C 实际上让您在这些类型之间“输入双关语”非常疯狂(与 C++ 不同)。 工会也避开了脆弱的严格别名规则。

然后 function 可以变成类似于此伪代码的内容:

static uint32_t mat_mul16 (mat2x2_t a, mat2x2_t b)
{
   uint32_t c0 = __SMUAD(a.u32[0], b.u32[0]);
   ...
}

根据SMUAD指令,假设每个这样的行应该给出 2x 有符号的 16 次乘法。

至于与某些默认的MUL相比,这是否真的带来了革命性的性能提升,我有点怀疑。 反汇编并计算 CPU 滴答声。

我是不是想多了,我应该让编译器完成它的工作,因为它比我更聪明?

最有可能:) 旧的经验法则:基准测试,然后仅在您实际发现性能瓶颈时手动优化。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM