SIMD operation was slow in Swift for blend operation on strided array

Viewed 104

I want to calculate a sign function for strided array (such like vDSP function) in my swift code. To do this, I used simd framework. However, as you can see below code, my code may have a bit redundant. (Actually this code was slower (0.06s) than pure swift implementation (0.03s) in spite of using SIMD!). Specifically, I think SIMD64<Float> Initialization(a) and copying destination pointer(b) are bottle-neck part (See below code).

Note, my comparison for strided array's 10e6 elements.

So, my question is;

  • How should I initialize SIMD64<Float> from strided array (i.e, UnsafePointer<Float> and vDSP_Stride) for the fastest way?
  • How to pass a calculated SIMD64<Float> into UnsafeMutablePointer<Float> with vDSP_Stride?
for (n = 0; n < N; ++n)
   if (A[n*IA] > 0)
       *D[n*ID] = *B;
   else if (A[n*IA] < 0)
       *D[n*ID] = *C;
   else
       *D[n*ID] = 0;

My SIMD code;

import Accelerate
import simd

/**
 Apply sign function for the given single-precision vector.
 
 - Parameters:
    - __A: Single-precision real input vector
    - __IA: Stride for A
    - __B: Pointer to single-precision real input scalar: upper destination
    - __C: Pointer to single-precision real input scalar: lower destination
    - __D: Single-precision real output vector
    -  __ID: Stride for D
    - __N: The number of elements to process
 
 */
func evDSP_sign(_ __A: UnsafePointer<Float>, _ __IA: vDSP_Stride, _ __B: UnsafePointer<Float>, _ __C: UnsafePointer<Float>, _ __D: UnsafeMutablePointer<Float>, _ __ID: vDSP_Stride, _ __N: vDSP_Length){
    var __A = UnsafeMutablePointer(mutating: __A)
    var __D = UnsafeMutablePointer(mutating: __D)
   let strideA = Int(__IA)
   let strideD = Int(__ID)
   
    let (iterations, remaining) = (Int(__N) / 64, Int(__N) % 64)
    let zeros = SIMD64<Float>(repeating: 0)
    for _ in 0..<iterations{
        ///////////////////// (a) ///////////////////////////////////////////
        var a = SIMD64<Float>(__A.pointee, (__A + __IA).pointee, (__A + 2*__IA).pointee, (__A + 3*__IA).pointee, (__A + 4*__IA).pointee, (__A + 5*__IA).pointee, (__A + 6*__IA).pointee, (__A + 7*__IA).pointee, (__A + 8*__IA).pointee, (__A + 9*__IA).pointee, (__A + 10*__IA).pointee, (__A + 11*__IA).pointee, (__A + 12*__IA).pointee, (__A + 13*__IA).pointee, (__A + 14*__IA).pointee, (__A + 15*__IA).pointee, (__A + 16*__IA).pointee, (__A + 17*__IA).pointee, (__A + 18*__IA).pointee, (__A + 19*__IA).pointee, (__A + 20*__IA).pointee, (__A + 21*__IA).pointee, (__A + 22*__IA).pointee, (__A + 23*__IA).pointee, (__A + 24*__IA).pointee, (__A + 25*__IA).pointee, (__A + 26*__IA).pointee, (__A + 27*__IA).pointee, (__A + 28*__IA).pointee, (__A + 29*__IA).pointee, (__A + 30*__IA).pointee, (__A + 31*__IA).pointee, (__A + 32*__IA).pointee, (__A + 33*__IA).pointee, (__A + 34*__IA).pointee, (__A + 35*__IA).pointee, (__A + 36*__IA).pointee, (__A + 37*__IA).pointee, (__A + 38*__IA).pointee, (__A + 39*__IA).pointee, (__A + 40*__IA).pointee, (__A + 41*__IA).pointee, (__A + 42*__IA).pointee, (__A + 43*__IA).pointee, (__A + 44*__IA).pointee, (__A + 45*__IA).pointee, (__A + 46*__IA).pointee, (__A + 47*__IA).pointee, (__A + 48*__IA).pointee, (__A + 49*__IA).pointee, (__A + 50*__IA).pointee, (__A + 51*__IA).pointee, (__A + 52*__IA).pointee, (__A + 53*__IA).pointee, (__A + 54*__IA).pointee, (__A + 55*__IA).pointee, (__A + 56*__IA).pointee, (__A + 57*__IA).pointee, (__A + 58*__IA).pointee, (__A + 59*__IA).pointee, (__A + 60*__IA).pointee, (__A + 61*__IA).pointee, (__A + 62*__IA).pointee, (__A + 63*__IA).pointee)
        
        a.replace(with: __B.pointee, where: a .> zeros)
        a.replace(with: __C.pointee, where: a .< zeros)
       
       ///////////////////// (b) ///////////////////////////////////////////
       withUnsafePointer(to: &a){
           ptr in
           ptr.withMemoryRebound(to: Float.self, capacity: 64){
               cblas_scopy(Int32(64), $0, Int32(1), __D, Int32(__ID))
           }
       }
       // proceeds offset
       __A += 64*strideA
       __D += 64*strideD

    }
    // remaining
   for _ in 0..<remaining{
       var tmp: Float
       if __A.pointee > 0{
           tmp = __B.pointee
       }
       else if __A.pointee < 0{
           tmp = __C.pointee
       }
       else{
           tmp = .zero
       }
       __D.assign(from: &tmp, count: 1)
       __A += strideA
       __D += strideD
   }
    
}

Pure swift code; Note withContiguousDataUnsafeMPtrT is my method to get a strided array's element.

func _sign<T: MfStorable>(ret: MyStriderdArrayClass, low: T, high: T) -> MyStriderdArrayClass{
            ret.withContiguousDataUnsafeMPtrT(datatype: T.self){
                if $0.pointee > .zero{
                    $0.pointee = high
                }
                else if $0.pointee < .zero{
                    $0.pointee = low
                }
            }
            return ret
        }
0 Answers
Related