Fix sparse component set performance
This commit is contained in:
parent
a559387891
commit
4ada634130
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue