Skip to content

Commit

Permalink
Merge branch 'release/5.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
abeimler committed Oct 3, 2023
2 parents 19a6e24 + 9fe5eb5 commit 074f60f
Show file tree
Hide file tree
Showing 120 changed files with 1,983 additions and 364 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ run_vcpkg()
# Set the project name and language
project(
ecs-benchmark
VERSION 4.7.4
VERSION 5.0.0
DESCRIPTION "Benchmarks of common ECS (Entity-Component-System)-Frameworks in C++ (or C)"
HOMEPAGE_URL "https://github.com/abeimler/ecs_benchmark"
LANGUAGES CXX C)
Expand Down
373 changes: 192 additions & 181 deletions README.md

Large diffs are not rendered by default.

47 changes: 29 additions & 18 deletions README.md.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
![License](https://img.shields.io/github/license/abeimler/ecs_benchmark)
![Standard](https://img.shields.io/badge/c%2B%2B-20-blue)

This repository contains a collection of benchmarks for popular Entity-Component-System (ECS) frameworks. The benchmarks cover different aspects of ECS frameworks, such as update systems, component additions/removals, and entity creation/destruction.
Each benchmark is performed on three simple components and three small systems, which can be scaled to hundreds of components and systems in a real-world scenario. The results of the benchmarks are displayed in tables and charts, allowing you to compare the performance of different ECS frameworks in different areas.
It's important to note that different ECS frameworks have different strengths and weaknesses. For example, some frameworks might excel in adding/removing components, while others might be better at creating/destroying entities. Therefore, it's crucial to choose an ECS framework based on your specific requirements.
This repository contains a collection of benchmarks for popular Entity-Component-System (ECS) frameworks.
The benchmarks cover different aspects of ECS frameworks, such as update systems, component additions/removals, and entity creation/destruction.
It's important to note that different ECS frameworks have different strengths and weaknesses.
For example, some frameworks might excel in adding/removing components, while others might be better at creating/destroying entities.
Therefore, it's crucial to choose an ECS framework based on your specific requirements.

ECS (Entity-Component-System) Frameworks:

Expand All @@ -20,23 +22,24 @@ ECS (Entity-Component-System) Frameworks:
## TL;DR Results

The benchmark results are displayed in tables and charts,
allowing you to quickly compare the performance of different ECS frameworks in different scenarios. The tables include the time in nanoseconds it takes to perform the benchmark on different numbers of entities,
while the charts provide a visual representation of the results.
allowing you to quickly compare the performance of different ECS frameworks in different scenarios.
When using ECS frameworks, it's important to benchmark your specific use case and compare the results.
Therefore, the results of these benchmarks should be used as a starting point for your own benchmarking efforts.

### Update systems (for-each entities (with mixed components) in 3 systems)

### Update systems (for-each entities (with mixed components) in 5 systems)

{{{ComplexSystemsUpdateMixedEntities}}}


While this benchmark only includes three simple components and three small systems,
While this benchmark only includes up to 5 simple components and 5 small systems,
it's important to note that Entity-Component-Systems can become much more complex in the wild,
with hundreds of components and systems.
Therefore, it's crucial to always benchmark your specific cases and systems when necessary and compare results.
Different ECS frameworks excel in different areas, such as faster adding/removing of components or creating/destroying entities.
Therefore, it's essential to choose an ECS framework based on its features.
For example, EnTT offers [resource management](https://github.com/skypjack/entt/wiki/Crash-Course:-resource-management) and [event handling](https://github.com/skypjack/entt/wiki/Crash-Course:-events,-signals-and-everything-in-between), while flecs provides useful [add-ons](https://github.com/SanderMertens/flecs#addons), and EntityX includes a built-in [world/system manager](https://github.com/alecthomas/entityx#manager-tying-it-all-together=).
Choose an ECS framework based on its features,
for example, EnTT offers [resource management](https://github.com/skypjack/entt/wiki/Crash-Course:-resource-management) and [event handling](https://github.com/skypjack/entt/wiki/Crash-Course:-events,-signals-and-everything-in-between),
while flecs provides useful [add-ons](https://github.com/SanderMertens/flecs#addons),
and EntityX includes a built-in [world/system manager](https://github.com/alecthomas/entityx#manager-tying-it-all-together=).

To evaluate a framework, look at its examples and API design, and pick the one that suits your needs the best.

Expand All @@ -52,15 +55,19 @@ Each framework has its own sub-project in the [`src/`](src) directory and must i

#### Components

1. `PositionComponent` with `x` and `y` coord.
2. `VelocityComponent` with `x` and `y` for movement.
3. `DataComponent` with some nonsense data.
1. `PositionComponent`: includes `x` and `y` coordinates.
2. `VelocityComponent`: includes `x` and `y` coordinates for movement.
3. `DataComponent`: includes some arbitrary data.
4. `HealthComponent`: Hero/Monster data includes HP/MaxHP and status.
5. `DamageComponent`: Hero/Monster data includes damage.

#### Systems

1. `MovementSystem`: updates `PositionComponent` with (const) `VelocityComponent`
2. `DataSystem`: updates `DataComponent` with nonsense
3. `MoreComplexSystem`: updates Components with random data and nonsense
1. `MovementSystem`: updates the `PositionComponent` with a constant `VelocityComponent`.
2. `DataSystem`: updates the `DataComponent` with arbitrary data.
3. `MoreComplexSystem`: updates components with random data and arbitrary information.
4. `HealthSystem`: update Hero/Monster health (update HP and status).
5. `DamageSystem`: update Hero/Monster health by taking damage.


## Additional Benchmarks
Expand Down Expand Up @@ -162,7 +169,7 @@ Benchmarks for more common features, such as "Creating entities", "Adding and re
* Not every entity has all three components, some got removed


### Update systems (for-each entities in 3 systems)
### Update systems (for-each entities in 5 systems)

{{{ComplexSystemsUpdate}}}

Expand All @@ -172,6 +179,8 @@ Benchmarks for more common features, such as "Creating entities", "Adding and re
1. `MovementSystem`
2. `DataSystem`
3. `MoreComplexSystem`
4. `HealthSystem`
5. `DamageSystem`
* \* EnTT iterate components via [views](https://github.com/skypjack/entt/wiki/Crash-Course:-entity-component-system#views=)
* \** EnTT iterate components via [runtime views](https://github.com/skypjack/entt/wiki/Crash-Course:-entity-component-system#runtime-views=)
* \*** EnTT iterate components via [groups](https://github.com/skypjack/entt/wiki/Crash-Course:-entity-component-system#groups=):
Expand All @@ -183,7 +192,7 @@ Benchmarks for more common features, such as "Creating entities", "Adding and re



### Update systems (for-each entities (with mixed components) in 3 systems)
### Update systems (for-each entities (with mixed components) in 5 systems)

{{{ComplexSystemsUpdateMixedEntities}}}

Expand All @@ -193,6 +202,8 @@ Benchmarks for more common features, such as "Creating entities", "Adding and re
1. `MovementSystem`
2. `DataSystem`
3. `MoreComplexSystem`
4. `HealthSystem`
5. `DamageSystem`
* Not every entity has all three components, some got removed
* \* EnTT iterate components via [views](https://github.com/skypjack/entt/wiki/Crash-Course:-entity-component-system#views=)
* \** EnTT iterate components via [runtime views](https://github.com/skypjack/entt/wiki/Crash-Course:-entity-component-system#runtime-views=)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.7.4
5.0.0
2 changes: 2 additions & 0 deletions benchmark/benchmarks/BaseECSBenchmark.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class BaseECSBenchmark {
state.counters["components_one"] = static_cast<double>(components_counter.component_one_count);
state.counters["components_two"] = static_cast<double>(components_counter.component_two_count);
state.counters["components_three"] = static_cast<double>(components_counter.component_three_count);
state.counters["hero_count"] = static_cast<double>(components_counter.hero_count);
state.counters["monster_count"] = static_cast<double>(components_counter.monster_count);
}


Expand Down
94 changes: 76 additions & 18 deletions benchmark/benchmarks/ECSBenchmark.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "BaseECSBenchmark.h"
#include "EntityBenchmark.h"
#include "base/components/HeroMonsterComponents.h"
#include "basic.h"
#include <algorithm>
#include <benchmark/benchmark.h>
Expand All @@ -14,7 +15,10 @@

namespace ecs::benchmarks::base {

template <StringLiteral Name, class Application, class EntityFactory, bool include_entity_benchmarks = false>
enum class ECSBenchmarkIncludeEntityBenchmarks : bool { No = false, Yes = true };

template <StringLiteral Name, class Application, class EntityFactory, class HeroMonsterEntityFactory,
ECSBenchmarkIncludeEntityBenchmarks include_entity_benchmarks = ECSBenchmarkIncludeEntityBenchmarks::No>
class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
public:
using EntityManager = typename EntityFactory::EntityManager;
Expand Down Expand Up @@ -65,7 +69,22 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
const auto nentities = static_cast<size_t>(state.range(0));
std::vector<Entity> entities;
Application app(m_options.add_more_complex_system);
const ComponentsCounter components_counter = this->initApplicationWithEntities(app, nentities, entities);
ComponentsCounter components_counter = this->initApplicationWithEntities(app, nentities, entities);
for (auto entity : entities) {
using namespace ecs::benchmarks::base::components;
m_hero_monster_entities_factory.addComponents(app.getEntities(), entity);
const auto type = m_hero_monster_entities_factory.initComponents(app.getEntities(), entity);
switch (type) {
case PlayerType::Hero:
components_counter.hero_count++;
break;
case PlayerType::Monster:
components_counter.monster_count++;
break;
case PlayerType::NPC:
break;
}
}
for (auto _ : state) {
app.update(fakeTimeDelta);
}
Expand All @@ -78,8 +97,39 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
const auto nentities = static_cast<size_t>(state.range(0));
std::vector<Entity> entities;
Application app(m_options.add_more_complex_system);
const ComponentsCounter components_counter =
ComponentsCounter components_counter =
this->template initApplicationWithMixedComponents<EntityFactory>(app, nentities, entities);
for (size_t i = 0, j = 0; i < entities.size(); i++) {
auto entity = entities[i];
if (nentities >= 100 || i >= nentities / 8) {
if (nentities >= 100 || (j % 2) == 0U) {
using namespace ecs::benchmarks::base::components;
if ((i % 6) == 0U) {
m_hero_monster_entities_factory.addComponents(app.getEntities(), entity);
const auto type = m_hero_monster_entities_factory.initComponents(app.getEntities(), entity);
switch (type) {
case PlayerType::Hero:
components_counter.hero_count++;
break;
case PlayerType::Monster:
components_counter.monster_count++;
break;
case PlayerType::NPC:
break;
}
} else if ((i % 4) == 0U) {
m_hero_monster_entities_factory.addComponents(app.getEntities(), entity);
m_hero_monster_entities_factory.initComponents(app.getEntities(), entity, PlayerType::Hero);
components_counter.hero_count++;
} else if ((i % 2) == 0U) {
m_hero_monster_entities_factory.addComponents(app.getEntities(), entity);
m_hero_monster_entities_factory.initComponents(app.getEntities(), entity, PlayerType::Monster);
components_counter.monster_count++;
}
}
j++;
}
}
for (auto _ : state) {
app.update(fakeTimeDelta);
}
Expand All @@ -90,7 +140,7 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {


template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes)
void BM_CreateNoEntities(benchmark::State& state) {
const auto nentities = 0;
for (auto _ : state) {
Expand All @@ -107,7 +157,7 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes)
void BM_CreateEmptyEntities(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
for (auto _ : state) {
Expand All @@ -124,7 +174,7 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks && HasBulkFeature<tEntityFactory>
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes && HasBulkFeature<tEntityFactory>)
void BM_CreateEmptyEntitiesInBulk(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
for (auto _ : state) {
Expand All @@ -141,7 +191,7 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes)
void BM_CreateEntities(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
for (auto _ : state) {
Expand All @@ -158,7 +208,8 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks && HasBulkFeatureWithOutput<tEntityFactory>
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes &&
HasBulkFeatureWithOutput<tEntityFactory>)
void BM_CreateEntitiesInBulk(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
for (auto _ : state) {
Expand All @@ -176,7 +227,7 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {


template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks && HasDestroyFeature<tEntityFactory>
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes && HasDestroyFeature<tEntityFactory>)
void BM_DestroyEntities(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
for (auto _ : state) {
Expand All @@ -202,7 +253,8 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks && HasBulkDestroyFeature<tEntityFactory>
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes &&
HasBulkDestroyFeature<tEntityFactory>)
void BM_DestroyEntitiesInBulk(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
for (auto _ : state) {
Expand All @@ -221,7 +273,8 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {


template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks && HasGetComponentsFeature<tEntityFactory>
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes &&
HasGetComponentsFeature<tEntityFactory>)
void BM_UnpackNoComponent(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
Application app(m_options.add_more_complex_system);
Expand All @@ -239,7 +292,8 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks && HasGetComponentsFeature<tEntityFactory>
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes &&
HasGetComponentsFeature<tEntityFactory>)
void BM_UnpackOneComponent_NoEntities(benchmark::State& state) {
const auto nentities = 0;
Application app(m_options.add_more_complex_system);
Expand All @@ -257,7 +311,8 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks && HasGetComponentsFeature<tEntityFactory>
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes &&
HasGetComponentsFeature<tEntityFactory>)
void BM_UnpackOneComponent(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
Application app(m_options.add_more_complex_system);
Expand All @@ -275,8 +330,8 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

// template <class tEntityFactory = EntityFactory>
// requires include_entity_benchmarks && HasGetComponentsFeature<tEntityFactory>
// void BM_UnpackOneConstComponent(benchmark::State& state) {
// requires (include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes &&
// HasGetComponentsFeature<tEntityFactory>) void BM_UnpackOneConstComponent(benchmark::State& state) {
// const auto nentities = static_cast<size_t>(state.range(0));
// Application app(m_options.add_more_complex_system);
// EntityManager& registry = app.getEntities();
Expand All @@ -293,7 +348,8 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
// }

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks && HasGetComponentsFeature<tEntityFactory>
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes &&
HasGetComponentsFeature<tEntityFactory>)
void BM_UnpackTwoComponents(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
Application app(m_options.add_more_complex_system);
Expand All @@ -312,7 +368,8 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks && HasGetComponentsFeature<tEntityFactory>
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes &&
HasGetComponentsFeature<tEntityFactory>)
void BM_UnpackThreeComponents(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
Application app(m_options.add_more_complex_system);
Expand All @@ -331,7 +388,7 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
}

template <class tEntityFactory = EntityFactory>
requires include_entity_benchmarks
requires(include_entity_benchmarks == ECSBenchmarkIncludeEntityBenchmarks::Yes)
void BM_RemoveAddComponent(benchmark::State& state) {
const auto nentities = static_cast<size_t>(state.range(0));
Application app(m_options.add_more_complex_system);
Expand Down Expand Up @@ -424,6 +481,7 @@ class ECSBenchmark : protected BaseECSBenchmark<EntityFactory> {
inline static constexpr auto m_name{Name.value};
const ESCBenchmarkOptions m_options;
EntityFactory m_entities_factory;
HeroMonsterEntityFactory m_hero_monster_entities_factory;
};
} // namespace ecs::benchmarks::base

Expand Down
Loading

0 comments on commit 074f60f

Please sign in to comment.