向量优化挑战:递归音频过滤功能

时间:2016-04-15 17:30:46

标签: swift performance optimization accelerate-framework

我试图在Swift中有效地复制Apple的vDSP_deq22功能(来自Accelerate框架)而不使用专有代码,因此我可以跨平台使用它。

我已经把这个功能归结为一个看起来像这样的相对简单的循环(我能够在数学上分解原始方程的某些项):

// `input` is a vector of ~1024 Floats between 0 and 1
func filterBuffer(input: [Float]) -> Float {
    // optimization:
    let (b0, a1, a2) = (coefficients[0], coefficients[3], coefficients[4])

    for x in input {
        let y = b0 * (x - scratchSpace[1]) - a1 * scratchSpace[2] - a2 * scratchSpace[3]

        // ... do something with y ...

        // These are the problematic values: previous values of y are used
        // in calculating the current value of y, i.e. the calculation is
        // recursive, which makes it difficult to vectorize.
        scratchSpace[3] = scratchSpace[2]   // y[n-2]
        scratchSpace[2] = y                 // y[n-1]

        scratchSpace[1] = scratchSpace[0]   // x[n-2]
        scratchSpace[0] = x                 // x[n-1]
    }
}

这大约是天真(非分解)实现的两倍,但Apple的功能仍然再次快50%(即我的版本需要50%的CPU时间)。

鉴于Apple的功能处于Accelerate框架中,我假设它使用某种SIMD指令和/或其他矢量化技巧。

按照这种思路,我试过这个:

// input gets multiplied by `b0` aka `coefficients[0]` and put into inputBuffer
// we saved the last two elements of inputBuffer
vDSP_vsmul(input, 1, coefficients, inputBuffer.advancedBy(2), 1, vDSP_Length(count)

// outputBuffer[n] = inputBuffer[n] - inputBuffer[n - 2]
vDSP_vsub(inputBuffer.advancedBy(2), 1, inputBuffer, 1, outputBuffer.advancedBy(2), 1, vDSP_Length(count))

for n in 2 ..< initedBufferLength {
    outputBuffer[n] -= a1 * outputBuffer[n - 1] + a2 * outputBuffer[n - 2]
}

// outputBuffer now contains the y values from the first loop, perform work on them

由于某种原因,这比第一个解决方案慢约75%,即使执行的工作(在存储器访问,乘法,减法等方面)应该相同。实际上,x[n - 1]x[n - 2]的值在循环的每次迭代中都没有被改组,因此整体上应该执行的工作量会减少。

“仪器”中的任何内容都没有让我知道可能需要花费多长时间,向量中的违规行&#39;优化&#39; (但实际上速度较慢)版本是for循环,占用了函数CPU时间的90%左右。鉴于这个循环比第一个循环更简单(整体术语更少,乘法减少一个),我不明白为什么它会变慢。

问题是,如何将循环矢量化更快?从理论上讲,我的版本应该比Apple的vDSP_deq22更快,因为我可以分解一些术语,而他们必须总是假设输入系数方面的最坏情况。

0 个答案:

没有答案