代码之家  ›  专栏  ›  技术社区  ›  HaggarTheHorrible

如何使用手臂皮质-A8的乘法和累加积分?

  •  13
  • HaggarTheHorrible  · 技术社区  · 16 年前

    如何使用GCC提供的乘法累加积分?

    float32x4_t vmlaq_f32 (float32x4_t , float32x4_t , float32x4_t);
    

    有人能解释我要传递给这个函数的三个参数吗?我的意思是源寄存器和目标寄存器,函数返回什么?

    救命!!!!

    3 回复  |  直到 14 年前
        1
  •  20
  •   Nils Pipenbrinck    16 年前

    简单地说,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汇编程序的固有功能,例如:

    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霓虹灯芯内。如果vml a指令的第一个参数(累加器)是前面的vml或vml a指令的结果,则此快速路径开始。我可以详细说明,但简而言之,这样的指令系列的运行速度是vml/vadd/vml/vadd系列的四倍。

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

        2
  •  8
  •   Aidan Cully    16 年前

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

    iow,函数的返回值将是一个包含4个32位浮点的向量,向量的每个元素都是通过乘以 b c ,并添加 a .

    高温高压

        3
  •  1
  •   gpudude    14 年前
    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序列通常是首选的,因为它不需要目标寄存器字段的写掩码。

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