performance optimizations

This commit is contained in:
Christian Treffs 2017-10-25 16:25:27 +02:00
parent f3c64d8dac
commit 8c38f76e5a
6 changed files with 36 additions and 41 deletions

View File

@ -68,7 +68,9 @@ public extension Entity {
@discardableResult
public final func assign(_ components: Component...) -> Entity {
components.forEach { assign($0) }
for component: Component in components {
assign(component)
}
return self
}

View File

@ -7,7 +7,7 @@
extension Family {
public func iterateMembers(_ apply: @escaping (EntityIdentifier) -> Void) {
memberIds.forEach { (entityId: EntityIdentifier) -> Void in
for entityId in memberIds {
apply(entityId)
}
}
@ -17,8 +17,7 @@ extension Family {
public func iterate<A>(components _: A.Type, _ apply: @escaping (EntityIdentifier, A?) -> Void)
where A: Component {
var iter = memberIds.makeIterator()
while let entityId: EntityIdentifier = iter.next() {
for entityId in memberIds {
let a: A? = self.nexus.get(for: entityId)
apply(entityId, a)
}
@ -26,8 +25,7 @@ extension Family {
public func iterate<A, B>(components _: A.Type, _: B.Type, _ apply: @escaping (EntityIdentifier, A?, B?) -> Void)
where A: Component, B: Component {
var iter = memberIds.makeIterator()
while let entityId: EntityIdentifier = iter.next() {
for entityId in memberIds {
let a: A? = self.nexus.get(for: entityId)
let b: B? = self.nexus.get(for: entityId)
apply(entityId, a, b)
@ -36,8 +34,7 @@ extension Family {
public func iterate<A, B, C>(components _: A.Type, _: B.Type, _: C.Type, _ apply: @escaping (EntityIdentifier, A?, B?, C?) -> Void)
where A: Component, B: Component, C: Component {
var iter = memberIds.makeIterator()
while let entityId: EntityIdentifier = iter.next() {
for entityId in memberIds {
let a: A? = self.nexus.get(for: entityId)
let b: B? = self.nexus.get(for: entityId)
let c: C? = self.nexus.get(for: entityId)
@ -47,8 +44,7 @@ extension Family {
public func iterate<A, B, C, D>(components _: A.Type, _: B.Type, _: C.Type, _: D.Type, _ apply: @escaping (EntityIdentifier, A?, B?, C?, D?) -> Void)
where A: Component, B: Component, C: Component, D: Component {
var iter = memberIds.makeIterator()
while let entityId: EntityIdentifier = iter.next() {
for entityId in memberIds {
let a: A? = self.nexus.get(for: entityId)
let b: B? = self.nexus.get(for: entityId)
let c: C? = self.nexus.get(for: entityId)
@ -58,8 +54,7 @@ extension Family {
}
public func iterate<A, B, C, D, E>(components _: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type, _ apply: @escaping (EntityIdentifier, A?, B?, C?, D?, E?) -> Void)
where A: Component, B: Component, C: Component, D: Component, E: Component {
var iter = memberIds.makeIterator()
while let entityId: EntityIdentifier = iter.next() {
for entityId in memberIds {
let a: A? = self.nexus.get(for: entityId)
let b: B? = self.nexus.get(for: entityId)
let c: C? = self.nexus.get(for: entityId)
@ -72,8 +67,7 @@ extension Family {
public func iterate<A, B, C, D, E, F>(components _: A.Type, _: B.Type, _: C.Type, _: D.Type, _: E.Type, _: F.Type,
_ apply: @escaping (EntityIdentifier, A?, B?, C?, D?, E?, F?) -> Void)
where A: Component, B: Component, C: Component, D: Component, E: Component, F: Component {
var iter = memberIds.makeIterator()
while let entityId: EntityIdentifier = iter.next() {
for entityId in memberIds {
let a: A? = self.nexus.get(for: entityId)
let b: B? = self.nexus.get(for: entityId)
let c: C? = self.nexus.get(for: entityId)

View File

@ -29,7 +29,12 @@ extension Nexus {
let componentId = component.identifier
let entityIdx = entity.identifier.index
let hash: EntityComponentHash = componentId.hashValue(using: entityIdx)
assert(!has(hash), "ComponentAdd collision: \(entityIdx) already has a component \(component)")
/// test if component is already assigned
guard !has(hash) else {
report("ComponentAdd collision: \(entityIdx) already has a component \(component)")
// TODO: replace component?! copy properties?!
return
}
var newComponentIndex: ComponentIndex = ComponentIndex.invalid
if componentsByType[componentId] != nil {
newComponentIndex = componentsByType[componentId]!.count // TODO: get next free index
@ -40,17 +45,11 @@ extension Nexus {
}
// assigns the component id to the entity id
// FIXME: expensive
if componentIdsByEntity[entityIdx] != nil {
let (inserted, _) = componentIdsSetByEntity[entityIdx]!.insert(componentId)
assert(inserted)
let newIndex = componentIdsByEntity[entityIdx]!.count
componentIdsByEntity[entityIdx]!.insert(componentId, at: newIndex)
componentIdsByEntityLookup[hash] = newIndex
let endIndex: Int = componentIdsByEntity[entityIdx]!.count
componentIdsByEntity[entityIdx]!.append(componentId) // Amortized O(1)
componentIdsByEntityLookup[hash] = endIndex
} else {
componentIdsSetByEntity[entityIdx] = Set<ComponentIdentifier>(minimumCapacity: 2)
let (inserted, _) = componentIdsSetByEntity[entityIdx]!.insert(componentId)
assert(inserted)
componentIdsByEntity[entityIdx] = ComponentIdentifiers(arrayLiteral: componentId)
componentIdsByEntityLookup[hash] = 0
}
@ -60,7 +59,7 @@ extension Nexus {
// FIXME: this is costly for many families
let entityId: EntityIdentifier = entity.identifier
familiyByTraitHash.forEach { (_, family) in
for (_, family) in familiyByTraitHash {
update(membership: family, for: entityId)
}
@ -111,13 +110,6 @@ extension Nexus {
}
// MARK: unassign component
guard componentIdsSetByEntity[entityId.index]?.remove(componentId) != nil else {
assert(false, "ComponentRemove failure: no component found to be removed in set")
report("ComponentRemove failure: no component found to be removed in set")
return false
}
guard let removeIndex: ComponentIdsByEntityIndex = componentIdsByEntityLookup.removeValue(forKey: hash) else {
assert(false, "ComponentRemove failure: no component found to be removed")
report("ComponentRemove failure: no component found to be removed")
@ -140,7 +132,7 @@ extension Nexus {
}
// FIXME: this is costly for many families
familiyByTraitHash.forEach { (_, family) in
for (_, family) in familiyByTraitHash {
update(membership: family, for: entityId)
}

View File

@ -14,10 +14,14 @@ extension Nexus {
/// - noneComponents: none component type may appear in this family.
/// - oneComponents: at least one of component types must appear in this family.
/// - Returns: family with given traits.
public func family(requiresAll allComponents: [Component.Type], excludesAll noneComponents: [Component.Type], needsAtLeastOne oneComponents: [Component.Type] = []) -> Family {
public func family(requiresAll allComponents: [Component.Type],
excludesAll noneComponents: [Component.Type],
needsAtLeastOne oneComponents: [Component.Type] = []) -> Family {
let traits = FamilyTraitSet(requiresAll: allComponents, excludesAll: noneComponents, needsAtLeastOne: oneComponents)
return family(with: traits)
}
public func family(with traits: FamilyTraitSet) -> Family {
guard let family: Family = get(family: traits) else {
return create(family: traits)
}
@ -26,10 +30,12 @@ extension Nexus {
public func canBecomeMember(_ entity: Entity, in family: Family) -> Bool {
let entityIdx: EntityIndex = entity.identifier.index
guard let componentSet: ComponentSet = componentIdsSetByEntity[entityIdx] else {
guard let componentIds: ComponentIdentifiers = componentIdsByEntity[entityIdx] else {
assert(false, "no component set defined for entity: \(entity)")
return false
}
// FIXME: may be a bottle neck
let componentSet: ComponentSet = ComponentSet.init(componentIds)
return family.traits.isMatch(components: componentSet)
}
@ -64,8 +70,8 @@ extension Nexus {
assert(replaced == nil, "Family with exact trait hash already exists: \(traitHash)")
// FIXME: this is costly for many entities
entities.forEach {
update(membership: family, for: $0.identifier)
for entity: Entity in entities {
update(membership: family, for: entity.identifier)
}
notify(FamilyCreated(family: traits))
@ -78,7 +84,9 @@ extension Nexus {
func update(membership family: Family, for entityId: EntityIdentifier) {
let entityIdx: EntityIndex = entityId.index
guard let componentsSet: ComponentSet = componentIdsSetByEntity[entityIdx] else { return }
guard let componentIds: ComponentIdentifiers = componentIdsByEntity[entityIdx] else { return }
// FIXME: bottle neck
let componentsSet: ComponentSet = ComponentSet.init(componentIds)
let isMember: Bool = family.isMember(entityId)
let isMatch: Bool = family.traits.isMatch(components: componentsSet)
switch (isMatch, isMember) {

View File

@ -53,14 +53,12 @@ public class Nexus {
var familiyByTraitHash: [FamilyTraitSetHash: Family]
var trashMap: TraitEntityIdHashSet
var familyMembersByTraitHash: [FamilyTraitSetHash: EntityIds]
var componentIdsSetByEntity: [EntityIndex: ComponentSet]
public init() {
entities = Entities()
componentsByType = [:]
componentIndexByEntityComponentHash = [:]
componentIdsByEntity = [:]
componentIdsSetByEntity = [:]
componentIdsByEntityLookup = [:]
freeEntities = ContiguousArray<EntityIdentifier>()
familiyByTraitHash = [:]

View File

@ -78,6 +78,7 @@ class NexusTests: XCTestCase {
let p0 = Position(x: 1, y: 2)
e0.assign(p0)
e0.assign(p0)
XCTAssert(e0.isValid)