zdandoh / ecs

Golang ECS library using go generate
MIT License
5 stars 0 forks source link

Faster iteration by removing type switch per entity #2

Closed delaneyj closed 8 months ago

delaneyj commented 8 months ago

In the select.go the switch per entity

func Select(selector interface{}) {
    match := true
    _ = match
    cont := true
    _ = cont
    var entity Entity
    for i := 0; i < currEntities; i++ {
        match = true
        entity = entities[i >> entityPageBits][i % entityPageSize]
        switch fun := selector.(type) {

        case func(Entity, *comp.Position, ) :
            match = match && entity.components[0] & 1 != 0

            if match {
                fun(entity, &storePosition[entity.id >> entityPageBits][entity.id % entityPageSize], )

            }

        case func(Entity, *comp.Position, *comp.Velocity, ) :
            match = match && entity.components[0] & 1 != 0
            match = match && entity.components[0] & 2 != 0

            if match {
                fun(entity, &storePosition[entity.id >> entityPageBits][entity.id % entityPageSize], &storeVelocity[entity.id >> entityPageBits][entity.id % entityPageSize], )

            }

        case func(Entity):
            fun(entity)
        default:
            panic(fmt.Sprintf("unknown selector function: run go generate: %s", reflect.TypeOf(selector).String()))
        }
    }
}

Gives benchmarks of 21752 72465 ns/op 0 B/op 0 allocs/op

Changing to

func Select(selector interface{}) {
    match := true
    _ = match
    cont := true
    _ = cont

    switch fun := selector.(type) {

    case func(Entity, *comp.Position):
        for i := 0; i < currEntities; i++ {
            entity := entities[i>>entityPageBits][i%entityPageSize]
            match := match && entity.components[0]&1 != 0

            if match {
                fun(entity, &storePosition[entity.id>>entityPageBits][entity.id%entityPageSize])

            }
        }

    case func(Entity, *comp.Position, *comp.Velocity):
        for i := 0; i < currEntities; i++ {
            entity := entities[i>>entityPageBits][i%entityPageSize]
            match = match && entity.components[0]&1 != 0
            match = match && entity.components[0]&2 != 0

            if match {
                fun(entity, &storePosition[entity.id>>entityPageBits][entity.id%entityPageSize], &storeVelocity[entity.id>>entityPageBits][entity.id%entityPageSize])

            }
        }

    case func(Entity):
        for i := 0; i < currEntities; i++ {
            entity := entities[i>>entityPageBits][i%entityPageSize]
            fun(entity)
        }
    default:
        panic(fmt.Sprintf("unknown selector function: run go generate: %s", reflect.TypeOf(selector).String()))
    }

}

results in 29890 53641 ns/op 0 B/op 0 allocs/op

zdandoh commented 8 months ago

Thanks for the callout, this almost doubled the Select performance on my system! Implemented