快速 数据初始化速度慢,内存效率低下,适用于大型数据集



如果我尝试用相对较大的MutableRandomAccessSlice<Data>初始化 Swift Data 结构,程序启动的内存使用量会很大,并且需要很长时间才能完成。然而,在Objective-C中用NSData做同样的事情似乎没有同样的问题。

例如,使用以下代码:

let startData = Data(count: 100_000_000)
let finalData = Data(startData[0..<95_234_877])

如果我使用以下方法编译它:

xcrun swiftc -O -sdk `xcrun --show-sdk-path --sdk macosx` -o output main.swift

执行(在我的MacBook Air 2011上)需要很长时间才能完成(87秒),内存使用量达到顶峰(请参阅下面的最高625MB):

$ time ./output
./output  85.21s user 1.29s system 99% cpu 1:26.91 total
$ top -o MEM
PID    COMMAND      %CPU  TIME     #TH   #WQ  #PORT MEM    PURG   CMPRS  PGRP  PPID  STATE
38156  output       99.0  01:25.57 1/1   0    10    625M+  0B     992M+  38156 36025 running

如果我分析每个步骤,则创建startData大约需要 0.00015 秒,从startData创建切片需要 0.000007 秒,其余时间初始化finalData

如果我在Objective-C中做同样的事情:

NSData *startData = [[NSMutableData alloc] initWithLength:100000000];
NSData *finalData = [startData subdataWithRange:NSMakeRange(0, 95234877)];

它只需要大约 0.00017 秒。

我在 Swift 示例中做错了什么吗?两者之间似乎存在非常大的差异。

正如你所发现的,Objective-C 代码[startData subdataWithRange:NSMakeRange(0, 95234877)]等同于 startData.subdata(in: 0..<95_234_877)

当你编写Data(startData[0..<95_234_877])时,Swift 调用RangeReplaceableCollectionpublic convenience init<S : Sequence where S.Iterator.Element == Iterator.Element>(_ elements: S),它在 RangeReplaceableCollection.swift.gyb 中定义。实现的核心部分是这样的:

for element in newElements {
  append(element)
}

您知道对集合重复append可能效率低下。

而且,如果你想从[UInt8]初始化一个Data,你最好调用一个特定于[UInt8]的初始值设定项:

let data = Data(bytes: [UInt8](repeating: 0, count: 10_000_000))

Data([UInt8](repeating: 0, count: 100_000_000))调用上述RangeReplaceableCollection中的初始值设定项。


在我看来,Swift 应该更多地优化这样的默认实现,但很难使它们像特定于类型的操作那样高效。

最新更新