一尘不染

Swift中的Mutable Array字典由于值执行速度非常慢?如何优化或正确构建?

swift

我正在尝试在Swift中构建一个数据结构,该数据结构将一个Integer映射到一个对象数组(一个以int为键,而array为值的字典)。这些对象非常小,它们只包装了一个UIColor和一个Int。我有两种实现,一种使用Swift数组作为Dictionary的值类型,而另一种使用NSMutableArray作为值类型。我的Objective-
C代码执行得非常快,但是我的Swift代码却运行缓慢。理想情况下,我不想使用NSMutableArray,而是希望将其保留为Swift数组。这样做的原因是我正在编写算法,并且性能很重要,我注意到objC_msgSend有一些开销。谁能帮助我优化我的Swift代码?我是在做错什么,还是仅仅是将数组作为值类型快速处理的副产品?如果是,我想了解为什么在这种情况下值类型的执行速度如此之慢,我的选择是什么,以及该方案在未来如何扩展?在下面,我发布了一个代码段以及由此产生的基准:

Swift数组代码:

let numColors = colorCount(filter: filter, colorInfoCount: colorInfo.count)
var colorCountsArray: [Int] = [Int]()
var countToColorMap: [Int:[CountedColor]] = [Int:[CountedColor]](minimumCapacity: capacity)
var topColors = [CountedColor]()

var startTime = CACurrentMediaTime()
for (color, colorCount) in colorInfo {
    colorCountsArray.append(colorCount)
    if countToColorMap[colorCount] != nil {
        countToColorMap[colorCount]?.append(CountedColor(color: color, colorCount: colorCount))
    } else {
        countToColorMap[colorCount] = [CountedColor(color: color, colorCount: colorCount)]
    }
}
var endTime = CACurrentMediaTime()
print("Time after mapping: \(endTime - startTime)")

快速性能:

Time after mapping: 45.0881789259997

NSMutableArray代码:

let numColors = colorCount(filter: filter, colorInfoCount: colorInfo.count)
var colorCountsArray: [Int] = [Int]()
var countToColorMap: [Int:NSMutableArray] = [Int:NSMutableArray](minimumCapacity: capacity)
var topColors = [CountedColor]()


var startTime = CACurrentMediaTime()
for (color, colorCount) in colorInfo {
    colorCountsArray.append(colorCount)
    if countToColorMap[colorCount] != nil {
        countToColorMap[colorCount]?.add(CountedColor(color: color, colorCount: colorCount))
    } else {
        countToColorMap[colorCount] = NSMutableArray(object: CountedColor(color: color, colorCount: colorCount))
    }
}
var endTime = CACurrentMediaTime()
print("Time after mapping: \(endTime - startTime)")

NSMutableArray性能:

Time after mapping: 0.367132211999888

colorInfo对象是将UIColor对象映射到表示计数的Integer值的字典。代码本质上是反向映射,将整数映射到UIColor数组(将其映射为数组,因为多个Color可以具有相同的计数)。colorInfo内部有60,000个UIColor和Int键值对。


阅读 263

收藏
2020-07-07

共1个答案

一尘不染

写时复制是一件棘手的事情,您需要仔细考虑有多少东西共享您要修改的结构。罪魁祸首在这里。

countToColorMap[colorCount]?.append(CountedColor(color: color as! UIColor, colorCount: colorCount))

这将生成一个临时值,该值将被修改并放回字典中。由于两个“事物”正在查看相同的基础数据结构(字典和append),因此它会强制执行写时复制。

解决此问题的秘诀是确保修改时只有一个副本。怎么样?从字典中取出它。替换为:

if countToColorMap[colorCount] != nil {
    countToColorMap[colorCount]?.append(CountedColor(color: color as! UIColor, colorCount: colorCount))
} else {
    countToColorMap[colorCount] = [CountedColor(color: color as! UIColor, colorCount: colorCount)]
}

运行时为:

Elapsed Time: 74.2517465990022
53217

有了这个:

var countForColor = countToColorMap.removeValue(forKey: colorCount) ?? []
countForColor.append(CountedColor(color: color as! UIColor, colorCount: colorCount))
countToColorMap[colorCount] = countForColor

运行时为:

Elapsed Time: 0.370953808000195
53217
2020-07-07