By the way, even without new Swift 3.1 feature, this works, providing optimized sum function for all three types:
extension ContiguousBufferedArray where Iterator.Element == Double { func sum() -> Double { var result = Double() withUnsafeBufferPointer { vDSP_sveD($0.baseAddress!, 1, &result, numericCast(count)) } return result } } extension ContiguousBufferedArray where Iterator.Element == Float { func sum() -> Float { var result = Float() withUnsafeBufferPointer { vDSP_sve($0.baseAddress!, 1, &result, numericCast(count)) } return result } } > On Nov 22, 2016, at 4:56 PM, Rick Mann <rm...@latencyzero.com> wrote: > > Thanks! It's all very educational, at the least. > > Obviously the ideal would be for LLVM to recognize and optimize (there are > many ways to write the sum of an array), but this is cool. > >> On Nov 22, 2016, at 16:50 , Hooman Mehr <hoo...@mac.com> wrote: >> >> For example, this reduces the six variants of sum to two: >> >> public protocol ContiguousBufferedArray: RandomAccessCollection { >> >> func withUnsafeBufferPointer<R>(_ body: >> (UnsafeBufferPointer<Iterator.Element>) throws -> R) rethrows -> R >> } >> >> extension Array: ContiguousBufferedArray {} >> extension ContiguousArray: ContiguousBufferedArray {} >> extension ArraySlice: ContiguousBufferedArray {} >> >> func sum<A>(_ array: A) -> Double where A: ContiguousBufferedArray, >> A.Iterator.Element == Double { >> var result = Double() >> array.withUnsafeBufferPointer { vDSP_sveD($0.baseAddress!, 1, &result, >> numericCast(array.count)) } >> return result >> } >> >> func sum<A>(_ array: A) -> Float where A: ContiguousBufferedArray, >> A.Iterator.Element == Float { >> var result = Float() >> array.withUnsafeBufferPointer { vDSP_sve($0.baseAddress!, 1, &result, >> numericCast(array.count)) } >> return result >> } >> >> I have to think a bit more to see what common API we can extract from array >> that can be generally useful. I will put up a pic on evolution once I get a >> clearer idea. >> >>> On Nov 22, 2016, at 4:46 PM, Rick Mann <rm...@latencyzero.com> wrote: >>> >>> That sounds interesting. Would you mind making that pitch on >>> swift-evolution? I just barely understood what you said :/ >>> >>>> On Nov 22, 2016, at 07:46 , Hooman Mehr <hoo...@mac.com> wrote: >>>> >>>> It is good to know that >>>> >>>> extension Array where Element == Double { } >>>> >>>> will work pretty soon with Swift 3.1. >>>> >>>> Back to reduce(0,+): >>>> >>>> If we get specialized instance for a reduce(0,+), so that it is known that >>>> “+” is a (Double, Double)->Double function, LLVM’s auto-vectorization >>>> should be able to optimize it for the CPU’s vector unit. In theory, it >>>> should be possible to add additional optimizers to LLVM layer to use other >>>> hardware or numeric libraries for that purpose, but I don’t think it would >>>> be a Swift-specific thing. >>>> >>>> Swift’s generics still has a long way to go. Since they are aiming for ABI >>>> stability by Swift 4.0, and there isn’t much time left, I don’t think many >>>> of the bigger generics improvements fit with the current Swift evolution >>>> discussions, although they could have huge impact on standard library >>>> (hence the ABI stability). >>>> >>>> One thing that might be worth discussing on Swift evolution and can >>>> potentially make it to standard library and Swift 4.0 is adding a common >>>> protocol for array-like types that have (or can have) contiguous buffers >>>> so that manually vectorizing operations on their elements becomes easier >>>> and cleaner. >>>> >>>> At the moment, we can manually define a protocol that extends >>>> RandomAccessCollection and provides `withUnsafeBufferPointer` and then >>>> declare the conformance of all of the standard library array variants to >>>> it so that we can provide a single generic sum global function for summing >>>> all of them using vDSP. This protocol may be worth adding to the standard >>>> library. >>>> >>>>> On Nov 21, 2016, at 7:05 PM, Rick Mann <rm...@latencyzero.com> wrote: >>>>> >>>>> Thanks, Hooman. Is it worth posting on swift-evolution the question about >>>>> specializing something like reduce(0, +) (it's complicated because it >>>>> would mean specializing reduce() based on both the type and the closure >>>>> passed, and that seems like something that would be difficult to specify >>>>> concisely in the syntax). >>>>> >>>>>> On Nov 21, 2016, at 18:29 , Hooman Mehr <hoo...@mac.com> wrote: >>>>>> >>>>>> This is not possible in Swift 3.0. Swift 4.0 will improve things with >>>>>> conditional conformances. >>>>>> >>>>>> For now, the best solution is using global functions instead of >>>>>> extending types or protocols. >>>>>> >>>>>> For example you can do this now: >>>>>> >>>>>> extension Array where Element: FloatingPoint { >>>>>> >>>>>> func sum() -> Element { >>>>>> guard count > 0 else { return 0 } >>>>>> switch self[0] { >>>>>> case is Double: >>>>>> var result = Double() >>>>>> vDSP_sveD(unsafeBitCast(self, to: Array<Double>.self), 1, >>>>>> &result, vDSP_Length(count)) >>>>>> print("vDSP") >>>>>> return unsafeBitCast(result, to: Element.self) >>>>>> case is Float: >>>>>> var result = Float() >>>>>> vDSP_sve(unsafeBitCast(self, to: Array<Float>.self), 1, &result, >>>>>> vDSP_Length(count)) >>>>>> print("vDSP") >>>>>> return unsafeBitCast(result, to: Element.self) >>>>>> default: >>>>>> print("default") >>>>>> return reduce(0, +) >>>>>> } >>>>>> } >>>>>> } >>>>>> >>>>>> But this is not very efficient, especially if it is defined in another >>>>>> module, which limits optimizations. >>>>>> >>>>>> Instead, a family of overloaded global functions gives you the most >>>>>> coverage and best performance, at the expense of repetition and >>>>>> boilerplate code: >>>>>> >>>>>> func sum<S: Sequence>(_ sequence: S) -> S.Iterator.Element where >>>>>> S.Iterator.Element: Integer { >>>>>> var result: S.Iterator.Element = 0 >>>>>> for element in sequence { result += element } >>>>>> return result >>>>>> } >>>>>> >>>>>> func sum<S: Sequence>(_ sequence: S) -> S.Iterator.Element where >>>>>> S.Iterator.Element: FloatingPoint { >>>>>> var result: S.Iterator.Element = 0 >>>>>> for element in sequence { result += element } >>>>>> return result >>>>>> } >>>>>> >>>>>> func sum(_ array: Array<Double>) -> Double { >>>>>> var result = Double() >>>>>> vDSP_sveD(array, 1, &result, vDSP_Length(array.count)) >>>>>> return result >>>>>> } >>>>>> >>>>>> func sum(_ array: ContiguousArray<Double>) -> Double { >>>>>> var result = Double() >>>>>> array.withUnsafeBufferPointer { vDSP_sveD($0.baseAddress!, 1, &result, >>>>>> vDSP_Length(array.count)) } >>>>>> return result >>>>>> } >>>>>> >>>>>> func sum(_ array: ArraySlice<Double>) -> Double { >>>>>> var result = Double() >>>>>> array.withUnsafeBufferPointer { vDSP_sveD($0.baseAddress!, 1, &result, >>>>>> vDSP_Length(array.count)) } >>>>>> return result >>>>>> } >>>>>> >>>>>> func sum(_ array: Array<Float>) -> Float { >>>>>> var result = Float() >>>>>> vDSP_sve(array, 1, &result, vDSP_Length(array.count)) >>>>>> return result >>>>>> } >>>>>> >>>>>> func sum(_ array: ContiguousArray<Float>) -> Float { >>>>>> var result = Float() >>>>>> array.withUnsafeBufferPointer { vDSP_sve($0.baseAddress!, 1, &result, >>>>>> vDSP_Length(array.count)) } >>>>>> return result >>>>>> } >>>>>> >>>>>> func sum(_ array: ArraySlice<Float>) -> Float { >>>>>> var result = Float() >>>>>> array.withUnsafeBufferPointer { vDSP_sve($0.baseAddress!, 1, &result, >>>>>> vDSP_Length(array.count)) } >>>>>> return result >>>>>> } >>>>>> >>>>>> The above code covers summing any integer or floating point sequence of >>>>>> numbers, while being accelerated for Float and Double array types >>>>>> (Array, ContiguousArray and ArraySlice) >>>>>> >>>>>> >>>>>>> On Nov 21, 2016, at 4:32 PM, Rick Mann via swift-users >>>>>>> <swift-users@swift.org> wrote: >>>>>>> >>>>>>> My googling is not turning up an answer that works in Xcode 8.1. I'd >>>>>>> like to do this: >>>>>>> >>>>>>> >>>>>>> import Accelerate >>>>>>> >>>>>>> extension >>>>>>> Array >>>>>>> where Element == Double >>>>>>> { >>>>>>> func >>>>>>> sum() >>>>>>> -> Double >>>>>>> { >>>>>>> var result: Double = 0.0 >>>>>>> vDSP_sveD(self, 1, &result, vDSP_Length(self.count)) >>>>>>> return result >>>>>>> } >>>>>>> } >>>>>>> >>>>>>> But I get "same-type requirement makes generic parameter 'Element' >>>>>>> non-generic." >>>>>>> >>>>>>> Also, will there ever be any way to specialize something like >>>>>>> >>>>>>> let numbers: [Double] = ... >>>>>>> let sum = numbers.reduce(0.0, +) >>>>>>> >>>>>>> Into a call to vDSP_sveD()? Would it require compiler optimizations for >>>>>>> Accelerate? >>>>>>> >>>>>>> Thanks! >>>>>>> >>>>>>> -- >>>>>>> Rick Mann >>>>>>> rm...@latencyzero.com >>>>>>> >>>>>>> >>>>>>> _______________________________________________ >>>>>>> swift-users mailing list >>>>>>> swift-users@swift.org >>>>>>> https://lists.swift.org/mailman/listinfo/swift-users >>>>>> >>>>> >>>>> >>>>> -- >>>>> Rick Mann >>>>> rm...@latencyzero.com >>>>> >>>>> >>>> >>> >>> >>> -- >>> Rick Mann >>> rm...@latencyzero.com >>> >>> >> > > > -- > Rick Mann > rm...@latencyzero.com > >
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users