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

Reply via email to