diff --git a/source/bubel/ecs/entity.d b/source/bubel/ecs/entity.d index 441f412..10720a2 100644 --- a/source/bubel/ecs/entity.d +++ b/source/bubel/ecs/entity.d @@ -45,6 +45,28 @@ struct Entity uint ind = cast(uint)((cast(void*)&this - block.dataBegin()) / EntityID.sizeof()); return cast(T*)(cast(void*)block + info.deltas[T.component_id] + ind * T.sizeof); } + + EntityMeta getMeta() + { + EntityMeta meta; + meta.block = gEM.getMetaData(&this); + static if (EntityID.sizeof == 8) + meta.index = cast(ushort)((cast(void*)&this - meta.block.dataBegin()) >> 3); + else + meta.index = cast(ushort)((cast(void*)&this - meta.block.dataBegin()) / EntityID.sizeof()); + return meta; + } +} + +struct EntityMeta +{ + EntityManager.EntitiesBlock* block; + ushort index; + + T* getComponent(T)() const + { + return cast(T*)(cast(void*)block + block.type_info.deltas[T.component_id] + index * T.sizeof); + } } /************************************************************************************************************************ diff --git a/tests/access_perf.d b/tests/access_perf.d new file mode 100644 index 0000000..141a2e6 --- /dev/null +++ b/tests/access_perf.d @@ -0,0 +1,128 @@ +module tests.access_perf; + +import tests.runner; + +import bubel.ecs.core; +import bubel.ecs.manager; +import bubel.ecs.entity; + +import std.array : staticArray; + +import core.stdc.stdio; + +struct CLong +{ + mixin ECS.Component; + + alias value this; + + long value = 10; +} + +struct CInt +{ + mixin ECS.Component; + + alias value this; + + int value = 10; +} + +struct CUInt +{ + mixin ECS.Component; + + alias value this; + + uint value = 12; +} + +struct CBig +{ + mixin ECS.Component; + uint[32] data; +} + +EntityTemplate* tmpl; + +void beforeEveryTest() +{ + gEM.initialize(0); + + gEM.beginRegister(); + + gEM.registerComponent!CLong; + gEM.registerComponent!CInt; + gEM.registerComponent!CUInt; + gEM.registerComponent!CBig; + + gEM.endRegister(); + + tmpl = gEM.allocateTemplate([CLong.component_id, CInt.component_id, CUInt.component_id, CBig.component_id].staticArray); + foreach(i; 0 .. 100_000)gEM.addEntity(tmpl); +} + +void afterEveryTest() +{ + if(tmpl)gEM.freeTemplate(tmpl); + tmpl = null; + gEM.destroy(); +} + +@("DirectAccess100k1comp") +unittest +{ + foreach(i;0..25000) + { + Entity* entity = gEM.getEntity(EntityID(i*4+1,0)); + CUInt* comp1 = entity.getComponent!CUInt; + comp1.value = 4; + } +} + +@("DirectAccess100k4comp") +unittest +{ + foreach(i;0..25000) + { + Entity* entity = gEM.getEntity(EntityID(i*4+1,0)); + CUInt* comp1 = entity.getComponent!CUInt; + comp1.value = 4; + CInt* comp2 = entity.getComponent!CInt; + comp2.value = 3; + CLong* comp3 = entity.getComponent!CLong; + comp3.value = 1; + CBig* comp4 = entity.getComponent!CBig; + comp4.data[0] = 2; + } +} + +@("DirectAccess100k1compWithMeta") +unittest +{ + foreach(i;0..25000) + { + Entity* entity = gEM.getEntity(EntityID(i*4+1,0)); + EntityMeta meta = entity.getMeta(); + CUInt* comp1 = meta.getComponent!CUInt; + comp1.value = 4; + } +} + +@("DirectAccess100k4compWithMeta") +unittest +{ + foreach(i;0..25000) + { + Entity* entity = gEM.getEntity(EntityID(i*4+1,0)); + EntityMeta meta = entity.getMeta(); + CUInt* comp1 = meta.getComponent!CUInt; + comp1.value = 4; + CInt* comp2 = meta.getComponent!CInt; + comp2.value = 3; + CLong* comp3 = meta.getComponent!CLong; + comp3.value = 1; + CBig* comp4 = meta.getComponent!CBig; + comp4.data[0] = 2; + } +} \ No newline at end of file diff --git a/tests/perf.d b/tests/perf.d new file mode 100644 index 0000000..135dc54 --- /dev/null +++ b/tests/perf.d @@ -0,0 +1,178 @@ +module tests.perf; + +import tests.runner; + +import bubel.ecs.core; +import bubel.ecs.manager; +import bubel.ecs.entity; + +import std.array : staticArray; + +import core.stdc.stdio; + +struct CLong +{ + mixin ECS.Component; + + alias value this; + + long value = 10; +} + +struct CShort +{ + mixin ECS.Component; + + alias value this; + + short value = 12; +} + +struct CInt +{ + mixin ECS.Component; + + alias value this; + + int value = 10; +} + +struct CUInt +{ + mixin ECS.Component; + + alias value this; + + uint value = 12; +} + +struct CBig +{ + mixin ECS.Component; + uint[32] data; +} + +EntityTemplate* tmpl; + +void beforeEveryTest() +{ + gEM.initialize(0); + + gEM.beginRegister(); + + gEM.registerComponent!CLong; + gEM.registerComponent!CShort; + gEM.registerComponent!CInt; + gEM.registerComponent!CUInt; + gEM.registerComponent!CBig; + + gEM.endRegister(); + tmpl = null; +} + +void afterEveryTest() +{ + if(tmpl)gEM.freeTemplate(tmpl); + tmpl = null; + gEM.destroy(); +} + +void smallTmpl() +{ + tmpl = gEM.allocateTemplate([CShort.component_id].staticArray); +} + +void bigTmpl() +{ + tmpl = gEM.allocateTemplate([CBig.component_id].staticArray); +} + +void multiSmallTmpl() +{ + tmpl = gEM.allocateTemplate([CShort.component_id, CLong.component_id, CInt.component_id, CUInt.component_id].staticArray); +} + +void multiBigTmpl() +{ + tmpl = gEM.allocateTemplate([CLong.component_id, CInt.component_id, CUInt.component_id, CBig.component_id].staticArray); +} + +@("AddEntities100k1comp2b") @(before, &smallTmpl) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k1comp128b") @(before, &bigTmpl) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k4comp18b") @(before, &multiSmallTmpl) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k4comp144b") @(before, &multiBigTmpl) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +void allocDealloc100k() +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); + gEM.commit(); + foreach(i; 0..100_000)gEM.removeEntity(EntityID(i + 1,0)); + gEM.commit(); +} + +void smallTmplPreAlloc() +{ + tmpl = gEM.allocateTemplate([CShort.component_id].staticArray); + allocDealloc100k(); +} + +void bigTmplPreAlloc() +{ + tmpl = gEM.allocateTemplate([CBig.component_id].staticArray); + allocDealloc100k(); +} + +void multiSmallTmplPreAlloc() +{ + tmpl = gEM.allocateTemplate([CShort.component_id, CLong.component_id, CInt.component_id, CUInt.component_id].staticArray); + allocDealloc100k(); +} + +void multiBigTmplPreAlloc() +{ + tmpl = gEM.allocateTemplate([CLong.component_id, CInt.component_id, CUInt.component_id, CBig.component_id].staticArray); + allocDealloc100k(); +} + +@("AddEntities100k1comp2bPreAlloc") @(before, &smallTmplPreAlloc) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k1comp128bPreAlloc") @(before, &bigTmplPreAlloc) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k4comp18bPreAlloc") @(before, &multiSmallTmplPreAlloc) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} + +@("AddEntities100k4comp144bPreAlloc") @(before, &multiBigTmplPreAlloc) +unittest +{ + foreach(i; 0..100_000)gEM.addEntity(tmpl); +} \ No newline at end of file diff --git a/tests/runner.d b/tests/runner.d index 4aa9bcb..f299664 100644 --- a/tests/runner.d +++ b/tests/runner.d @@ -9,8 +9,19 @@ import bubel.ecs.vector; import bubel.ecs.simple_vector; import bubel.ecs.std; +import std.traits; + import tests.time; +version (LDC) +{ + import ldc.attributes; +} +else +{ + enum optStrategy = 0; +} + enum int ASSERTED = 123; enum string OUT_FILE = "test_report.xml"; @@ -124,42 +135,65 @@ struct TestRunner(Args...) write(""); } + @(optStrategy,"none") void runTests(string[] include = null, string[] exclude = null) { foreach (i, module_; Args) { TestSuite* suite = &suites[i]; suite.name = module_.stringof; + + void function() before; + void function() after; + foreach (index, unittest_; __traits(getUnitTests, module_)) { enum attributes = __traits(getAttributes, unittest_); + static if (attributes.length != 0) { - if (include.length > 0) + foreach(attr_id, attr; attributes) { - bool matched = false; - foreach (str; include) + static if(isFunctionPointer!(attr)){} + else static if(attr == "_tr_before") { - if (match(str, attributes[0])) - { - matched = true; - break; - } + static assert(attr_id+1 < attributes.length); + enum attr2 = attributes[attr_id + 1]; + //static assert(__traits(hasMember, module_, attr2)); + //alias func = __traits(getMember, module_, attr2); + //attr2(); + before = attr2; + //static assert(is(typeof(__traits(getMember, module_, attr2)) == void function())); } - - foreach (str; exclude) + else { - if (match(str, attributes[0])) + if (include.length > 0) { - matched = false; - break; - } - } + bool matched = false; + foreach (str; include) + { + if (match(str, attr)) + { + matched = true; + break; + } + } - if (!matched) - { - suite.skipped++; - continue; + foreach (str; exclude) + { + if (match(str, attr)) + { + matched = false; + break; + } + } + + if (!matched) + { + suite.skipped++; + continue; + } + } } } } @@ -178,6 +212,7 @@ struct TestRunner(Args...) static if (__traits(hasMember, module_, "beforeEveryTest")) module_.beforeEveryTest(); + if(before)before(); version(D_BetterC) { @@ -318,6 +353,8 @@ version (notBetterC) version (D_Coverage) extern (C) void dmd_coverDestPath(string path); } +enum before = "_tr_before"; + void extractStrings(ref Vector!string container, string str) { uint s = 0; @@ -383,7 +420,7 @@ extern (C) int main(int argc, char** args) } } - TestRunner!(tests.runner, tests.id_manager, tests.vector, tests.basic) runner; + TestRunner!(tests.id_manager, tests.vector, tests.basic, tests.perf, tests.access_perf) runner; runner.runTests(include[], exclude[]);