任何人都可以帮助解释为什么下面的代码在运行时消耗超过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?
答案 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字节,假设您的代码中没有错误?
好吧,这可能是因为为你的元素分配了内存的方式
当满足请求时,存储器分配器可以分配比所请求的更多的存储器。这可能是因为它需要一些内部元数据,因为分页,因为对齐,......
但即使这看起来有点夸张,所以请使用乐器并仔细检查你的代码在做什么。