【问题标题】:Optimisation of IIR filterIIR滤波器的优化
【发布时间】:2012-04-06 12:42:30
【问题描述】:

与 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;

【问题讨论】:

    标签: c++ c optimization filter signal-processing


    【解决方案1】:

    这实际上取决于您的编译器和优化选项。这是我的看法:

    • 任何现代编译器都会忽略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;
    }
    

    【讨论】:

      【解决方案2】:

      我不确定,但是这个:

      out[i] = b[1]*m[1] + b[2]*m[2] + b[0]*w;
      

      可能会更糟,因为它会编译为间接访问,这比直接访问性能更糟。

      实际查看的唯一方法是检查编译的汇编程序并分析代码。

      【讨论】:

        【解决方案3】:

        如果您可以将系数 b0、b1、b2 声明为 const,您可能会受益。如果您的任何操作数在编译时已知并固定,代码将更加高效。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-04-26
          • 1970-01-01
          • 1970-01-01
          • 2014-05-11
          • 1970-01-01
          • 2016-07-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多