首页 > 解决方案 > 是否可以在计算中对串行依赖项使用 SIMD,例如指数移动平均滤波器?

问题描述

我正在处理我的音频应用程序中不同参数的多个(独立)指数移动平均1 极滤波器,目的是在音频速率下平滑每个参数值:

for (int i = 0; i < mParams.GetSize(); i++) {
    mParams.Get(i)->SmoothBlock(blockSize);
}

...

inline void SmoothBlock(int blockSize) {
    double inputA0 = mValue * a0;

    for (int sampleIndex = 0; sampleIndex < blockSize; sampleIndex++) {
        mSmoothedValues[sampleIndex] = z1 = inputA0 + z1 * b1;
    }
}   

我想利用 CPUSIMD指令,并行处理它们,但我不确定如何实现这一点。

事实上,z1是递归的:考虑到“以前的值”,不能“打包”双数组,对吧?

也许有办法正确组织不同过滤器的数据并并行处理它们?

欢迎任何提示或建议!

请注意:我没有多个信号路径。任何参数代表(唯一)处理信号的不同控制。假设我有一个 sin 信号:参数 1 将影响增益,参数 2 音高,参数 3 滤波器截止,参数 4 声像等等。

标签: c++optimizationdependenciessimdintrinsics

解决方案


您有一个特殊情况,输入信号是Heaviside step function。您希望获得对此函数的过滤器响应,称为Step response。这种情况下的递归可以被消除。首先,让我们展开几个步骤的递归。

z[1] = in + z[0]*b
z[2] = in + z[1]*b = in + (in + z[0]*b)*b  = in*(1 + b) + z[0]*b^2
z[3] = in + z[2]*b = in*(1 + b + b^2) + z[0]*b^3
z[4] = in + z[3]*b = in*(1 + b + b^2 + b^3) + z[0]*b^4

从最后一个方程:

z[1] = in*(1 + b + b^2 + b^3) + z[-3]*b^4
z[2] = in*(1 + b + b^2 + b^3) + z[-2]*b^4
z[3] = in*(1 + b + b^2 + b^3) + z[-1]*b^4
z[4] = in*(1 + b + b^2 + b^3) + z[0]*b^4

现在很容易以矢量化形式重写它。

in' = {in, in, in, in};
z' = in' * (1 + b + b^2 + b^3) + z'*b^4

其中“'”表示向量或单个 SIMD 寄存器。现在很容易将其翻译成 immintrin 指令。请注意,现在您不能更改任何样本的输入值,但有时可以更改四个样本的倍数。

此外,您可以将两个或多个 SIMD 寄存器表示为一个向量,并进一步扩展递归。由于更好的管道利用率,这将提高性能,但不要过度使用,否则您将没有足够的寄存器。


推荐阅读