如何使用SSE指令提高FIR算法效率
2008-11-24 13:33
351 查看
如何使用SSE指令提高FIR算法效率
在搭建好VC6.0 SSE环境后(请参考:如何使VC6支持内嵌SSE指令),菜菜这里就开始现学现卖了J。因为自己的机器较老,仅支持MMX/SSE/SSE2指令,所以这里的方法也仅仅局限于此。好的,言归正传!一般的FIR算法如下:
static void ShowFIR( float *in, float *out, float *coeff, unsigned int count ) { unsigned int i; unsigned int j; for ( i = 0; i < count - TAP; ++ i ) { float sum; sum = 0; for ( j = 0; j < TAP; ++ j ) { sum += in[ j ] * coeff[ j ]; } *out ++ = sum; ++ in; } } |
SSE的指令在于它使用了向量计算,极大的提高了运算速度,所以在优化之前,我们首先要对原有的c代码进行向量构造。因为水平有限,构造了一个粗糙向量(如果有那位网友能够指导我进行优化,我将万分感谢!)。
这里假设相关的所有长度都是16个字节的倍数。
static void ShowFIR_O1( float *in, float *out, float *coeff, unsigned int count ) { unsigned int i; unsigned int j; for ( i = 0; i < count - TAP; i += 4 ) { out[ 0 ] = 0; out[ 1 ] = 0; out[ 2 ] = 0; out[ 3 ] = 0; for ( j = 0; j < TAP; j += 4 ) { out[ 0 ] += in[ j ] * coeff[ j ]; out[ 0 ] += in[ j + 1 ] * coeff[ j + 1 ]; out[ 0 ] += in[ j + 2 ] * coeff[ j + 2 ]; out[ 0 ] += in[ j + 3 ] * coeff[ j + 3 ]; out[ 1 ] += in[ j + 1 ] * coeff[ j ]; out[ 1 ] += in[ j + 2 ] * coeff[ j + 1 ]; out[ 1 ] += in[ j + 3 ] * coeff[ j + 2 ]; out[ 1 ] += in[ j + 4 ] * coeff[ j + 3 ]; out[ 2 ] += in[ j + 2 ] * coeff[ j ]; out[ 2 ] += in[ j + 3 ] * coeff[ j + 1 ]; out[ 2 ] += in[ j + 4 ] * coeff[ j + 2 ]; out[ 2 ] += in[ j + 5 ] * coeff[ j + 3 ]; out[ 3 ] += in[ j + 3 ] * coeff[ j ]; out[ 3 ] += in[ j + 4 ] * coeff[ j + 1 ]; out[ 3 ] += in[ j + 5 ] * coeff[ j + 2 ]; out[ 3 ] += in[ j + 6 ] * coeff[ j + 3 ]; } in += 4; out += 4; } } |
[95,64],[63:32], [31:0]),所以在计算最后要将它们水平相加后再放入out数组中。
相关的汇编代码如下:
static void ShowFIR_O2( float *inPtr, float *outPtr, float *coeffPtr, unsigned int count ) { __asm { xorps xmm0, xmm0 xorps xmm1, xmm1 xorps xmm2, xmm2 xorps xmm3, xmm3 xor eax, eax xor ecx, ecx mov ebx, DWORD PTR[ coeffPtr ] mov esi, DWORD PTR[ inPtr ] mov edx, DWORD PTR[ outPtr ] jmp b2 b1: movaps xmm4, XMMWORD PTR[ ebx + ecx * 4 ] movaps xmm5, XMMWORD PTR[ esi + ecx * 4 ] mulps xmm5, xmm4 addps xmm0, xmm5 movups xmm5, XMMWORD PTR[ esi + ecx * 4 + 4 ] mulps xmm5, xmm4 addps xmm1, xmm5 movups xmm5, XMMWORD PTR[ esi + ecx * 4 + 8 ] mulps xmm5, xmm4 addps xmm2, xmm5 movups xmm5, XMMWORD PTR[ esi + ecx * 4 + 12 ] mulps xmm5, xmm4 addps xmm3, xmm5 add ecx, 4 cmp ecx, TAP jb b1 movhlps xmm5, xmm0 addps xmm5, xmm0 movaps xmm6, xmm5 shufps xmm5, xmm5, 1 addps xmm5, xmm6 movss XMMWORD PTR[ edx + eax * 4 ], xmm5 movhlps xmm5, xmm1 addps xmm5, xmm1 movaps xmm6, xmm5 shufps xmm5, xmm5, 1 addps xmm5, xmm6 movss XMMWORD PTR[ edx + eax * 4 + 4 ], xmm5 movhlps xmm5, xmm2 addps xmm5, xmm2 movaps xmm6, xmm5 shufps xmm5, xmm5, 1 addps xmm5, xmm6 movss XMMWORD PTR[ edx + eax * 4 + 8 ], xmm5 movhlps xmm5, xmm3 addps xmm5, xmm3 movaps xmm6, xmm5 shufps xmm5, xmm5, 1 addps xmm5, xmm6 movss XMMWORD PTR[ edx + eax * 4 + 12 ], xmm5 add eax, 4 b2: cmp eax, count - TAP jb b1 } } |
align( 16 ) )”修饰,比如:
static __declspec( align( 16 ) ) float fin[ COUNT ]; static __declspec( align( 16 ) ) float fout1[ COUNT ]; static __declspec( align( 16 ) ) float fout2[ COUNT ]; static __declspec( align( 16 ) ) float fcoeff[ TAP ]; |
小结:
上面的汇编代码有几个较大的缺陷:
1. 没有对水平相加进行优化,SSE3有一个HADDPS的水平相加指令,它对性能的提升有重大作用,因为我现在无法调试(计算机仅仅支持SSE2)所以使用了上面一对难看的指令。
2. 没有对out向量进行一次向量写操作(在这个实现中,用了很傻的4次标量写操作)。主要原因是还没有充分理解所有相关的SSE指令,并将它们融会贯通。
3. 对原有代码的向量构造不够好,我想一定有一种方法可以避免最后的差劲的水平向量加操作和4次标量写操作。我的理想状态是没有水平向量相加和一次向量写操作。
我想,随着学习的深入,这些方面将会被逐一解决。我将在后继的文章中对这些遗憾的方面进行逐步探讨以获取更好的性能。如果有大侠能够出手相救,给我醍醐灌顶的提示,小弟在此叩拜了J!
相关文章推荐
- 如何使用SSE指令提高FIR算法效率(进化二)
- 如何使用SSE指令提高FIR算法效率(进化一)
- 如何在Xilinx ISE中使用TCL提高工作效率
- 如何提高Request集合的使用效率?
- 如何使用Tmux提高终端环境下的效率
- 玩转Eclipse--如何使用eclipse可以更好的提高我们的工作效率
- Android并发编程之如何使用ReentrantReadWriteLock替代synchronized来提高程序的效率
- 如何使用Tmux提高终端环境下的效率
- 如何使用组策略集中部署Windows防火墙提高配置效率
- 从关于素数的算法题来学习如何提高代码效率
- 如何使用Tmux提高终端环境下的效率
- 如何实现提高UG软件license在企业中的使用效率
- 如何使用ThreadingTest提高软件安全性检测效率
- Android并发编程之如何使用ReentrantReadWriteLock替代synchronized来提高程序的效率
- Win10系统如何将常用网站添加到开始菜单以提高使用效率
- 如何实现提高UG软件license在企业中的使用效率
- 如何使用Tmux提高终端环境下的效率
- 如何提高TreeView与存储过程的结合使用的效率
- 如何使用eclipse可以更好的提高我们的工作效率
- 如何使用ThreadingTest提高软件安全性检测效率(下)