I’m using buffer
in many other vDSP operations before the code below. To those operations it is naturally a contiguous piece of memory, and that’s handy as there are vastly reduced number of calls to vDSP.
// […] many operations with buffer here
var buffer = UnsafeMutableBufferPointer<Double>.allocate(capacity: 10000000)
var sortedIndices: UnsafeMutablePointer<UInt> = UnsafeMutablePointer<UInt>.allocate(capacity: 10000000)
for (indexIndices, indexC) in buffer.indices.enumerated() {
sortedIndices[indexIndices] = UInt(indexC)
}
vDSP_vsortiD(
UnsafePointer<Double>(buffer.baseAddress)!,
sortedIndices,
nil,
UInt(buffer.count),
vDSP.SortOrder.descending.rawValue
)
I need to sort buffer
sliced up. Also, I’d like not to copy its contents before every sort into smaller buffers as this is performance critical (as many times as possible per second).
vDSP_vsortiD()
and vDSP_vsorti()
do not support Slice
.
For example this code is not possible since .baseAddress
doesn’t exist on a Slice
and I guess UnsafePointer
doesn’t understand Slice
either.
var sliceOfBuffer: Slice<UnsafeMutableBufferPointer<Double>> = buffer[0...30000]
vDSP_vsortiD(
UnsafePointer<Double>(sliceOfBuffer.baseAddress)!, // This fails
sortedIndices[0...30000], // This also fails
nil,
UInt(30000),
vDSP.SortOrder.descending.rawValue
)
// Is there another sort indices function which accepts those types as params?
Many operations under vDSP
accept Slice
, however the above sort functions don’t.
Swift v5.6
CodePudding user response:
You can use withUnsafeBufferPointer()
to pass the address of the slice's element storage to the vDSP function:
sliceOfBuffer.withUnsafeBufferPointer { bp in
vDSP_vsortiD(
bp.baseAddress!,
sortedIndices,
nil,
UInt(sliceOfBuffer.count),
vDSP.SortOrder.descending.rawValue
)
}
With respect to slicing the indices: It seems that one can pass sortedIndices sliceOfBuffer.startIndex
to the vDSP function. However, the documentation states that the indices must be initialized starting at zero, so I would not rely on that.
CodePudding user response:
Given that it's a C API, it's less error prone to just think a little more in C terms. Forget slices, just use pointers.
In C it would look something like this:
double *buffer = (double *)calloc(size(double), 10000000);
vDSP_Length *sortedIndices = (vDSP_Length*) calloc(size(vDSP_Length), 10000000);
vDSP_vsortiD(
buffer,
sortedIndices,
NULL,
(vDSP_Length)30000,
vDSP_SortOrder_descending // Probably a different name in reality
);
And for sorting in the middle of the buffer it would look like this:
const vDSP_Length sortOffset = 4000;
const vDSP_Length sortCount = 10000;
vDSP_vsortiD(
buffer sortOffset,
sortedIndices sortOffset,
NULL,
sortCount,
vDSP_SortOrder_descending // Probably a different name in reality
);
In Swift the spelling of how you do that is a little different, but you do exactly the same thing. You have an UnsafeMutableBufferPointer
, so you're on the right track that you need to use its baseAddress
, but you don't need to do anything else to it. UnsafeMutablePointer
types can be passed to UnsafePointer
parameters for C calls (but not the other way around). sortedIndices
is already a pointer rather than a buffer pointer, so you can just use it directly.
vDSP_vsortiD(
buffer.baseAddress!,
sortedIndices,
nil,
vDSP_Length(30000),
vDSP.SortOrder.descending.rawValue
)
If instead, you want to sort somewhere in the middle of your buffer:
// index range of 4000..<14_000
let startOfSlice = 4000
let lengthOfSlice = 10_000
vDSP_vsortiD(
buffer.baseAddress!.advanced(by: startOfSlice),
sortedIndices.advanced(by: startOfSlice),
nil,
vDSP_Length(lengthOfSlice),
vDSP.SortOrder.descending.rawValue
)