Swift巨大的数组字典,非常慢

时间:2018-10-03 23:47:36

标签: arrays swift performance dictionary nsdictionary

我正在使用dictionary在Swift中进行项目。

此词典的类型为[String : [Posting]]。我要在其中插入大约200k个不同的“术语”(键),对于每个术语,我要在列表中附加大约500至1000个对象。我知道这是一种奇怪的做法,但我没有选择,我必须处理所有这些要素。

问题在于,随着字典的变大,这非常慢。我尝试过切换到NSMutableDictionary,但不走运。

每次需要插入元素时,都会调用我的addTerm函数:

   func addTerm(_ term: String, withId id: Int, atPosition position: Int) {

        if self.map[term] == nil {
            self.map[term] = [Posting]()
        }

        if self.map[term]!.last?.documentId == id {
            self.map[term]!.last?.addPosition(position)
        }
        else {
            self.map[term]!.append(Posting(withId: id, atPosition: position, forTerm: term))
        }
    }

编辑:我意识到不是导致所有这种滞后的字典,而是实际上包含的数组。数组在添加新元素时会过多地重新分配,而我最好的办法是将它们替换为ContiguousArray

3 个答案:

答案 0 :(得分:1)

当代码太慢时,通常的方法是在Instruments中对其进行概要分析,以找出哪些行实际花费的时间最长并从那里开始。在其他地方可能会遇到瓶颈,等等。直接从Xcode内部运行应用程序还会创建一个调试版本,这会牺牲可调试性的性能。发布版本的性能可能会好得多。

此外,如果您的程序占用大量内存,则系统可能难以使该内存可供您的应用使用。在非iOS平台上,这将导致将内存换出到磁盘,这将严重影响应用程序的性能,因为系统无法预期,接下来将访问字典的哪些元素。

如果内存需求不是造成速度下降的原因,则可以尝试以下几种方法:

  • 如果可以估计要插入字典中的项目数,则可以使用dictionary.reserveCapacity(numberOfItems)。随着字典的增长,可能需要调整其大小,这可能需要重建哈希表,字典类型在内部使用该哈希表。这种方法也适用于数组。

  • Swift提供了使用公用键Dictionary(grouping: collection, by: { item in item.property })将项目自动分组为字典的方法。这种方法的计算效率更高,因为所有内容都可以在一批中处理。

  • 另一种方法可能是使用不需要频繁重新分配的其他数据类型,例如树形图。但是Swift在标准库中没有提供这种类型。

答案 1 :(得分:1)

这是相当普遍的性能陷阱,也可以在以下情况中看到

问题源于以下事实:您在表达式self.map[term]!.append(...)中进行突变的数组是字典存储中基础数组的临时可变副本。这意味着该数组永远不会被唯一引用,因此总是要重新分配其缓冲区。

这种情况将在Swift 5中通过非官方引入通用访问器来解决,但在此之前,一种解决方案(如上述两个问答中所述)是使用Dictionary的{​​{1}} Swift 4.1可以直接在存储中更改值。

尽管您的案例并不是应用单个突变的简单案例,所以您需要某种包装函数才能使您能够对可变数组进行范围化访问。

例如,它看起来像:

subscript(_:default:)

答案 2 :(得分:0)

我有同样的问题。 20万个条目太疯狂了…… 所以我做了一个课,把数组放在那里……

class MyIndex
{
    var entries: [Posting]
}

var map = [String: MyIndex]()

现在看来工作很快