目前算法大多使用C语言开发,将算法移植到各种CPU平台时,由于各家编译器存在差异,经常会遇到MIPS性能问题。比如同样的C代码,不同的编译器优化结果会不同,导致在一个ARM上运行没问题的C代码换一个ARM芯片就可能存在性能问题。这时就需要将费时的C函数改成内联汇编指令。因此学一点汇编指令入门还是有必要的。本文讲解Speex使用x86的SSE指令优化乘加运算,代码在resample_sse.c。

#include <xmmintrin.h>

static inline float inner_product_single(const float *a, const float *b, unsigned int len)

{

   int i;

   float ret;

   __m128 sum = _mm_setzero_ps();

   /*

float sum = 0.0f;

  for (i = 0; i < len; i ++)

  {

    sum += a[i] * b[i];

  }

*/

   for (i=0;i<len;i+=8)

   {

      sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i)));

      sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4)));

   }

   sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));

   sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));

   _mm_store_ss(&ret, sum);

   return ret;

}

下面先逐个解释SSE指令的含义,再理解上述代码就不难了。

1、SSE支持浮点运算,VS2013已经集成SSE,包含头文件即可,具体如下。

mmintrin.h     MMX

xmmintrin.h    SSE

emmintrin.h    SSE2

pmmintrin.h    SSE3

tmmintrin.h    SSSE3

intrin.h        SSE4A

smmintrin.h    SSE4.1

nmmintrin.h    SSE4.2

2、__m128是128位(16字节)的数据类型,可以视为四个 32 位浮点元素a(a3, a2, a1, a0),下标从0到3,3是高位元素。_m128变量在16字节边界上自动对齐。VS2013调试器中显示如下图。

Speex的SSE优化

 

3、__m128 _mm_setzero_ps(void);清零指令,返回值r0 := r1 := r2 := r3 := 0.0

4、__m128 _mm_load_ps(float * p ); 加载指令,返回值r0 := p[0], r1 := p[1], r2 := p[2], r3 := p[3].

5、__m128 _mm_mul_ps(__m128 a , __m128 b ); 乘法指令,返回值r0 := a0 * b0, r1 := a1 * b1, r2 := a2 * b2,r3 := a3 * b3.

6、__m128 _mm_add_ps(__m128 a, __m128 b); 加法指令,返回值r0 := a0 + b0, r1 := a1 + b1, r2 := a2 + b2,r3 := a3 + b3.

__m128 _mm_add_ss(__m128 a , __m128 b ) ; 加法指令,返回值r0 := a0 + b0, r1 := a1, r2 := a2, r3 := a3.

SSE的浮点数运算指令分为两大类:packed和scalar。当后面的修饰符是_ps时,表示四个32位浮点数都执行加法运算,当后面的修饰符是_ss时,表示只对最低32位浮点数执行相关运算,高位保持不变。下图很清晰的作出解释。

Speex的SSE优化

 

7、__m128 _mm_movehl_ps( __m128 a, __m128 b );传送指令,将a和b的高位2个float组合得到新的__m128,返回值r3 := a3, r2 := a2, r1 := b3, r0 := b2.

8、__m128 _mm_shuffle_ps(__m128 a , __m128 b , int i );组合指令,shuffle翻译成中文有洗牌的意思。基于位置掩码i从a和b中选择特定元素构成返回值,掩码i可以使用宏_MM_SHUFFLE(z, y, x, w)  (z<<6) | (y<<4) | (x<<2) | w计算。其中z, y说明返回值高位两元素使用b中哪个元素组成,x, w说明低位两元素使用a中哪个元素组成。例如:

Speex的SSE优化

 

9、void _mm_store_ss(float * p , __m128 a ); 存储指令,返回值*p := a0.

 

更多内容,请关注微信公众号“音频算法与工程实践”。

Speex的SSE优化

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-09-11
  • 2021-06-01
  • 2021-08-11
  • 2021-05-16
  • 2021-06-09
猜你喜欢
  • 2021-06-05
  • 2022-12-23
  • 2021-12-17
  • 2022-01-25
  • 2022-03-04
  • 2021-10-20
相关资源
相似解决方案