Usage of UnsafePointer in an example from the Accelerate framework

The article Controlling vDSP Operations with Stride says in the “Use a Negative Stride” section:

To use a negative stride, create a pointer to the last element in the array by initializing an UnsafePointer that's advanced by the number of elements less one.

and gives the following example:

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)

let strideA = vDSP_Stride(-1)
let strideB = vDSP_Stride(1)
let strideC = vDSP_Stride(1)

vDSP_vadd(UnsafePointer(a).advanced(by: a.count - 1), strideA,
          b, strideB, &c, strideC, n)

I wonder if/why this use of UnsafePointer(a) is correct. The documentation states that

... You are responsible for handling the life cycle of any memory you work with through unsafe pointers to avoid leaks or undefined behavior.

I thought that passing a to UnsafePointer(_ other: UnsafePointer<Float>) creates a temporary pointer to the element storage, and that might be invalid after the init method returns.

Would it better/correct to use a.withUnsafeBufferPointer(...) instead?

Thanks, I reported this to the Accelerate team (they're mostly not on the Swift forums, so a feedback report is usually the easier way to get them with this sort of thing, even if it's more opaque).

1 Like

@scanon Ah, I just filed rdar://57973764

1 Like

The good news at least is that in Swift 5.2, you'll get a warning for this:

warning: initialization of 'UnsafePointer<Float>' results in a dangling pointer 
vDSP_vadd(UnsafePointer(a).advanced(by: a.count - 1), strideA,
          ^~~~~~~~~~~~~~~~
note: implicit argument conversion from '[Float]' to 'UnsafePointer<Float>' produces a pointer valid only for the duration of the call to 'init(_:)'
vDSP_vadd(UnsafePointer(a).advanced(by: a.count - 1), strideA,
                        ^
note: use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope 
vDSP_vadd(UnsafePointer(a).advanced(by: a.count - 1), strideA,
                        ^
2 Likes

I’m a bit surprised that the Swift overlay for vDSP doesn’t have its functions take a stride parameter (defaulted to 1), and automatically go in reverse when the stride is negative.

1 Like

In practice the use of this is extremely niche, and it's accessible by using the vDSP C API for anyone who needs it. It doesn't seem to justify the complexity it would add to the API surface. If you (or anyone else) has a real need for it, it's a relatively straightforward feature request.

Hi! In most cases, non-unit stride means the function isn't vectorized, so stride was omitted from the Swift overlay to ensure users didn't experience potential performance issues. The main use case for a stride of -1 is the vDSP convolution function, and the Swift overlay hides that requirement: https://github.com/apple/swift/blob/master/stdlib/public/Darwin/Accelerate/vDSP_Convolution.swift#L53

3 Likes