将 ArraySlice 与 Accelerate 函数一起使用的更好方法?
Better way to use ArraySlice with Accelerate functions?
我有一些非常大的数组,我必须对其执行数百万次计算。在 Objective-C 中,数组将存储为 NSData,我会将它们抽象为 C 数组,以便在(求和、加等)上使用 Accelerate 函数。但是,(考虑到到处使用指针的明显问题)我喜欢更多地使用边界检查 Swift 数组已经内置。因此,我可以使用嵌套的 withUnsafeBufferPointer 来处理两个数组。
func mult(_ x: ArraySlice<Double>, _ y: ArraySlice<Double>) -> [Double] {
assert(x.count == y.count)
var results = [Double](repeating:0, count: x.count)
x.withUnsafeBufferPointer({xBuffer in
y.withUnsafeBufferPointer({yBuffer in
vDSP_vmulD([Double](xBuffer), 1, [Double](yBuffer), 1, &results, 1, vDSP_Length(xBuffer.count))
})
})
return results
}
var testArray = [Double]([0,1,2,3,4,5,6,7,8,9,10])
var testArray2 = [Double]([2,2,2,2,2,2,2,2,2,2,2])
let results = mult(testArray[5...10], testArray2[5...10])
print("\(results)")
首先,当编译器已经知道如何转换 [Double] 本身时,将指针重铸为预期类型似乎很奇怪(在块内传递的指针是 UnsafeBufferPointer<Double>
类型,而vDSP 函数需要 UnsafePointer<Double>
(同样,如果我将数组变量本身传递给它也没有任何抱怨)。其次,必须嵌套 withUnsafeBufferPointer
看起来很奇怪,尽管我理解其用法。最后,如果我使用 ArraySlice<Double>
作为输入参数类型,那么我无法将该函数泛化为 Double 数组和该数组的一部分。
有更好的方法吗?
重铸确实是个问题,它创建了一个全新的数组。为避免它,您可以使用 UnsafeBuffer
的 baseAddress
属性(并将其解包在 Swift 3)
嵌套withUnsafeBufferPointer
确实是正确的,无法避免(据我所知)。缓冲区指针仅在闭包内有效。
您可以为此创建协议
总而言之,这是您的代码,其中包含以下更改:
import Accelerate
protocol ArrayType {
associatedtype Element
var count : Int { get }
func withUnsafeBufferPointer<R>(_ body: @noescape (UnsafeBufferPointer<Element>) throws -> R) rethrows -> R
}
extension Array : ArrayType {}
extension ArraySlice : ArrayType {}
extension ContiguousArray : ArrayType {}
func mult<A : ArrayType where A.Element == Double>(x: A, y: A) -> [Double] {
assert(x.count == y.count)
var result = [Double](repeating: 0, count: x.count)
x.withUnsafeBufferPointer { x in
y.withUnsafeBufferPointer { y in
vDSP_vmulD(x.baseAddress!, 1, y.baseAddress!, 1, &result, 1, vDSP_Length(x.count))
}
}
return result
}
var testArray1 : [Double] = [0,1,2,3,4,5,6,7,8,9,10]
var testArray2 : [Double] = [2,2,2,2,2,2,2,2,2,2,2]
let results = mult(x: testArray1[5...10], y: testArray2[5...10])
print("\(results)")
强制解包会很好,因为三种一致的类型永远不会给你一个空指针。
我有一些非常大的数组,我必须对其执行数百万次计算。在 Objective-C 中,数组将存储为 NSData,我会将它们抽象为 C 数组,以便在(求和、加等)上使用 Accelerate 函数。但是,(考虑到到处使用指针的明显问题)我喜欢更多地使用边界检查 Swift 数组已经内置。因此,我可以使用嵌套的 withUnsafeBufferPointer 来处理两个数组。
func mult(_ x: ArraySlice<Double>, _ y: ArraySlice<Double>) -> [Double] {
assert(x.count == y.count)
var results = [Double](repeating:0, count: x.count)
x.withUnsafeBufferPointer({xBuffer in
y.withUnsafeBufferPointer({yBuffer in
vDSP_vmulD([Double](xBuffer), 1, [Double](yBuffer), 1, &results, 1, vDSP_Length(xBuffer.count))
})
})
return results
}
var testArray = [Double]([0,1,2,3,4,5,6,7,8,9,10])
var testArray2 = [Double]([2,2,2,2,2,2,2,2,2,2,2])
let results = mult(testArray[5...10], testArray2[5...10])
print("\(results)")
首先,当编译器已经知道如何转换 [Double] 本身时,将指针重铸为预期类型似乎很奇怪(在块内传递的指针是 UnsafeBufferPointer<Double>
类型,而vDSP 函数需要 UnsafePointer<Double>
(同样,如果我将数组变量本身传递给它也没有任何抱怨)。其次,必须嵌套 withUnsafeBufferPointer
看起来很奇怪,尽管我理解其用法。最后,如果我使用 ArraySlice<Double>
作为输入参数类型,那么我无法将该函数泛化为 Double 数组和该数组的一部分。
有更好的方法吗?
重铸确实是个问题,它创建了一个全新的数组。为避免它,您可以使用
UnsafeBuffer
的baseAddress
属性(并将其解包在 Swift 3)嵌套
withUnsafeBufferPointer
确实是正确的,无法避免(据我所知)。缓冲区指针仅在闭包内有效。您可以为此创建协议
总而言之,这是您的代码,其中包含以下更改:
import Accelerate
protocol ArrayType {
associatedtype Element
var count : Int { get }
func withUnsafeBufferPointer<R>(_ body: @noescape (UnsafeBufferPointer<Element>) throws -> R) rethrows -> R
}
extension Array : ArrayType {}
extension ArraySlice : ArrayType {}
extension ContiguousArray : ArrayType {}
func mult<A : ArrayType where A.Element == Double>(x: A, y: A) -> [Double] {
assert(x.count == y.count)
var result = [Double](repeating: 0, count: x.count)
x.withUnsafeBufferPointer { x in
y.withUnsafeBufferPointer { y in
vDSP_vmulD(x.baseAddress!, 1, y.baseAddress!, 1, &result, 1, vDSP_Length(x.count))
}
}
return result
}
var testArray1 : [Double] = [0,1,2,3,4,5,6,7,8,9,10]
var testArray2 : [Double] = [2,2,2,2,2,2,2,2,2,2,2]
let results = mult(x: testArray1[5...10], y: testArray2[5...10])
print("\(results)")
强制解包会很好,因为三种一致的类型永远不会给你一个空指针。