Controlling vDSP operations with stride
Operate selectively on the elements of a vector at regular intervals.
Overview
Many functions in vDSP provide support for specifying a stride (that is, the distance between the elements that functions read from or write to) for a particular vector. For example, if you want to access consecutive elements, use a stride of 1 (referred to as a unit stride). If you want to access every third element, for example to work with the red channel in interleaved RGB data, use a stride of 3. If you want to access every second element, for example to work with a single audio channel in interleaved stereo audio data, use a stride of 2.
Typically, you use a unit stride. Use other strides to, for example, operate along a column of a matrix, where the stride is the number of elements per row.
Use a unit stride for the best performance and energy efficiency
For most of the functions in vDSP, you obtain the best performance when the stride is 1. Any other stride value generally prevents the use of vectorized code, and reduces both performance and energy efficiency.
The major exception to this limitation is in functions that support the use of interleaved complex data, such as vDSP_ctoz and vDSP_ztoc. In these cases, use a stride of 2.
Set the stride for each vector independently
The code below calls the vDSP_vadd function to add each element in array a to the corresponding element in array b, and write the result to array c. Note that the stride used for each array is 1.
let strideA = vDSP_Stride(1)
let strideB = vDSP_Stride(1)
let strideC = vDSP_Stride(1)
let a: [Float] = [10, 20, 30, 40, 50, 60, 70, 80]
let b: [Float] = [ 1, 2, 3, 4, 5, 6, 7, 8]
let n = vDSP_Length(a.count)
var c = [Float](repeating: .nan,
count: a.count)
vDSP_vadd(a, strideA,
b, strideB,
&c, strideC,
n)In this example, the result is [11.0, 22.0, 33.0, 44.0, 55.0, 66.0, 77.0, 88.0].
[Image]
Use a nonunit stride on inputs
A nonunit stride allows you to, for example, access a particular color channel in interleaved RGB data. If you change strideB to 3, the operation adds the first, fourth, and seventh items in array b to the first, second, and third items in array a.
let strideA = vDSP_Stride(1)
let strideB = vDSP_Stride(3)
let strideC = vDSP_Stride(1)
...
let n = vDSP_Length(3)[Image]
Note that vDSP operations always read n elements. Therefore, your collections require at least ((n - 1) * stride) + 1 elements.
Use a nonunit stride on output
If you change array c’s stride to 3, the calculation writes the result to its first, fourth, and seventh items. Using the example of interleaved RGB data discussed in Controlling vDSP operations with stride, this approach would write the result of an operation to the red channel. The example below defines the stride for the input array, a, as 2, so the operation uses the first, third, and fifth elements:
let strideA = vDSP_Stride(2)
let strideB = vDSP_Stride(1)
let strideC = vDSP_Stride(3)
...
let n = vDSP_Length(3)[Image]
Use a negative stride
Use a negative stride to access a vector in reverse order, for example, define the stride as −1 when convolving with a filter using vDSP_conv).
To use a negative stride, pass the vDSP_vadd function a pointer to the address of the last element in the array. The example below shows the Swift code required to reverse the elements in array a:
let strideA = vDSP_Stride(-1)
let strideB = vDSP_Stride(1)
let strideC = vDSP_Stride(1)
...
a.withUnsafeBufferPointer { buffer in
vDSP_vadd(buffer.baseAddress!.advanced(by: buffer.count - 1), strideA,
b, strideB,
&c, strideC,
n)
}The result of adding a and b with a stride of -1 for a is [81.0, 72.0, 63.0, 54.0, 45.0, 36.0, 27.0, 18.0]:
[Image]
Controlling vDSP operations with strides
With interleaved complex data, vDSP stores alternating real and imaginary components consecutively. Use a stride of 2 for interleaved complex data, counting the individual component elements rather than counting complex numbers.
For example, use the code below to copy the contents of a DSPSplitComplex structure to an array of DSPComplex values:
var real: [Float] = [10, 20, 30, 40, 50, 60, 70, 80]
var imag: [Float] = [ 1, 2, 3, 4, 5, 6, 7, 8]
let n = real.count
var complex = [DSPComplex](repeating: DSPComplex(),
count: n)
real.withUnsafeMutableBufferPointer { realPtr in
imag.withUnsafeMutableBufferPointer { imagPtr in
var splitComplex = DSPSplitComplex(realp: realPtr.baseAddress!,
imagp: imagPtr.baseAddress!)
let strideSplitComplex = vDSP_Stride(1)
let strideComplex = vDSP_Stride(2)
vDSP_ztoc(&splitComplex, strideSplitComplex,
&complex, strideComplex,
vDSP_Length(n))
}
}On return, complex contains the pairs [10.0 1.0], [20.0 2.0], [30.0 3.0] ... [80.0 8.0].
Conversely, use the example below to copy the values of an array of DSPComplex values to a DSPSplitComplex structure:
vDSP_ctoz(&complex, strideComplex,
&splitComplex, strideSplitComplex,
vDSP_Length(n))