Swift中的Mutable Array字典由于值执行速度非常慢?如何优化或正确构建? [英] Dictionary in Swift with Mutable Array as value is performing very slow? How to optimize or construct properly?

查看:171
本文介绍了Swift中的Mutable Array字典由于值执行速度非常慢?如何优化或正确构建?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to build a data structure in Swift that maps an Integer to an array of objects (a dictionary with int as key and array as value). The objects are extremely small, and they simply wrap a UIColor and an Int. I have two implementations one that uses a Swift array as the Dictionary's value type, while the other uses a NSMutableArray as the value type. My objective-C code performs extremely fast, but my Swift code is running egregiously slow. Ideally, I would not like to use an NSMutableArray, and would like to keep it as a Swift array. Reason for this is I am writing an algorithm and performance matters, I have noticed some overhead with objC_msgSend. Can anyone help me optimize my Swift code? Am I doing something wrong or is this just a byproduct of swift treating array's as value types? If it is, I would like to understand why the value type performs so slow in this case, what my options are, and how can this scenario can scale going forward? Below I have posted a code snippet and the resulting benchmarks:

快速数组代码:

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代码:

NSMutableArray code:

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性能:

NSMutableArray Performance:

Time after mapping: 0.367132211999888

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

The colorInfo object is a dictionary mapping UIColor objects to an Integer value representing a count. The code essentially reverse maps this, mapping an integer to a UIColor array (its an array because multiple Colors can have the same count). The colorInfo has 60,000 UIColor, Int key value pairs inside of it.

推荐答案

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

Copy on write is a tricky thing, and you need to think carefully about how many things are sharing a structure that you're trying to modify. The culprit is here.

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

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

This is generating a temporary value that is modified and put back into the dictionary. Since two "things" are looking at the same underlying data structure (the dictionary, and append), it forces a copy-on-write.

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

The secret to fixing this is to make sure that there's only one copy when you modify it. How? Take it out of the dictionary. Replace this:

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

这篇关于Swift中的Mutable Array字典由于值执行速度非常慢?如何优化或正确构建?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆