在 Swift 中使用 Accelerate Framework 复数支持

Posted

技术标签:

【中文标题】在 Swift 中使用 Accelerate Framework 复数支持【英文标题】:Using Accelerate Framework complex number support in Swift 【发布时间】:2016-01-20 10:56:09 【问题描述】:

我需要在 Swift 中使用 Accelerate Multiply、Complex Conjugate 和 Exp 执行以下操作。我已经使用Complex Swift code by dankogai 完成了这项工作,但这对于我正在做的工作来说太慢了。我在使用 Accelerate 框架创建工作版本时遇到了问题,我希望能加快我的理解。

Swift 代码

let frequencyShift = (2 * M_PI * Double(self.centralFrequency) * delays).i / Double(self.samplingFrequencyHertz)
let result = conj(exp(frequencyShift))

delays 是一个包含大约 200k 双精度的数组,这些行将被调用几百次。我将它们转换为 Swift Complex 风格的复数,然后在结果上调用复数 exp() 和 conj() 方法。

相乘

双精度复数向量标量乘法。

vDSP_zvzsmlD

共轭

复向量共轭;双精度。

vDSP_zvconjD

Accelerate 中是否有 exp() 等效项?您将如何重新组织此代码以执行等效的 Accelerate 版本的操作?

【问题讨论】:

【参考方案1】:

在 Swift 中使用 Accelerate 的一般原则

我发现首先尝试将您的代码从使用 for 循环的幼稚实现转换为映射很有帮助。如果您的代码被构造为使用地图,那么切换到加速变得非常容易,因为您已经解决了将算法结构化为矢量化操作的问题。

for 循环映射

let array: [Int] = [1, 2, 3, 4]
let array2 = [Int]()
for value in array 
    array2 = value * 2


let array: [Int] = [1, 2, 3, 4]
array.map( (value: Int) -> Int in
    return value * 2
)

在相等大小的数组上操作版本

如果你发现要枚举两个或多个相同大小的数组那么上面可以结合map和enumerate

let alphas: [Double] = betas.enumerate().map( (index: Int, beta: Double) -> Double in
    return beta * phis[index]
)

设置用于 Accelerate 的数组

使用 Accelerate 的方式并不总是很明显,特别是 UnsafePointerUnsafeMutablePointer 语法。这基本上是不必要的。

var alphaLowers = [Double](count: elementDelays.count, repeatedValue: 0)
vDSP_vmulD(&alphas, 1, &x_ns, 1, &alphaLowers, 1, UInt(elementDelays.count))

所以 Swift 避免了 malloc 和 free 的麻烦,它允许您创建一个自动内存管理对象,然后简单地将它与 & 号一起传递。我提到这一点是因为它避免尝试将对象包装在 UnsafeMutablePointer<Double>(alphaLowers) 中。

复数

我想做的大部分事情都依赖于涉及复数的运算。因此,要创建一个可以在 Accelerate 中使用的对象,您可以尝试以下操作。

var reals = [Double](count: 100, repeatedValue: 0)
var imaginaries = [Double](count: 100, repeatedValue: 0)
var complexNumbers = DSPDoubleSplitComplex(realp: &reals, imagp: &imaginaries)

复杂的exp()

我没有找到与 exp 等效的 Accelerate,但您可以使用欧拉方法分解这些值并对实数和虚数执行必要的操作,如下所示,使用 Complex Swift 库。

public func exp<T:RealType>(z:Complex<T>) -> Complex<T> 
    let r = T.exp(z.re)
    let a = z.im
    return Complex(r * T.cos(a), r * T.sin(a))

我还没有找到避免这个问题的好方法,所以我实际上是在不使用 Accelerate 的情况下执行此步骤。只需将虚部取反即可计算复共轭。

【讨论】:

哇,Swift 中 malloc/free 的消除值得了解,我很高兴我加入了这个线程!

以上是关于在 Swift 中使用 Accelerate Framework 复数支持的主要内容,如果未能解决你的问题,请参考以下文章

使用 Swift 中的 Accelerate 框架来自 AVAudioPCMBuffer 的频谱图

Swift SIMD 或 Accelerate Sum UInt32

为啥有时 Apple Accelerate 框架很慢?

如何使用 vDSP / Accelerate in swift for iOS 计算向量元素的平方根

来自 UIImage 或来自文件的 vImage - Swift 和 Accelerate 框架

在 Accelerate Framework 中将复数 (__CLPK_complex) 提升为指数