Swift:将字节从数组复制到数组

时间:2018-04-16 05:22:19

标签: arrays swift

我有一些性能敏感的代码需要在特定的偏移处将一系列值从一个数组复制到另一个数组。你能在Swift中执行简单的移动内存操作吗?我整个上午一直在阅读这篇文章,但他们已经让我在Swift中难以置信地获取内存我不确定它是否可能(我是新手斯威夫特显然)。

我确实尝试使用Array.replaceSubrange,但是它创建了一个非常讨厌的代码块,谁知道引擎盖下有多少内存副本"数组切片"从Swift到目前为止,函数本身可能很慢。一个简单的memmove()可以轻松地解决问题。

我认为可行的一个例子。

var src: [UInt32] = [1, 2, 3, 4]
var dest: [UInt32] = [0, 0, 0, 0]

dest.withUnsafeMutableBytes { destBytes in
    src.withUnsafeBytes { srcBytes in
       // for example copy 4 bytes starting at the address of destBytes[1]
       // from the address of srcBytes[1]
       movemem(&destBytes[1], &srcBytes[1], 4)
    }
}
// dest now should be [0, 2, 3, 0] assuming UInt32 is 2 bytes

2 个答案:

答案 0 :(得分:0)

假设问题是 需要在特定的偏移处将一系列值从一个数组复制到另一个数组。

var src: [UInt32] = [1, 2, 3, 4]
var dest: [UInt32] = [0, 0, 0, 0]

let rangeOfSrc = [1...2] /// Will get from 1st to 2nd so that 2, 3
dest.insert(contentsOf: rangeOfSrc, at: 2) /// Will insert this range at 2nd position of the dest array
print(dest)
  

输出: [0,0,2,3,0,0]

有关更多详情,请参阅此documentation

编辑2:如果您想要替换范围而不是插入。

dest.replaceSubrange(1...2, with: src[1...2])
print(dest)
  

输出: [0,2,3,0]

编辑1:memmove

memmove(&dest[0], &src[0], 4)
print(dest)
  

输出: [1,0,0,0]

<强> EDIT3

src.withUnsafeBufferPointer {(result) in
     memmove(&dest[0], result.baseAddress, 8)
     print(dest)
}
  

输出: [1,2,0,0]

答案 1 :(得分:0)

即使我认为这不是最好的解决方案,我也会回答我自己的问题。我不太了解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()调用之前获得一次数组字节,所以它在实践中甚至比这更快。

replaceSubrange:0.750978946685791

memmove: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。

BlockMove:0.0957469940185547 replaceSubrange:1.89903497695923 memmove: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)")

}