我有点回答我自己的问题,尽管我认为这不是最好的解决方案。不太了解 Swift,我认为这可能是我们能做的最好的事情,但是对于 memmove(),强制转换为 UnsafeMutableRawPointer(mutating:) 可能会导致内存分配。必须有一种方法来转换它,否则在我的代码中插入该分配肯定会降低性能。
如果有人知道如何避免转换为 UnsafeMutableRawPointer,请告诉我。
var src: [UInt32] = [1, 2, 3, 4]
var dest: [UInt32] = [0, 0, 0, 0]
let elemSize = MemoryLayout<UInt32>.stride
dest.withUnsafeBytes { (destBuffer: UnsafeRawBufferPointer) in
src.withUnsafeBytes { (srcBuffer: UnsafeRawBufferPointer) in
// memmove requires UnsafeMutableRawPointer but how do we avoid this allocation?
// maybe Swift optimizes this somehow but it looks really bad from here
let destPtr = UnsafeMutableRawPointer(mutating: destBuffer.baseAddress)
let destOffset = destPtr! + elemSize
let srcOffset = srcBuffer.baseAddress! + 0
// copy 2 elements from src[0] to dest[1]
memmove(destOffset, srcOffset, elemSize * 2)
}
}
print(dest) // [0, 1, 2, 0]
编辑 1: 现在越来越近了。 replaceSubrange() 显然比 memmove() 慢,实际上慢了大约 6-7 倍。相比之下,字节数越小,replaceSubrange() 就越快。在一个真实的例子中,在执行所有 memmove() 调用之前,您只需获取一次数组字节,因此在实践中它甚至比这更快。
替换子范围:0.750978946685791
内存移动:0.139282941818237
func TestMemmove() {
var src: [UInt32] = Array(repeating: 1, count: 1000)
var dest: [UInt32] = Array(repeating: 0, count: 1000)
let elemSize = MemoryLayout<UInt32>.stride
let testCycles = 100000
let rows = 200
var startTime = CFAbsoluteTimeGetCurrent()
for _ in 0..<testCycles {
dest.replaceSubrange(1...1+rows, with: src[0...rows])
}
var endTime = CFAbsoluteTimeGetCurrent()
print("replaceSubrange: \(endTime - startTime)")
startTime = CFAbsoluteTimeGetCurrent()
for _ in 0..<testCycles {
dest.withUnsafeMutableBytes { destBytes in
src.withUnsafeMutableBytes { srcBytes in
let destOffset = destBytes.baseAddress! + elemSize
let srcOffset = srcBytes.baseAddress! + 0
memmove(destOffset, srcOffset, elemSize * rows)
}
}
}
endTime = CFAbsoluteTimeGetCurrent()
print("memmove: \(endTime - startTime)")
}
编辑 2: 毕竟这种愚蠢只是从 c 调用 memmove 是最快的。 Swift 会将指向数组第一个元素的指针传递给 c 函数,您可以使用 c 中的指针算法来处理在 Swift 中需要 .withUnsafeXXX 调用的偏移量(这可能分配了一些类包装器)。
结论是 Swift 很慢,所以用任何对性能敏感的代码来修补 c。
方块移动:0.0957469940185547
替换子范围:1.89903497695923
内存移动:0.136561989784241
// from .c file bridged to Swift
void BlockMove (void* dest, int destOffset, const void* src, int srcOffset, size_t count) {
memmove(dest + destOffset, src + srcOffset, count);
}
func TestMemmove() {
var src: [UInt32] = Array(repeating: 1, count: 1000)
var dest: [UInt32] = Array(repeating: 0, count: 1000)
let elemSize = MemoryLayout<UInt32>.stride
let testCycles = 100000
let rows = 500
var startTime: CFAbsoluteTime = 0
var endTime: CFAbsoluteTime = 0
// BlockMove (from c)
startTime = CFAbsoluteTimeGetCurrent()
for _ in 0..<testCycles {
BlockMove(&dest, Int32(elemSize), &src, 0, Int32(elemSize * rows))
}
endTime = CFAbsoluteTimeGetCurrent()
print("BlockMove: \(endTime - startTime)")
// replaceSubrange
startTime = CFAbsoluteTimeGetCurrent()
for _ in 0..<testCycles {
dest.replaceSubrange(1...1+rows, with: src[0...rows])
}
endTime = CFAbsoluteTimeGetCurrent()
print("replaceSubrange: \(endTime - startTime)")
// memmove
startTime = CFAbsoluteTimeGetCurrent()
for _ in 0..<testCycles {
dest.withUnsafeMutableBytes { destBytes in
src.withUnsafeMutableBytes { srcBytes in
let destOffset = destBytes.baseAddress! + elemSize
let srcOffset = srcBytes.baseAddress! + 0
memmove(destOffset, srcOffset, elemSize * rows)
}
}
}
endTime = CFAbsoluteTimeGetCurrent()
print("memmove: \(endTime - startTime)")
}