15

如何使用 GCC 提供的 Multiply-Accumulate 内在函数?

float32x4_t vmlaq_f32 (float32x4_t , float32x4_t , float32x4_t);

谁能解释我必须传递给这个函数的三个参数。我的意思是源和目标寄存器以及函数返回什么?

帮助!!!

4

3 回答 3

21

简单地说 vmla 指令执行以下操作:

struct 
{
  float val[4];
} float32x4_t


float32x4_t vmla (float32x4_t a, float32x4_t b, float32x4_t c)
{
  float32x4 result;

  for (int i=0; i<4; i++)
  {
    result.val[i] =  b.val[i]*c.val[i]+a.val[i];
  }

  return result;
}

所有这些都编译成一条汇编指令:-)

您可以在典型的 4x4 矩阵乘法中使用这个 NEON-assembler 内在函数,用于 3D 图形,如下所示:

float32x4_t transform (float32x4_t * matrix, float32x4_t vector)
{
  /* in a perfect world this code would compile into just four instructions */
  float32x4_t result;

  result = vml (matrix[0], vector);
  result = vmla (result, matrix[1], vector);
  result = vmla (result, matrix[2], vector);
  result = vmla (result, matrix[3], vector);

  return result;
}

这节省了几个周期,因为您不必在乘法后将结果相加。这种加法经常被使用,以至于如今乘法累加 hsa 成为主流(甚至 x86 也将它们添加到最近的一些 SSE 指令集中)。

另外值得一提的是:像这样的乘法累加运算在线性代数和 DSP(数字信号处理)应用中非常常见。ARM 非常聪明,并在 Cortex-A8 NEON-Core 内实现了快速路径。如果 VMLA 指令的第一个参数(累加器)是前面的 VML 或 VMLA 指令的结果,则此快速路径启动。我可以详细介绍,但简而言之,这样的指令系列的运行速度是 VML / VADD / VML / VADD 系列的四倍。

看看我的简单矩阵乘法:我就是这样做的。由于这条快速路径,它的运行速度大约是使用 VML 和 ADD 而不是 VMLA 编写的实现的四倍。

于 2010-07-13T20:11:54.547 回答
9

谷歌搜索vmlaq_f32找到了 RVCT 编译器工具的参考。它是这样说的:

Vector multiply accumulate: vmla -> Vr[i] := Va[i] + Vb[i] * Vc[i]
...
float32x4_t vmlaq_f32 (float32x4_t a, float32x4_t b, float32x4_t c);

定义了以下类型来表示向量。NEON 向量数据类型根据以下模式命名: <type><size>x<number of lanes>_t 例如,int16x4_t 是一个包含四个通道的向量,每个通道都包含一个有符号的 16 位整数。表 E.1 列出了矢量数据类型。

IOW,该函数的返回值将是一个包含 4 个 32 位浮点数的向量,向量的每个元素通过乘以 和 的对应元素,bc加上 的内容来计算a

高温高压

于 2010-07-13T19:16:08.513 回答
1
result = vml (matrix[0], vector);
result = vmla (result, matrix[1], vector);
result = vmla (result, matrix[2], vector);
result = vmla (result, matrix[3], vector);

不过,这个顺序是行不通的。问题是 x 分量仅累积由矩阵行调制的 x 并且可以表示为:

result.x = vector.x * (matrix[0][0] + matrix[1][0] + matrix[2][0] + matrix[3][0]);

...

正确的顺序是:

result = vml (matrix[0], vector.xxxx);
result = vmla(result, matrix[1], vector.yyyy);

...

NEON 和 SSE 没有内置的字段选择(这将需要 8 位指令编码,每个向量寄存器)。例如,GLSL/HLSL 确实有这种设施,所以大多数 GPU 也有。

实现这一目标的替代方法是:

result.x = dp4(vector, matrix[0]);
result.y = dp4(vector, matrix[1]);

... // 当然,矩阵将被转置以产生相同的结果

mul,madd,madd,madd 序列通常是首选,因为它不需要目标寄存器字段的写掩码。

否则代码看起来不错。=)

于 2012-02-29T12:33:07.573 回答