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
> 
> 

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to