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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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
>>>>>>> <[email protected]> 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
>>>>>>> [email protected]
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> swift-users mailing list
>>>>>>> [email protected]
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Rick Mann
>>>>> [email protected]
>>>>>
>>>>>
>>>>
>>>
>>>
>>> --
>>> Rick Mann
>>> [email protected]
>>>
>>>
>>
>
>
> --
> Rick Mann
> [email protected]
>
>
_______________________________________________
swift-users mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-users