【问题标题】:vDSP_zrvmul not returning any results (or all zeros)vDSP_zrvmul 不返回任何结果(或全为零)
【发布时间】:2019-09-03 09:59:56
【问题描述】:

按照其他用户 cmets 的说法,我已经整理并压缩了我的代码以使其可读。我有一个 complexFloatArray 类,用于存储复杂向量的数组

class complexFloatArray {
    var reals: [Float]
    var imaginaries: [Float]

    init(reals: [Float], imaginaries: [Float]){
    self.reals = reals
    self.imaginaries = imaginaries
    }
}

然后我在这个类的扩展中定义了一些函数。一个是:

func useAsDSPSplitComplex<R>(_ closure: (inout DSPSplitComplex) -> R) -> R {
    return reals.withUnsafeMutableBufferPointer { realBufferPointer in
        return imaginaries.withUnsafeMutableBufferPointer { imaginaryBufferPointer in
            var dspSplitComplex = DSPSplitComplex(realp: realBufferPointer.baseAddress!, imagp: imaginaryBufferPointer.baseAddress!)
            return closure(&dspSplitComplex)
        }
    }
}

这个想法是当在 complexFloatArray 的实例上调用时,它会创建一个 DSPSplitComplex 指针以用于 Accelerate 框架。

最后我有一个加速函数,我想使用 (vDSP_zrvmul) 将我的复数向量乘以一个实数向量。

func floatMultiply(with other: [Float]) -> complexFloatArray {
    assert(self.count == other.count, "Multiplied Vectors Must have the same size!")

    var result = complexFloatArray.zeros(count: other.count)

    self.useAsDSPSplitComplex { selfPointer in
        result.useAsDSPSplitComplex { resultPointer in
            vDSP_zrvmul(
                &selfPointer, complexFloatArray.stride,
                other, complexFloatArray.stride,
                &resultPointer, complexFloatArray.stride,
                vDSP_Length(result.count))
        }

    }

    return result
}

我使用以下方法调用函数:

var kernel = sine.floatMultiply(with: gauss)

其中 sine 是一个 complexFloatArray,而 gauss 是一个 FloatArray,两者的长度相等以创建一个 morlet 小波。然而,结果是一个用零填充的 complexFloatArray。

调试时,我可以在 floatMultiply 函数中的任意点设置断点,并确认 self 和 other(正弦和高斯)都充满了值。所以它在 vDSP 调用中的某个地方没有返回正确的结果。

为了完整性,complexFloatArray.stride = 1(该值在 complexFloatArray 类中声明)。

'zeros' 是 complexFloatArray 中的一个函数,用于填充具有一定长度的零的数组。 (数组(重复:0,计数:N)

有什么建议为什么我在这次通话中的结果为零?

我现在已经包含了完整的代码,而不是提供不完整图片的 sn-ps。
ComplexFloatArray 类的完整代码如下:

class ComplexFloatArray {
    var reals: [Float]
    var imaginaries: [Float]

    init(reals: [Float], imaginaries: [Float]){
    self.reals = reals
    self.imaginaries = imaginaries
    }
}

extension ComplexFloatArray {
    var count: Int {
    assert(reals.count == imaginaries.count)
    return reals.count
    }

    static let stride = 1

    func append(real: Float, imaginary: Float) {
        self.reals.append(real)
        self.imaginaries.append(imaginary)
    }

    func removeAtIndex(index: Int) {
        self.reals.remove(at: index)
        self.imaginaries.remove(at: index)
    }

    func useAsDSPSplitComplex<R>(_ closure: (inout DSPSplitComplex) -> R) -> R {
    return reals.withUnsafeMutableBufferPointer { realBufferPointer in
        return imaginaries.withUnsafeMutableBufferPointer { imaginaryBufferPointer in
            var dspSplitComplex = DSPSplitComplex(realp: realBufferPointer.baseAddress!, imagp: imaginaryBufferPointer.baseAddress!)
            return closure(&dspSplitComplex)
            }
        }
    }
}

extension ComplexFloatArray {
    convenience init() {
        self.init(reals:[], imaginaries:[])
    }

static func zeros(count: Int) -> ComplexFloatArray {
    return ComplexFloatArray(reals:Array(repeating: 0, count: count), imaginaries: Array(repeating:0, count:count))
    }
}

extension ComplexFloatArray {
    enum ComplexMultiplicationType: Int32 { case normal = 1, conjugate = -1}

    func ComplexMultiply(
        with other: ComplexFloatArray,
        multiplicationType: ComplexMultiplicationType = .normal
    ) -> ComplexFloatArray {
        assert(self.count == other.count, "Multiplied Vectors Must have the same size!")

        var result = ComplexFloatArray.zeros(count: self.count)

        self.useAsDSPSplitComplex { selfPointer in
            other.useAsDSPSplitComplex { otherPointer in
                result.useAsDSPSplitComplex { resultPointer in
                    vDSP_zvmul(
                    &selfPointer, ComplexFloatArray.stride,
                    &otherPointer, ComplexFloatArray.stride,
                    &resultPointer, ComplexFloatArray.stride,
                    vDSP_Length(result.count),
                    multiplicationType.rawValue)
                }
            }
        }

        return result
    }
}


extension ComplexFloatArray {

    func floatMultiply(
        with other: [Float]
        ) -> ComplexFloatArray {
        assert(self.count == other.count, "Multiplied Vectors Must have the same size!")

        var result = ComplexFloatArray.zeros(count: other.count)

        self.useAsDSPSplitComplex { selfPointer in
            result.useAsDSPSplitComplex { resultPointer in
                vDSP_zrvmul(
                    &selfPointer, ComplexFloatArray.stride,
                    other, ComplexFloatArray.stride,
                    &resultPointer, ComplexFloatArray.stride,
                    vDSP_Length(result.count))
            }
         }

        return result
    }
 }

extension ComplexFloatArray {
enum FourierTransformDirection: Int32  { case forward = 1, inverse = -1 }

    func outOfPlaceComplexFourierTransform(
        setup: FFTSetup,
        resultSize:Int,
        logSize: UInt,
        direction: FourierTransformDirection) -> ComplexFloatArray {

        var result = ComplexFloatArray.zeros(count:resultSize)

        self.useAsDSPSplitComplex { selfPointer in
            result.useAsDSPSplitComplex { resultPointer in
                vDSP_fft_zop(
                setup,
                &selfPointer,
                ComplexFloatArray.stride,
                &resultPointer,
                ComplexFloatArray.stride,
                logSize,
                direction.rawValue)
            }
        }
        return result
    }
}

我的 CWT 类的完整代码如下:

var nVoices = 96            //number of voices per octave
var kernelLength = 2048     //Length of N
var fs = globalSampleRate


class CWT{

    var timeArray:[Float] = []
    var sines: [ComplexFloatArray] = []
    var gaussian:[[Float]] = []
    var fftFilterBank:[ComplexFloatArray] = []
    var filterBank:[ComplexFloatArray] = []
    var convProduct:[ComplexFloatArray] = []
    var centreFreqs:[Float]=[]
    var phase:[Float] = []
    var magnitude:[Float] = []


    func synthesizeKernels(){

        timeArray = makeArray(from: ((1.0/Float(fs))*((-0.5)*Float(kernelLength))), to: ((1.0/Float(fs))*((0.5)*Float(kernelLength))), increment: 1/fs)

         centreFreqs = getCentreFreqs(N:timeArray.count)

         for i in 0..<centreFreqs.count {
            makeSine(freq: centreFreqs[i], N:timeArray.count, iteration: i)
            makeGaus(freq: centreFreqs[i], N:timeArray.count, iteration: i)
            makeMorlet(sine: sines[i], gauss: gaussian[i], count:timeArray.count, iteration: i)
            fftKernel(kernel: filterBank[i], N:timeArray.count, iteration:i)
        }
    }



    func convolveSignal(realSamples:[Float], imagSamples:[Float]) {

        let logN = 11
        let fft1Setup = vDSP_create_fftsetup(UInt(logN), FFTRadix(FFT_RADIX2))!
        var product = ComplexFloatArray.zeros(count: filterBank.count)
        var input = ComplexFloatArray(reals: realSamples, imaginaries: imagSamples)
        var fftOfSamples = ComplexFloatArray.zeros(count: input.count)
        fftOfSamples = input.outOfPlaceComplexFourierTransform(setup: fft1Setup, resultSize: input.count, logSize: UInt(logN), direction: ComplexFloatArray.FourierTransformDirection(rawValue: 1)!)

        fftOfSamples.removeAtIndex(index: 0)


        for i in 0..<self.filterBank.count {
            var kernel = fftFilterBank[i]
            var multiplyResult = kernel.ComplexMultiply(with: fftOfSamples)
            convProduct.append(multiplyResult)
        }
   }



    //HELPER FUNCTION FOR TIME ARRAY
    func makeArray(from:Float, to:Float, increment:Float) ->[Float]{
        var Array:[Float]=[]
        for i in stride(from: from, to: to, by: increment) {
            Array.append(i)
        }
        return Array
    }


    //MAKE COMPLEX SINE WAVE
    func makeSine(freq:Float, N:Int, iteration:Int) {
        var compSine = ComplexFloatArray.init()
        for i in 0..<timeArray.count{
            let x = 2 * Float.pi * freq * timeArray[i]
            compSine.append(real: cos(x), imaginary: sin(x))
        }
        sines.append(compSine)
    }



    //MAKE GAUSSIAN WINDOW
    func makeGaus(freq:Float, N:Int, iteration:Int) {
        var gaus:[Float] = Array(repeating:0, count:N)
        let s:Float = 7 / (2.0 * Float.pi * freq)
        let interimCalc: Float = Float(2)*Float(pow(s,2))
        for i in 0..<N{
            var u = pow(timeArray[i],2)
            u = (-u)
            let v = u / interimCalc
            gaus[i] = exp(v)
        }
        gaussian.append(gaus)

    }


    //CREATE CENTRE FREQUENCIES
    func getCentreFreqs(N:Int) ->[Float]{
        var CF:[Float] = []
        var filteredCF:[Float] = []
        var G:Float = pow(10,(3/10))
        var x = makeArray(from: -1000, to: 1350, increment: 1)

        for i in 0..<x.count {
            var fraction:Float = (Float(2*Float(x[i]))-Float(59.0)) / Float(2*nVoices)
            var fr:Float = Float(1000.0) * Float(powf(Float(G), Float(fraction)))
            CF.append(fr)
        }

        for i in 0..<CF.count {
            if (Float(20) < CF[i] && CF[i] < Float(20000))  {
                filteredCF.append(CF[i])
            }
        }
        return filteredCF
    }


    //MAKE COMPLEX MORLET WAVELET
    func makeMorlet(sine:ComplexFloatArray, gauss:[Float], count:Int, iteration:Int) {
        var kernel = sine.floatMultiply(with: gauss)
        filterBank.append(kernel)
    }


    //PERFORM FFT ON KERNEL
    func fftKernel(kernel: ComplexFloatArray, N:Int, iteration:Int) {
        var size = kernel.count
        var logSize = 11
        var FFTSetup = vDSP_create_fftsetup(vDSP_Length(logSize), FFTRadix(FFT_RADIX2))
        var output = kernel.outOfPlaceComplexFourierTransform(setup: FFTSetup!, resultSize: size, logSize: UInt(logSize), direction: ComplexFloatArray.FourierTransformDirection(rawValue: 1)!)
        output.removeAtIndex(index:0)
        fftFilterBank.append(output)

    }


    //Test Signal to Convolve - 1kHz Sine Wave
    func testSine(){
        var testTimeArray = makeArray(from: ((1.0/Float(fs))*((-0.5)*Float(kernelLength))), to: ((1.0/Float(fs))*((0.5)*Float(kernelLength))), increment: 1/fs)
        var testSine = ComplexFloatArray.zeros(count: testTimeArray.count)

        for i in 0..<testTimeArray.count{
            var x = 2 * Float.pi * 1000 * testTimeArray[i]
            testSine.reals[i] = cos(x)
            testSine.imaginaries[i] = sin(x)
        }
        convolveSignal(realSamples: testSine.reals, imagSamples:testSine.imaginaries)

    }
}

最后在我的 ViewController 类中,我有以下内容:

class ViewController: UIViewController {

    var wavelet = CWT()

    func viewDidLoad(){
        wavelet.synthesizeKernels()
        wavelet.testSine()
    }
}

如果我对此进行调试并暂停 makeMorlet 函数,FloatMultiply 的结果全为零,尽管等式左右两侧的长度值相同。

【问题讨论】:

  • 我无法用您显示的代码重现相同的问题。您对zeros(count:) 的实现可能是错误的,或者您已经过多地简化了代码,并且显示的代码已经从您的实际代码中丢失了一些重要的东西。无论如何,请尝试创建一个重现相同问题并显示整个代码而不隐藏任何行的项目。
  • 顺便说一句,使用 UpperCamelCase 作为类型名称是很常规的,就像我最初实现 ComplexFloatArray 时所做的那样。这使得将它们与保存该类型实例的变量区分开来很容易。例如。 let complexFloatArray = ComplexFloatArray()
  • @OOPer 你知道使用other: [Float] 作为UnsafePointer&lt;Float&gt; 类型参数的参数是否有效?我不太了解 Swift 在数组和指针类型之间的桥接。
  • @Alexander:是的,它传递了一个指向(连续)元素存储的指针。唯一要记住的是,该指针只保证在函数调用期间有效。 – 也在此处讨论和澄清:forums.swift.org/t/….
  • 像往常一样,需要minimal reproducible example :)

标签: swift pointers accelerate-framework vdsp


【解决方案1】:

很遗憾,您的代码无法在 Xcode 10.2 上以默认设置运行。

线程1:同时访问0x600001170550,但修改需要独占访问

我不确定您是否将 Exclusive Access to Memory 设置为关闭(仅限编译时强制),或使用某些旧版本的 Xcode,但 Swift 编译器会优化并生成代码,假设 Exclusivity Enforcement 为完全有效。 (因此,您永远不应关闭对内存的独占访问。)

请仔细阅读这篇文章:

Swift 5 Exclusivity Enforcement

您在ComplexFloatArray 中的count 实施违反了此规定。在执行传递给reals.withUnsafeMutableBufferPointer 的闭包时,您无法访问reals,因为该数组已被该方法独占。

如果违反此规则,Swift 运行时可能会出现任何类型的意外行为。

尝试更改count 的实现,如下所示,看看会发生什么:

class ComplexFloatArray {
    var reals: [Float]
    var imaginaries: [Float]

    init(reals: [Float], imaginaries: [Float]){
        self.reals = reals
        self.imaginaries = imaginaries

        assert(reals.count == imaginaries.count)
        self.count = reals.count
    }

    //Make `count` a stored property.
    var count: Int
}

extension ComplexFloatArray {
    //Remove this computed property.
//    var count: Int {
//        assert(reals.count == imaginaries.count)
//        return reals.count
//    }

    static let stride = 1

    func append(real: Float, imaginary: Float) {
        self.reals.append(real)
        self.imaginaries.append(imaginary)

        count += 1
    }

    func removeAtIndex(index: Int) {
        self.reals.remove(at: index)
        self.imaginaries.remove(at: index)

        count -= 1
    }

    //...    
}

另外,您的代码会生成许多带有推荐设置的警告,您最好不要忽略它们。

【讨论】:

  • 我刚刚阅读并检查过,您是正确的,内存的独占访问设置为关闭,尽管我不记得这样做了,即使我刚刚创建了一个新项目。我已按照您的建议更正并实施了计数,并且它工作正常。
  • @samp17,排他性强制执行规则是新的并且非常强大,以至于默认设置可能会受到其他一些因素的影响。当我发现一些与排他性执法相关的问题时,我会考虑这种可能性。不管怎样,很高兴听到你成功了。
猜你喜欢
  • 2018-05-01
  • 1970-01-01
  • 2017-12-28
  • 2017-12-09
  • 2020-10-03
  • 2017-03-19
  • 2017-05-20
  • 2012-08-08
  • 1970-01-01
相关资源
最近更新 更多