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