我有一个巨大的数组,我只想复制它的中间部分。我正在尝试使用
memcpy
进行表演,但无法找到这样做的方法。下面是一个例子。我们如何为源提供偏移量?
var source: [UInt8] = [1,2,3,4,5,6,7,8,9]
var dest: [UInt8] = [0,0,0,0,0,0,0,0,0]
memcpy(&dest[2], source, 5)
print(dest) // [0, 0, 1, 2, 3, 4, 5, 0, 0]
// This works
var source: [UInt8] = [1,2,3,4,5,6,7,8,9]
var dest: [UInt8] = [0,0,0,0,0,0,0,0,0]
memcpy(&dest[2], &source[2], 5)
print(dest) // [0, 0, 3, 0, 0, 0, 0, 0, 0]
// Only `source[2]` is copied
您可以为此目的使用
replaceSubrange()
:
let source: [UInt8] = [1, 2, 3, 4, 5, 6, 7, 8, 9]
var dest: [UInt8] = [0, 0, 0, 0, 0, 0, 0, 0, 0]
dest.replaceSubrange(2..<(2+5), with: source[2..<(2+5)])
print(dest) // [0, 0, 3, 4, 5, 6, 7, 0, 0]
或者简单地将源切片分配给目标切片:
dest[2..<(2+5)] = source[2..<(2+5)]
print(dest) // [0, 0, 3, 4, 5, 6, 7, 0, 0]
在 MacBook Air M2 上,代码在发布配置中编译。
import Foundation
let N = 1_000_000 // Length of source and destination array
let sourceStart = 50
let destinationStart = 2000
let length = 12345
let source: [UInt8] = Array(repeating: 5, count: N)
for _ in 1...10 {
do {
var dest: [UInt8] = Array(repeating: 0, count: N)
let start = Date()
dest[destinationStart...].withUnsafeMutableBufferPointer { p in
source.copyBytes(to: p, from: sourceStart..<sourceStart + length)
}
let end = Date()
print("copyBytes: ", end.timeIntervalSince(start) * 1000, "ms")
}
do {
var dest: [UInt8] = Array(repeating: 0, count: N)
let start = Date()
source[sourceStart...].withUnsafeBytes { sourcePtr in
dest[destinationStart...].withUnsafeMutableBytes { destPtr in
_ = memcpy(destPtr.baseAddress, sourcePtr.baseAddress, length)
}
}
let end = Date()
print("memcpy: ", end.timeIntervalSince(start) * 1000, "ms")
}
do {
var dest: [UInt8] = Array(repeating: 0, count: N)
let start = Date()
dest.replaceSubrange(destinationStart..<(destinationStart+length),
with: source[sourceStart..<(sourceStart+length)])
let end = Date()
print("replaceSubrange:", end.timeIntervalSince(start) * 1000, "ms")
}
do {
var dest: [UInt8] = Array(repeating: 0, count: N)
let start = Date()
dest[destinationStart..<(destinationStart+length)]
= source[sourceStart..<(sourceStart+length)]
let end = Date()
print("slicing: ", end.timeIntervalSince(start) * 1000, "ms")
}
print()
}
结果:
copyBytes: 0.8690357208251953 ms
memcpy: 0.9009838104248047 ms
replaceSubrange: 0.010013580322265625 ms
slicing: 0.008940696716308594 ms
我建议改用 Swift 指针 API。
处理
UInt8
数组时,可以使用copyBytes
。获取目标数组的一部分(不创建副本),从中获取缓冲区指针,并将源范围复制到该缓冲区。
let destinationStart = 2
let sourceStart = 2
let sourceEnd = 7
dest[destinationOffset...].withUnsafeMutableBufferPointer { p in
source.copyBytes(to: p, from: sourceStart..<sourceEnd)
}
我认为
memcpy
需要取 2 个切片,并从两个切片中获取指针:
source[sourceStart...].withUnsafeBytes { sourcePtr in
dest[destinationStart...].withUnsafeMutableBytes { destPtr in
memcpy(destPtr.baseAddress, sourcePtr.baseAddress, length)
}
}
似乎在工作:
import Foundation
var source: [UInt8] = [1,2,3,4,5,6,7,8,9]
var dest: [UInt8] = [0,0,0,0,0,0,0,0,0]
memcpy(&dest[2], UnsafePointer<UInt8>(&source) + 2, 5)
print(dest) // [0, 0, 3, 4, 5, 6, 7, 0, 0]
并给出一堆警告:
“UnsafePointer”的初始化导致悬空指针
从“[UInt8]”到“UnsafePointer”的隐式参数转换会产生一个指针,该指针仅在调用“init(_:)”期间有效
在 Array 上使用“withUnsafeBufferPointer”方法,以便将参数显式转换为对定义的范围有效的缓冲区指针
withUnsafePointer 摆脱警告:
import Foundation
var source: [UInt8] = [1,2,3,4,5,6,7,8,9]
var dest: [UInt8] = [0,0,0,0,0,0,0,0,0]
let _ = withUnsafePointer(to: &source[2])
{
memcpy(&dest[2], $0, 5)
}
print(dest) // [0, 0, 3, 4, 5, 6, 7, 0, 0]
不同的数据类型仍然会有问题。 无论数据类型如何,内存中的步幅似乎都是相同的。