Add benchmarks
This commit is contained in:
parent
72f635400a
commit
acc03a75aa
1 changed files with 437 additions and 31 deletions
468
tests/zecsy.cpp
468
tests/zecsy.cpp
|
@ -7,7 +7,7 @@
|
|||
|
||||
using namespace zecsy;
|
||||
|
||||
TEST_CASE("Create a single entity and verify its existence")
|
||||
TEST_CASE("Create a single entity and verify its existence", "[test]")
|
||||
{
|
||||
world w;
|
||||
|
||||
|
@ -16,7 +16,8 @@ TEST_CASE("Create a single entity and verify its existence")
|
|||
REQUIRE(w.is_alive(e));
|
||||
}
|
||||
|
||||
TEST_CASE("Destroy an entity and ensure it no longer exists in the world")
|
||||
TEST_CASE("Destroy an entity and ensure it no longer exists in the world",
|
||||
"[test]")
|
||||
{
|
||||
world w;
|
||||
|
||||
|
@ -26,7 +27,7 @@ TEST_CASE("Destroy an entity and ensure it no longer exists in the world")
|
|||
REQUIRE_FALSE(w.is_alive(e));
|
||||
}
|
||||
|
||||
TEST_CASE("Entity #0 should be reserved and never used")
|
||||
TEST_CASE("Entity #0 should be reserved and never used", "[test]")
|
||||
{
|
||||
world w;
|
||||
|
||||
|
@ -40,7 +41,8 @@ struct ChoosenOne
|
|||
{
|
||||
};
|
||||
|
||||
TEST_CASE("Entity shouldn't have a component that wasn't attached to it")
|
||||
TEST_CASE("Entity shouldn't have a component that wasn't attached to it",
|
||||
"[test]")
|
||||
{
|
||||
world w;
|
||||
|
||||
|
@ -49,7 +51,7 @@ TEST_CASE("Entity shouldn't have a component that wasn't attached to it")
|
|||
REQUIRE_FALSE(w.has<ChoosenOne>(e));
|
||||
}
|
||||
|
||||
TEST_CASE("Attempt of getting non-owned component should throw")
|
||||
TEST_CASE("Attempt of getting non-owned component should throw", "[test]")
|
||||
{
|
||||
world w;
|
||||
|
||||
|
@ -59,7 +61,8 @@ TEST_CASE("Attempt of getting non-owned component should throw")
|
|||
}
|
||||
|
||||
TEST_CASE("Attach a simple component to an entity and verify it is correctly "
|
||||
"associated")
|
||||
"associated",
|
||||
"[test]")
|
||||
{
|
||||
world w;
|
||||
|
||||
|
@ -80,7 +83,8 @@ struct Comp
|
|||
};
|
||||
|
||||
TEST_CASE("Retrieve a component from an entity and verify its data matches "
|
||||
"what was set")
|
||||
"what was set",
|
||||
"[test]")
|
||||
{
|
||||
world w;
|
||||
|
||||
|
@ -95,7 +99,8 @@ TEST_CASE("Retrieve a component from an entity and verify its data matches "
|
|||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Remove a component from an entity and verify it is no longer attached")
|
||||
"Remove a component from an entity and verify it is no longer attached",
|
||||
"[test]")
|
||||
{
|
||||
world w;
|
||||
|
||||
|
@ -111,7 +116,7 @@ TEST_CASE(
|
|||
REQUIRE_FALSE(w.has<ChoosenOne>(e));
|
||||
}
|
||||
|
||||
TEST_CASE("Addresses of removed components should be reused")
|
||||
TEST_CASE("Addresses of removed components should be reused", "[test]")
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
|
@ -156,7 +161,8 @@ TEST_CASE("Addresses of removed components should be reused")
|
|||
}
|
||||
|
||||
TEST_CASE("Attach multiple components to an entity and verify all are "
|
||||
"correctly stored and retrievable")
|
||||
"correctly stored and retrievable",
|
||||
"[test]")
|
||||
{
|
||||
world w;
|
||||
|
||||
|
@ -173,7 +179,8 @@ TEST_CASE("Attach multiple components to an entity and verify all are "
|
|||
}
|
||||
|
||||
TEST_CASE("Create a simple system that processes entities with a specific "
|
||||
"component and verify it executes correctly")
|
||||
"component and verify it executes correctly",
|
||||
"[test]")
|
||||
{
|
||||
struct Component
|
||||
{
|
||||
|
@ -200,7 +207,8 @@ TEST_CASE("Create a simple system that processes entities with a specific "
|
|||
}
|
||||
|
||||
TEST_CASE("Test a systems ability to query and process only entities with a "
|
||||
"specific combination of components")
|
||||
"specific combination of components",
|
||||
"[test]")
|
||||
{
|
||||
struct C0
|
||||
{
|
||||
|
@ -226,12 +234,13 @@ TEST_CASE("Test a systems ability to query and process only entities with a "
|
|||
REQUIRE(w.get<C0>(e0).value == 0);
|
||||
REQUIRE(w.get<C1>(e0).value == 10);
|
||||
|
||||
w.query<C0, C1>([e0](entity_id e, C0& c0, C1& c1)
|
||||
{
|
||||
REQUIRE(e == e0);
|
||||
c0.value++;
|
||||
c1.value++;
|
||||
});
|
||||
w.query<C0, C1>(
|
||||
[e0](entity_id e, C0& c0, C1& c1)
|
||||
{
|
||||
REQUIRE(e == e0);
|
||||
c0.value++;
|
||||
c1.value++;
|
||||
});
|
||||
|
||||
REQUIRE(w.get<C0>(e0).value == 1);
|
||||
REQUIRE(w.get<C1>(e0).value == 11);
|
||||
|
@ -243,7 +252,7 @@ TEST_CASE("Test a systems ability to query and process only entities with a "
|
|||
REQUIRE_FALSE(w.has<C0>(e2));
|
||||
}
|
||||
|
||||
TEST_CASE("Systems execute at correct frequencies")
|
||||
TEST_CASE("Systems execute at correct frequencies", "[test]")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
@ -268,7 +277,7 @@ TEST_CASE("Systems execute at correct frequencies")
|
|||
REQUIRE(slow_count == 2); // 1 Hz system should execute 1 time
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle zero-frequency gracefully")
|
||||
TEST_CASE("Systems handle zero-frequency gracefully", "[test]")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
@ -288,7 +297,7 @@ TEST_CASE("Systems handle zero-frequency gracefully")
|
|||
REQUIRE(zero_count == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle varying update rates")
|
||||
TEST_CASE("Systems handle varying update rates", "[test]")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
@ -308,7 +317,7 @@ TEST_CASE("Systems handle varying update rates")
|
|||
REQUIRE(varying_count == 10);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle large time steps")
|
||||
TEST_CASE("Systems handle large time steps", "[test]")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
@ -325,7 +334,7 @@ TEST_CASE("Systems handle large time steps")
|
|||
REQUIRE(large_step_count == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle multiple frequencies")
|
||||
TEST_CASE("Systems handle multiple frequencies", "[test]")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
@ -351,7 +360,7 @@ TEST_CASE("Systems handle multiple frequencies")
|
|||
REQUIRE(slow_count == 1); // 1 Hz system
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle fractional frequencies")
|
||||
TEST_CASE("Systems handle fractional frequencies", "[test]")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
@ -372,7 +381,7 @@ TEST_CASE("Systems handle fractional frequencies")
|
|||
REQUIRE(fractional_count == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle zero delta time")
|
||||
TEST_CASE("Systems handle zero delta time", "[test]")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
@ -389,7 +398,7 @@ TEST_CASE("Systems handle zero delta time")
|
|||
REQUIRE(zero_dt_count == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Systems handle negative delta time")
|
||||
TEST_CASE("Systems handle negative delta time", "[test]")
|
||||
{
|
||||
world w;
|
||||
system_scheduler scheduler;
|
||||
|
@ -410,7 +419,7 @@ TEST_CASE("Systems handle negative delta time")
|
|||
REQUIRE(count == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("Entity count tracking")
|
||||
TEST_CASE("Entity count tracking", "[test]")
|
||||
{
|
||||
world w;
|
||||
REQUIRE(w.entity_count() == 0);
|
||||
|
@ -423,7 +432,7 @@ TEST_CASE("Entity count tracking")
|
|||
REQUIRE(w.entity_count() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Component counting mechanisms")
|
||||
TEST_CASE("Component counting mechanisms", "[test]")
|
||||
{
|
||||
struct Health
|
||||
{
|
||||
|
@ -454,7 +463,7 @@ TEST_CASE("Component counting mechanisms")
|
|||
REQUIRE(w.total_component_count() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Archetype signature management")
|
||||
TEST_CASE("Archetype signature management", "[test]")
|
||||
{
|
||||
struct A
|
||||
{
|
||||
|
@ -509,7 +518,7 @@ TEST_CASE("Archetype signature management")
|
|||
REQUIRE(w.archetype_count() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Component distribution across archetypes")
|
||||
TEST_CASE("Component distribution across archetypes", "[test]")
|
||||
{
|
||||
struct A
|
||||
{
|
||||
|
@ -547,7 +556,7 @@ TEST_CASE("Component distribution across archetypes")
|
|||
REQUIRE(w.archetype_count() == 3); //<A>, <A, B>, <B>
|
||||
}
|
||||
|
||||
TEST_CASE("Entity inspection")
|
||||
TEST_CASE("Entity inspection", "[test]")
|
||||
{
|
||||
struct Transform
|
||||
{
|
||||
|
@ -572,3 +581,400 @@ TEST_CASE("Entity inspection")
|
|||
w.remove<Transform>(e);
|
||||
REQUIRE(w.components_in_entity(e) == 1);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// Benchmark components
|
||||
struct Position
|
||||
{
|
||||
float x, y;
|
||||
};
|
||||
struct Velocity
|
||||
{
|
||||
float dx, dy;
|
||||
};
|
||||
struct Health
|
||||
{
|
||||
int value;
|
||||
};
|
||||
struct BigData
|
||||
{
|
||||
char buffer[4096];
|
||||
};
|
||||
|
||||
// Benchmark entity counts
|
||||
constexpr int SMALL = 1'000;
|
||||
constexpr int MEDIUM = 5'000;
|
||||
constexpr int LARGE = 10'000;
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Core operations benchmarks", "[benchmark]")
|
||||
{
|
||||
BENCHMARK_ADVANCED("Create entities [1000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
world w;
|
||||
for(int i = 0; i < SMALL; ++i)
|
||||
{
|
||||
w.make_entity();
|
||||
}
|
||||
return w.entity_count();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Create entities [5000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
world w;
|
||||
for(int i = 0; i < MEDIUM; ++i)
|
||||
{
|
||||
w.make_entity();
|
||||
}
|
||||
return w.entity_count();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Create entities [10000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
world w;
|
||||
for(int i = 0; i < LARGE; ++i)
|
||||
{
|
||||
w.make_entity();
|
||||
}
|
||||
return w.entity_count();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Create entities with components [1000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
world w;
|
||||
for(int i = 0; i < SMALL; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<Position>(e, {1.0f, 2.0f});
|
||||
w.set<Velocity>(e, {3.0f, 4.0f});
|
||||
}
|
||||
return w.total_component_count();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Create entities with components [5000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
world w;
|
||||
for(int i = 0; i < MEDIUM; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<Position>(e, {1.0f, 2.0f});
|
||||
w.set<Velocity>(e, {3.0f, 4.0f});
|
||||
}
|
||||
return w.total_component_count();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Create entities with components [10000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
world w;
|
||||
for(int i = 0; i < LARGE; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<Position>(e, {1.0f, 2.0f});
|
||||
w.set<Velocity>(e, {3.0f, 4.0f});
|
||||
}
|
||||
return w.total_component_count();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Component operations benchmarks", "[benchmark]")
|
||||
{
|
||||
BENCHMARK_ADVANCED("Add component to existing entities [1000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
for(int i = 0; i < SMALL; ++i)
|
||||
{
|
||||
entities.push_back(w.make_entity());
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
for(auto e: entities)
|
||||
{
|
||||
w.set<Health>(e, {100});
|
||||
}
|
||||
return w.component_count<Health>();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Add component to existing entities [5000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
for(int i = 0; i < MEDIUM; ++i)
|
||||
{
|
||||
entities.push_back(w.make_entity());
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
for(auto e: entities)
|
||||
{
|
||||
w.set<Health>(e, {100});
|
||||
}
|
||||
return w.component_count<Health>();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Add component to existing entities [10000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
for(int i = 0; i < LARGE; ++i)
|
||||
{
|
||||
entities.push_back(w.make_entity());
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
for(auto e: entities)
|
||||
{
|
||||
w.set<Health>(e, {100});
|
||||
}
|
||||
return w.component_count<Health>();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Remove component from entities [1000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
for(int i = 0; i < SMALL; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<Health>(e, {100});
|
||||
entities.push_back(e);
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
for(auto e: entities)
|
||||
{
|
||||
w.remove<Health>(e);
|
||||
}
|
||||
return w.component_count<Health>();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Remove component from entities [5000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
for(int i = 0; i < MEDIUM; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<Health>(e, {100});
|
||||
entities.push_back(e);
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
for(auto e: entities)
|
||||
{
|
||||
w.remove<Health>(e);
|
||||
}
|
||||
return w.component_count<Health>();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Remove component from entities [10000]")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
for(int i = 0; i < LARGE; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<Health>(e, {100});
|
||||
entities.push_back(e);
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
for(auto e: entities)
|
||||
{
|
||||
w.remove<Health>(e);
|
||||
}
|
||||
return w.component_count<Health>();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Query performance benchmarks", "[benchmark]")
|
||||
{
|
||||
BENCHMARK_ADVANCED("Dense query (90% match)")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
for(int i = 0; i < LARGE; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<Position>(e);
|
||||
if(i % 10 != 0)
|
||||
w.set<Velocity>(e); // 90% match
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
int count = 0;
|
||||
w.query<Position, Velocity>([&](auto...) { count++; });
|
||||
return count;
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Sparse query (10% match)")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
for(int i = 0; i < LARGE; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<Position>(e);
|
||||
if(i % 10 == 0)
|
||||
w.set<Velocity>(e); // 10% match
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
int count = 0;
|
||||
w.query<Position, Velocity>([&](auto...) { count++; });
|
||||
return count;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Memory intensive benchmarks", "[benchmark]")
|
||||
{
|
||||
BENCHMARK_ADVANCED("Large component allocation")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
world w;
|
||||
for(int i = 0; i < SMALL; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<BigData>(e);
|
||||
}
|
||||
return w.total_component_count();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Component memory reuse")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
for(int i = 0; i < SMALL; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<BigData>(e);
|
||||
entities.push_back(e);
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
// Remove and re-add components
|
||||
for(auto e: entities)
|
||||
{
|
||||
w.remove<BigData>(e);
|
||||
w.set<BigData>(e);
|
||||
}
|
||||
return w.component_count<BigData>();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Archetype transition benchmarks", "[benchmark]")
|
||||
{
|
||||
BENCHMARK_ADVANCED("Single component addition")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
for(int i = 0; i < MEDIUM; ++i)
|
||||
{
|
||||
entities.push_back(w.make_entity());
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
for(auto e: entities)
|
||||
{
|
||||
w.set<Health>(e);
|
||||
}
|
||||
return w.archetype_count();
|
||||
});
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("Multi-component transition")(
|
||||
Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
world w;
|
||||
std::vector<entity_id> entities;
|
||||
for(int i = 0; i < MEDIUM; ++i)
|
||||
{
|
||||
auto e = w.make_entity();
|
||||
w.set<Position>(e);
|
||||
entities.push_back(e);
|
||||
}
|
||||
|
||||
meter.measure(
|
||||
[&]
|
||||
{
|
||||
for(auto e: entities)
|
||||
{
|
||||
w.set<Velocity>(e);
|
||||
w.set<Health>(e);
|
||||
}
|
||||
return w.archetype_count();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue