swift memcpy复制带偏移量的数组

osh3o9ms  于 2023-03-22  发布在  Swift
关注(0)|答案(4)|浏览(163)

我有一个很大的数组,我只想复制它的中间部分。我试图使用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
polhcujo

polhcujo1#

您可以使用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上,代码在Release配置中编译。

import Foundation

let N = 1_000_000 // Length of source and destination array
let sourceStart = 500
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..<(destinationStart+length)].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.withUnsafeBytes { sourcePtr in
            _ = memcpy(&dest[destinationStart], sourcePtr.baseAddress! + sourceStart, 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.010013580322265625 ms
memcpy:          0.0010728836059570312 ms
replaceSubrange: 0.009059906005859375 ms
slicing:         0.010013580322265625 ms
0yg35tkg

0yg35tkg2#

我建议使用Swift指针API。
当处理UInt8数组时,可以使用copyBytes,取目标数组的一个切片,从中获取一个缓冲区指针,然后将源数组的一个范围复制到该缓冲区。

let destinationStart = 2
let sourceStart = 2
let sourceEnd = 7
let length = sourceEnd - sourceStart
let destinationEnd = destinationStart + length
dest[destinationStart..<destinationEnd].withUnsafeMutableBufferPointer { p in
    source.copyBytes(to: p, from: sourceStart..<sourceEnd)
}

如果要使用memcpy,可以从源代码中获取缓冲区指针,并向其添加源代码偏移量。

source.withUnsafeBytes { sourcePtr in
    memcpy(&dest[destinationStart], sourcePtr.baseAddress! + sourceStart, length)
}
zlwx9yxi

zlwx9yxi3#

似乎在工作:

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]

而且不同的数据类型仍然会有问题。不管数据类型如何,内存中的步幅似乎都是相同的。

c6ubokkw

c6ubokkw4#

感谢@Martin R,我编辑了他的基准代码,memcpy仍然比切片快得多:

import Foundation
import UserModule

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
            _ = memcpy(&dest[destinationStart], 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:       63.13800811767578 ms
memcpy:          0.0680685043334961 ms
replaceSubrange: 334.49602127075195 ms
slicing:         5.218029022216797 ms

相关问题