与IIR滤波器系数有关的快速问题。这是我在网上找到的直接形式II双二阶IIR处理器的一个非常典型的实现。
// b0, b1, b2, a1, a2 are filter coefficients
// m1, m2 are the memory locations
// dn is the de-denormal coeff (=1.0e-20f)
void processBiquad(const float* in, float* out, unsigned length)
{
for(unsigned i = 0; i < length; ++i)
{
register float w = in[i] - a1*m1 - a2*m2 + dn;
out[i] = b1*m1 + b2*m2 + b0*w;
m2 = m1; m1 = w;
}
dn = -dn;
}
据我所知,“寄存器”在某种程度上是不必要的,因为现代编译器的智能程度如何。我的问题是,将滤波器系数存储在单个变量中而不是使用数组并取消引用这些值是否有任何潜在的性能优势?这个问题的答案是否取决于目标平台?
即
out[i] = b[1]*m[1] + b[2]*m[2] + b[0]*w;
与
out[i] = b1*m1 + b2*m2 + b0*w;
答案 0 :(得分:5)
这实际上取决于您的编译器和优化选项。这是我的看法:
register
。它只是编译器的一个提示,而现代的只是不使用它。编辑:好的,出于好奇,我编写了一个小程序,并在使用VS2010进行全面优化时生成了“相同”的代码。这是我在循环中得到的问题表达式(两种情况完全相同):
0128138D fmul dword ptr [eax+0Ch]
01281390 faddp st(1),st
01281392 fld dword ptr [eax+10h]
01281395 fld dword ptr [w]
01281398 fld st(0)
0128139A fmulp st(2),st
0128139C fxch st(2)
0128139E faddp st(1),st
012813A0 fstp dword ptr [ecx+8]
请注意,我添加了几行来输出结果,这样我就确保编译器不会优化所有内容。这是代码:
#include <iostream>
#include <iterator>
#include <algorithm>
class test1
{
float a1, a2, b0, b1, b2;
float dn;
float m1, m2;
public:
void processBiquad(const float* in, float* out, unsigned length)
{
for(unsigned i = 0; i < length; ++i)
{
float w = in[i] - a1*m1 - a2*m2 + dn;
out[i] = b1*m1 + b2*m2 + b0*w;
m2 = m1; m1 = w;
}
dn = -dn;
}
};
class test2
{
float a[2], b[3];
float dn;
float m1, m2;
public:
void processBiquad(const float* in, float* out, unsigned length)
{
for(unsigned i = 0; i < length; ++i)
{
float w = in[i] - a[0]*m1 - a[1]*m2 + dn;
out[i] = b[0]*m1 + b[1]*m2 + b[2]*w;
m2 = m1; m1 = w;
}
dn = -dn;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
test1 t1;
test2 t2;
float a[1000];
float b[1000];
t1.processBiquad(a, b, 1000);
t2.processBiquad(a, b, 1000);
std::copy(b, b+1000, std::ostream_iterator<float>(std::cout, " "));
return 0;
}
答案 1 :(得分:3)
我不确定,但是这个:
out[i] = b[1]*m[1] + b[2]*m[2] + b[0]*w;
可能会更糟,因为它会编译为间接访问,这比直接访问性能更糟糕。
实际看到的唯一方法是检查已编译的汇编程序并分析代码。
答案 2 :(得分:2)
如果可以将系数b0,b1,b2声明为常量,则可能会获益。如果在编译时已知并修复了任何操作数,代码将更有效。