如何在ARM Cortex-a8中使用乘法和累加内在函数?

时间:2010-07-13 18:57:00

标签: c arm simd intrinsics neon

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

float32x4_t vmlaq_f32 (float32x4_t , float32x4_t , float32x4_t);

任何人都可以解释我必须传递给这个函数的三个参数。我的意思是源和目标寄存器以及函数返回的内容?

帮助!!!

3 个答案:

答案 0 :(得分:20)

简单地说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;
}

所有这些都编译成一个单独的汇编指令: - )

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

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编写的实现的四倍。

答案 1 :(得分:8)

Google为vmlaq_f32提出了the reference for the RVCT compiler tools。这就是它所说的:

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向量数据类型根据以下模式命名:  &lt; type&gt;&lt; size&gt; x&lt;泳道数&gt; _t 例如,int16x4_t是一个包含四个通道的向量,每个通道包含一个带符号的16位整数。表E.1列出了矢量数据类型。

IOW,函数的返回值将是一个包含4个32位浮点数的向量,向量的每个元素都是通过乘以bc的相应元素并加入来计算的。 a

的内容

HTH

答案 2 :(得分: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序列通常是首选,因为它不需要为目标寄存器字段写掩码。

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