Swift字典记忆消耗是天文数字

时间:2015-12-04 03:32:44

标签: xcode swift dictionary memory trie

任何人都可以帮助解释为什么下面的代码在运行时消耗超过100 MB的RAM?

public struct Trie<Element : Hashable> {
    private var children: [Element:Trie<Element>]
    private var endHere : Bool

    public init() {
        children = [:]
        endHere  = false
    }
    public init<S : SequenceType where S.Generator.Element == Element>(_ seq: S) {
        self.init(gen: seq.generate())
    }
    private init<G : GeneratorType where G.Element == Element>(var gen: G) {
        if let head = gen.next() {
            (children, endHere) = ([head:Trie(gen:gen)], false)
        } else {
            (children, endHere) = ([:], true)
        }
    }
    private mutating func insert<G : GeneratorType where G.Element == Element>(var gen: G) {
        if let head = gen.next() {
            let _ = children[head]?.insert(gen) ?? { children[head] = Trie(gen: gen) }()
        } else {
            endHere = true
        }
    }
    public mutating func insert<S : SequenceType where S.Generator.Element == Element>(seq: S) {
        insert(seq.generate())
    }
}

var trie = Trie<UInt32>()
for i in 0..<300000 {
    trie.insert([UInt32(i), UInt32(i+1), UInt32(i+2)])
}

根据我的计算,上述数据结构的总内存消耗应该在以下几个地方......

3 * count * sizeof(Trie<UInt32>)

或 -

3 * 300,000 * 9 = 8,100,000 bytes = ~8 MB

在运行时,这个数据结构如何消耗超过100 MB?

2 个答案:

答案 0 :(得分:2)

sizeof仅报告堆栈上的静态足迹,Dictionary只是对其内部引用类型实现的引用的包装,以及写入支持的副本。换句话说,键值对和字典的哈希表在堆上分配,sizeof未涵盖。这适用于所有其他Swift集合类型。

在你的情况下,你创建三个Trie - 间接三个词典 - 每次迭代300000.如果@Macmade提到的96字节分配是最小的开销,我不会感到惊讶字典(例如它的哈希桶)。

可能还有与增长存储相关的成本。因此,您可以尝试查看在字典上设置minimumCapacity是否有帮助。另一方面,如果您不需要每次迭代生成的发散路径,您可以将间接枚举视为替代,例如。

public enum Trie<Element> {
    indirect case Next(Element, Trie<Element>)
    case End
}

应该使用更少的内存。

答案 1 :(得分:1)

结构的大小为 9 字节,而不是5。

您可以使用sizeof

进行检查
let size = sizeof( Trie< UInt32 > );

此外,你迭代300'000次,但插入3个值(当然,这是一个特里)。那就是900'000 无论如何,这并不能解释您正在观察的内存消耗。

我对Swift不是很流利,我不懂你的代码 也许它还有一些错误,使它分配的内存超过了所需的内存。

但无论如何,为了了解发生了什么,您需要在乐器(command-i)中运行您的代码。

在我的机器上,swift_slowAlloc可以看到900'000 96字节分配。
那更像是......

为什么96字节,假设您的代码中没有错误?
好吧,这可能是因为为你的元素分配了内存的方式 当满足请求时,存储器分配器可以分配比所请求的更多的存储器。这可能是因为它需要一些内部元数据,因为分页,因为对齐,......

但即使这看起来有点夸张,所以请使用乐器并仔细检查你的代码在做什么。