Fix sparse component set performance

This commit is contained in:
Christian Treffs 2017-10-31 11:32:07 +01:00
parent a559387891
commit 4ada634130
1 changed files with 22 additions and 44 deletions

View File

@ -7,43 +7,34 @@
public class SparseComponentSet { public class SparseComponentSet {
public typealias Element = Component public typealias Element = Component
public let chunkSize: Int = 4096
fileprivate typealias ComponentIdx = Int fileprivate typealias ComponentIdx = Int
fileprivate var size: Int = 0 fileprivate var size: Int = 0
fileprivate class Pair {
let key: EntityIndex
let value: Element
init(key: EntityIndex, value: Element) {
self.key = key
self.value = value
}
}
fileprivate var dense: ContiguousArray<Pair?> fileprivate var dense: ContiguousArray<Pair?>
fileprivate var sparse: [EntityIndex: ComponentIdx] fileprivate var sparse: [EntityIndex: ComponentIdx]
fileprivate typealias Pair = (key: EntityIndex, value: Element)
public init() { public init() {
dense = ContiguousArray<Pair?>() dense = ContiguousArray<Pair?>()
dense.reserveCapacity(chunkSize) sparse = [EntityIndex: ComponentIdx]()
sparse = [EntityIndex: ComponentIdx].init(minimumCapacity: chunkSize) }
deinit {
clear()
} }
public var count: Int { return size } public var count: Int { return size }
internal var capacitySparse: Int { return sparse.count } internal var capacitySparse: Int { return sparse.capacity }
internal var capacityDense: Int { return dense.count } internal var capacityDense: Int { return dense.capacity }
public func contains(_ entityIdx: EntityIndex ) -> Bool { public func contains(_ entityIdx: EntityIndex ) -> Bool {
guard let compIdx: ComponentIdx = sparse[entityIdx] else { return false } return sparse[entityIdx] != nil &&
return compIdx < count && dense[compIdx] != nil sparse[entityIdx]! < count &&
dense[sparse[entityIdx]!] != nil
} }
@discardableResult @discardableResult
public func add(_ element: Element, with entityIdx: EntityIndex ) -> Bool { public func add(_ element: Element, with entityIdx: EntityIndex ) -> Bool {
if contains(entityIdx) { return false } if contains(entityIdx) { return false }
if needsToGrow(entityIdx) {
grow(including: entityIdx)
}
sparse[entityIdx] = count sparse[entityIdx] = count
let entry: Pair = Pair(key: entityIdx, value: element) let entry: Pair = Pair(key: entityIdx, value: element)
dense.append(entry) dense.append(entry)
@ -52,52 +43,39 @@ public class SparseComponentSet {
} }
public func get(at entityIdx: EntityIndex) -> Element? { public func get(at entityIdx: EntityIndex) -> Element? {
guard let compIdx: ComponentIdx = sparse[entityIdx] else { return nil } guard contains(entityIdx) else { return nil }
return dense[compIdx]!.value return dense[sparse[entityIdx]!]!.value
} }
@discardableResult @discardableResult
public func remove(_ entityIdx: EntityIndex ) -> Element? { public func remove(_ entityIdx: EntityIndex ) -> Element? {
guard let compIdx: ComponentIdx = sparse[entityIdx] else { return nil } guard contains(entityIdx) else { return nil }
let last: Int = count-1 let compIdx: ComponentIdx = sparse[entityIdx]!
dense.swapAt(compIdx, last) let lastIdx: ComponentIdx = count-1
dense.swapAt(compIdx, lastIdx)
sparse[entityIdx] = nil sparse[entityIdx] = nil
let swapped: Pair = dense[compIdx]! let swapped: Pair = dense[compIdx]!
sparse[swapped.key] = compIdx sparse[swapped.key] = compIdx
let removed: Pair = dense.popLast()!! let removed: Pair = dense.popLast()!!
size -= 1 size -= 1
if size == 0 {
clear(keepingCapacity: false)
}
return removed.value return removed.value
} }
public func clear(keepingCapacity: Bool = false) { public func clear(keepingCapacity: Bool = false) {
size = 0
dense.removeAll(keepingCapacity: keepingCapacity) dense.removeAll(keepingCapacity: keepingCapacity)
sparse.removeAll(keepingCapacity: keepingCapacity)
} }
fileprivate func needsToGrow(_ index: Int) -> Bool {
return index > count - 1
}
fileprivate func grow(including index: Int) {
let newCapacity: Int = nearest(to: index)
//let newCount: Int = newCapacity-count
dense.reserveCapacity(newCapacity)
/*for _ in 0..<newCount {
dense.append(nil)
}*/
}
fileprivate func nearest(to index: Int) -> Int {
let delta = Float(index) / Float(chunkSize)
let multiplier = Int(delta) + 1
return multiplier * chunkSize
}
} }
extension SparseComponentSet: Sequence { extension SparseComponentSet: Sequence {
public func makeIterator() -> AnyIterator<Element> { public func makeIterator() -> AnyIterator<Element> {
var iterator = dense.makeIterator() var iterator = dense.makeIterator()
return AnyIterator<Element> { return AnyIterator<Element> {
iterator.next()??.value iterator.next()??.value
} }