Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Direct component access when iterating, benchmarks #93

Merged
merged 20 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e6dfeef
Start fiddling with kotlinx.benchmark
0ffz Aug 29, 2023
e11e0bc
Work on letting accessors get component data directly instead of pack…
0ffz Aug 31, 2023
49c1549
Add some more unpacking benchmarks, use fast int to int map for compo…
0ffz Aug 31, 2023
0de9597
Cache component accessor index per archetype, don't repack archetype …
0ffz Aug 31, 2023
f57a7b0
Avoid componentId calls in common methods, don't bother firing set ev…
0ffz Aug 31, 2023
6e1be1c
Allow component accessor to set directly. Add a velocity system test
0ffz Sep 1, 2023
bf45f6f
Use EntityTypes's indexOf instead of a map in archetype
0ffz Sep 1, 2023
7becb5d
Fix some bugs with event listeners, add new simpler tests for them
0ffz Sep 1, 2023
2605936
Work further on accessor syntax for listeners
0ffz Sep 3, 2023
768c143
Improve accessor syntax for default, map, removable
0ffz Sep 3, 2023
e12e1eb
Remove commented out code in some accessors
0ffz Sep 3, 2023
e72a181
Add jvm specific implementation for archetype map, avoid some slow co…
0ffz Sep 4, 2023
484f380
Iterate to calculate indexOf for EntityType
0ffz Sep 4, 2023
506ec30
Slight cleanup, allow using component accessor without delegate
0ffz Sep 4, 2023
28738fa
Update addons, rename AccessorThisRef to Pointer
0ffz Sep 5, 2023
1cd4a99
Use records typealias, remove ops/s comments
0ffz Sep 5, 2023
aa4eb48
Update copyright line in license to be more accurate
0ffz Sep 12, 2023
56d8c58
A bit of cleanup
0ffz Sep 14, 2023
4e2e8ef
Reimplement checking handler, rename fastForEach, add toList, other c…
0ffz Sep 17, 2023
7ef2702
Final bits of cleanup
0ffz Sep 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 Mine In Abyss
Copyright (c) 2020 Danielle Voznyy

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ data class Velocity(var x: Double, var y: Double)

class UpdatePositionSystem : TickingSystem(interval = 20.milliseconds) {
// Specify all components we want (Geary also supports branched AND/OR/NOT statements for selection)
val TargetScope.position by get<Position>()
val TargetScope.velocity by get<Velocity>()
val Pointer.position by get<Position>()
val Pointer.velocity by get<Velocity>()

override fun TargetScope.tick() {
override fun Pointer.tick() {
// We can access our components like regular variables!
position.x += velocity.x
position.y += velocity.y
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package com.mineinabyss.geary.game.systems

import com.mineinabyss.geary.annotations.optin.UnsafeAccessors
import com.mineinabyss.geary.game.components.Expiry
import com.mineinabyss.geary.systems.RepeatingSystem
import com.mineinabyss.geary.systems.accessors.TargetScope
import com.mineinabyss.geary.systems.accessors.Pointer


/**
* Handles removing components when an [Expiry] relation exists with another component.
*/
class ExpiringComponentSystem : RepeatingSystem() {
private val TargetScope.expiry by getRelations<Expiry, Any?>()
private val Pointer.expiry by getRelationsWithData<Expiry, Any?>()

override fun TargetScope.tick() {
if (expiry.data.timeOver()) {
entity.remove(expiry.kind.id)
entity.remove(expiry.relation.id)
@OptIn(UnsafeAccessors::class)
override fun Pointer.tick() {
expiry.forEach {
if (it.data.timeOver()) {
entity.remove(it.kind.id)
entity.remove(it.relation.id)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
package com.mineinabyss.geary.prefabs.configuration.systems

import com.mineinabyss.geary.annotations.Handler
import com.mineinabyss.geary.components.EntityName
import com.mineinabyss.geary.components.relations.NoInherit
import com.mineinabyss.geary.annotations.optin.UnsafeAccessors
import com.mineinabyss.geary.helpers.addParent
import com.mineinabyss.geary.helpers.entity
import com.mineinabyss.geary.prefabs.configuration.components.ChildOnPrefab
import com.mineinabyss.geary.prefabs.configuration.components.ChildrenOnPrefab
import com.mineinabyss.geary.prefabs.configuration.components.Prefab
import com.mineinabyss.geary.systems.Listener
import com.mineinabyss.geary.systems.accessors.TargetScope
import com.mineinabyss.geary.systems.accessors.Pointers


class ParseChildOnPrefab : Listener() {
private val TargetScope.child by onSet<ChildOnPrefab>().onTarget()
private var Pointers.child by get<ChildOnPrefab>().removable().whenSetOnTarget()

@Handler
private fun TargetScope.convertToRelation() {
@OptIn(UnsafeAccessors::class)
override fun Pointers.handle() {
entity {
addParent(entity)
setAll(child.components)
addParent(target.entity)
setAll(child!!.components)
}
entity.remove<ChildOnPrefab>()
child = null
}
}

class ParseChildrenOnPrefab : Listener() {
private val TargetScope.children by onSet<ChildrenOnPrefab>().onTarget()
private var Pointers.children by get<ChildrenOnPrefab>().removable().whenSetOnTarget()

@Handler
private fun TargetScope.convertToRelation() {
children.nameToComponents.forEach { (name, components) ->
@OptIn(UnsafeAccessors::class)
override fun Pointers.handle() {
children!!.nameToComponents.forEach { (name, components) ->
entity {
set(EntityName(name))
set(Prefab())
addParent(entity)
addParent(target.entity)
addRelation<NoInherit, Prefab>()
setAll(components)
}
}
entity.remove<ChildrenOnPrefab>()
children = null
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package com.mineinabyss.geary.prefabs.configuration.systems

import com.mineinabyss.geary.annotations.Handler
import com.mineinabyss.geary.prefabs.configuration.components.RelationOnPrefab
import com.mineinabyss.geary.systems.Listener
import com.mineinabyss.geary.systems.accessors.TargetScope
import com.mineinabyss.geary.systems.accessors.Pointers


class ParseRelationOnPrefab : Listener() {
private val TargetScope.relation by onSet<RelationOnPrefab>().onTarget()
private var Pointers.relation by get<RelationOnPrefab>().removable().whenSetOnTarget()

@Handler
private fun TargetScope.convertToRelation() {
override fun Pointers.handle() {
try {
val rel: RelationOnPrefab = relation
val rel: RelationOnPrefab = relation!!
// entity.setRelation(relation.value, entity.parseEntity(relation.key).id)
} finally {
entity.remove<RelationOnPrefab>()
relation = null
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package com.mineinabyss.geary.prefabs.configuration.systems

import com.mineinabyss.geary.annotations.Handler
import com.mineinabyss.geary.datatypes.Records
import com.mineinabyss.geary.annotations.optin.UnsafeAccessors
import com.mineinabyss.geary.systems.Listener
import com.mineinabyss.geary.systems.accessors.Pointers
import com.mineinabyss.geary.systems.accessors.RelationWithData
import com.mineinabyss.geary.systems.accessors.TargetScope


class ParseRelationWithDataSystem : Listener() {
private val TargetScope.relationWithData by onSet<RelationWithData<*, *>>().onTarget()
private val Records.relationWithData by get<RelationWithData<*, *>>().whenSetOnTarget()

@Handler
private fun TargetScope.convertToRelation() {
@OptIn(UnsafeAccessors::class)
override fun Pointers.handle() {
val entity = target.entity
val data = relationWithData.data
val targetData = relationWithData.targetData
if (data != null) entity.set(data, relationWithData.relation.id)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package com.mineinabyss.geary.prefabs.systems

import com.mineinabyss.geary.annotations.Handler
import com.mineinabyss.geary.annotations.optin.UnsafeAccessors
import com.mineinabyss.geary.datatypes.family.family
import com.mineinabyss.geary.prefabs.events.PrefabLoaded
import com.mineinabyss.geary.prefabs.helpers.inheritPrefabs
import com.mineinabyss.geary.systems.Listener
import com.mineinabyss.geary.systems.accessors.EventScope
import com.mineinabyss.geary.systems.accessors.TargetScope
import com.mineinabyss.geary.systems.accessors.Pointers


class InheritPrefabsOnLoad : Listener() {
private val EventScope.loaded by family { has<PrefabLoaded>() }.onEvent()
private val Pointers.loaded by family { has<PrefabLoaded>() }.on(event)

@Handler
private fun TargetScope.inheritOnLoad() {
entity.inheritPrefabs()
@OptIn(UnsafeAccessors::class)
override fun Pointers.handle() {
target.entity.inheritPrefabs()
}
}

Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package com.mineinabyss.geary.prefabs.systems

import com.mineinabyss.geary.annotations.Handler
import com.mineinabyss.geary.components.relations.NoInherit
import com.mineinabyss.geary.datatypes.Records
import com.mineinabyss.geary.annotations.optin.UnsafeAccessors
import com.mineinabyss.geary.prefabs.PrefabKey
import com.mineinabyss.geary.prefabs.prefabs
import com.mineinabyss.geary.systems.Listener
import com.mineinabyss.geary.systems.accessors.TargetScope
import com.mineinabyss.geary.systems.accessors.Pointers


class TrackPrefabsByKeySystem : Listener() {
private val TargetScope.key by onSet<PrefabKey>().onTarget()
private val Records.key by get<PrefabKey>().whenSetOnTarget()

@Handler
private fun TargetScope.registerOnSet() {
prefabs.manager.registerPrefab(key, entity)
entity.addRelation<NoInherit, PrefabKey>()
@OptIn(UnsafeAccessors::class)
override fun Pointers.handle() {
prefabs.manager.registerPrefab(key, target.entity)
target.entity.addRelation<NoInherit, PrefabKey>()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,27 @@ package com.mineinabyss.geary.uuid.systems

import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import com.mineinabyss.geary.annotations.Handler
import com.mineinabyss.geary.annotations.optin.UnsafeAccessors
import com.mineinabyss.geary.systems.GearyListener
import com.mineinabyss.geary.systems.accessors.TargetScope
import com.mineinabyss.geary.systems.accessors.Pointers

import com.mineinabyss.geary.uuid.components.RegenerateUUIDOnClash
import com.mineinabyss.geary.uuid.uuid2Geary

class TrackUuidOnAdd : GearyListener() {
private val TargetScope.uuid by onSet<Uuid>().onTarget()
var Pointers.uuid by get<Uuid>().whenSetOnTarget()
val Pointers.regenerateUUIDOnClash by get<RegenerateUUIDOnClash>().orNull().on(target)

@Handler
private fun TargetScope.track() {
@OptIn(UnsafeAccessors::class)
override fun Pointers.handle() {
if (uuid in uuid2Geary)
if (entity.has<RegenerateUUIDOnClash>()) {
if (regenerateUUIDOnClash != null) {
val newUuid = uuid4()
entity.set(newUuid)
uuid2Geary[newUuid] = entity
} else error("Tried tracking entity $entity with already existing uuid $uuid")
uuid = newUuid
uuid2Geary[newUuid] = target.entity
} else error("Tried tracking entity $target.entity with already existing uuid $uuid")
else
uuid2Geary[uuid] = entity
uuid2Geary[uuid] = target.entity
}
}

Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package com.mineinabyss.geary.uuid.systems

import com.benasher44.uuid.Uuid
import com.mineinabyss.geary.annotations.Handler
import com.mineinabyss.geary.components.events.EntityRemoved
import com.mineinabyss.geary.datatypes.Records
import com.mineinabyss.geary.datatypes.family.family
import com.mineinabyss.geary.systems.GearyListener
import com.mineinabyss.geary.systems.accessors.EventScope
import com.mineinabyss.geary.systems.accessors.TargetScope
import com.mineinabyss.geary.systems.accessors.Pointers

import com.mineinabyss.geary.uuid.uuid2Geary

class UnTrackUuidOnRemove : GearyListener() {
private val TargetScope.uuid by get<Uuid>().onTarget()
private val EventScope.removed by family { has<EntityRemoved>() }.onEvent()
private val Pointers.uuid by get<Uuid>().on(target)
private val Pointers.removed by family { has<EntityRemoved>() }.on(event)

@Handler
private fun TargetScope.untrack() {
override fun Pointers.handle() {
uuid2Geary.remove(uuid)
}
}
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ allprojects {
subprojects {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
// kotlinOptions.freeCompilerArgs += "-Xno-param-assertions"
}
}

Expand Down
62 changes: 62 additions & 0 deletions geary-benchmarks/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import kotlinx.benchmark.gradle.JvmBenchmarkTarget
import org.jetbrains.kotlin.allopen.gradle.AllOpenExtension

@Suppress("DSL_SCOPE_VIOLATION")
plugins {
id(libs.plugins.mia.kotlin.jvm.get().pluginId)
// id(libs.plugins.mia.publication.get().pluginId)
// alias(libs.plugins.kotlinx.serialization)
id("org.jetbrains.kotlinx.benchmark") version "0.4.9"
kotlin("plugin.allopen") version "1.8.20"
}

configure<AllOpenExtension> {
annotation("org.openjdk.jmh.annotations.State")
}

dependencies {
implementation(project(":geary-core"))
implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.9")
}

benchmark {
configurations {
named("main") {
exclude("jvmTesting")
warmups = 3
iterations = 3
iterationTime = 5
iterationTimeUnit = "sec"
}

create("fast") {
exclude("jvmTesting")
warmups = 1
iterations = 1
iterationTime = 3
iterationTimeUnit = "sec"
}

create("fastest") {
exclude("jvmTesting")
warmups = 1
iterations = 1
iterationTime = 3
iterationTimeUnit = "sec"
}

create("specific") {
include("Unpack")
warmups = 1
iterations = 1
iterationTime = 3
iterationTimeUnit = "sec"
}
}
targets {
register("main") {
this as JvmBenchmarkTarget
jmhVersion = "1.21"
}
}
}
Loading