Skip to content

Instantly share code, notes, and snippets.

@SanderMertens
Last active December 10, 2023 19:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SanderMertens/400562cc6f84f74ecccbc0b9967cf62d to your computer and use it in GitHub Desktop.
Save SanderMertens/400562cc6f84f74ecccbc0b9967cf62d to your computer and use it in GitHub Desktop.
#include <bench_path.h>
#include <stdio.h>
#include <stdlib.h>
#include "entt.hpp"
#define MILLION (1000 * 1000)
#define BILLION (1000 * MILLION)
#define ENTITY_COUNT (255)
#define COMPONENT_COUNT (10)
#define QUERY_COUNT (3)
#define SAMPLE_COUNT (100)
template <int C>
struct Type { };
bool flip_coin(void) {
int r = rand();
return r >= ((float)RAND_MAX / 2.0f);
}
void report(const char *query_name, double sum, double min, double max,
int64_t entity_count, uint64_t entity_sum)
{
sum /= (double)SAMPLE_COUNT;
entity_count /= SAMPLE_COUNT;
ecs_trace(" total min: %.2fus, avg: %.2fus, max: %.2fus (#[red]%s#[reset])",
min * MILLION, sum * MILLION, max * MILLION, query_name, entity_sum);
min /= (double)entity_count;
sum /= (double)entity_count;
max /= (double)entity_count;
ecs_trace(" per entity: min: %.2fns, avg: %.2fns, max: %.2fns",
min * BILLION, sum * BILLION, max * BILLION);
ecs_trace("");
}
template <typename ... Types>
void bench_view(entt::registry& reg) {
auto view = reg.view<Types...>();
/* Warmup */
view.each([&](const auto entity) { });
ecs_time_t t = {0};
uint64_t entity_sum = 0;
uint64_t entity_count = 0;
double sum = 0, min = 10e10, max = 0;
for (int s = 0; s < SAMPLE_COUNT; s ++) {
ecs_time_measure(&t);
view.each([&](const auto entity) {
entity_sum += static_cast<uint64_t>(entity);
entity_count ++;
});
double v = ecs_time_measure(&t);
min = v < min ? v : min;
max = v > max ? v : max;
sum += v;
}
report("EnTT - View", sum, min, max, entity_count, entity_sum);
}
int main(int argc, char *argv[]) {
entt::registry reg;
ecs_world_t *world = ecs_mini();
ecs_log_set_level(0);
ecs_time_t t = {0};
// Create component ids
ecs_time_measure(&t);
ecs_entity_t *ids = ecs_os_malloc_n(ecs_entity_t, COMPONENT_COUNT);
for (int i = 0; i < COMPONENT_COUNT; i ++) {
ids[i] = ecs_new_id(world);
}
// Record table count before creating entities
const ecs_world_info_t *info = ecs_get_world_info(world);
int32_t table_count = info->table_count;
// Create entities
for (int i = 0; i < ENTITY_COUNT; i ++) {
ecs_entity_t e = ecs_new_id(world);
entt::entity entt_e = reg.create();
for (int c = 0; c < COMPONENT_COUNT; c ++) {
if (flip_coin()) {
ecs_add_id(world, e, ids[c]);
switch(c) {
case 0: reg.emplace<Type<0>>(entt_e); break;
case 1: reg.emplace<Type<1>>(entt_e); break;
case 2: reg.emplace<Type<2>>(entt_e); break;
case 3: reg.emplace<Type<3>>(entt_e); break;
case 4: reg.emplace<Type<4>>(entt_e); break;
case 5: reg.emplace<Type<5>>(entt_e); break;
case 6: reg.emplace<Type<6>>(entt_e); break;
case 7: reg.emplace<Type<7>>(entt_e); break;
case 8: reg.emplace<Type<8>>(entt_e); break;
case 9: reg.emplace<Type<9>>(entt_e); break;
case 10: reg.emplace<Type<10>>(entt_e); break;
case 11: reg.emplace<Type<11>>(entt_e); break;
case 12: reg.emplace<Type<12>>(entt_e); break;
case 13: reg.emplace<Type<13>>(entt_e); break;
case 14: reg.emplace<Type<14>>(entt_e); break;
case 15: reg.emplace<Type<15>>(entt_e); break;
default: ecs_os_abort();
}
}
}
}
// Report # of created tables
printf("\n");
ecs_trace("entities created: %d (w/%d randomized components)", ENTITY_COUNT, COMPONENT_COUNT);
ecs_trace("tables created : %d", info->table_count - table_count);
ecs_trace("");
ecs_trace("querying for %d components", QUERY_COUNT);
ecs_trace("taking %d samples", SAMPLE_COUNT);
ecs_trace("");
// Create query for N components
ecs_filter_desc_t fd = { 0 };
for (int i = 0; i < QUERY_COUNT; i ++) {
fd.terms[i].id = ids[i];
fd.terms[i].src.flags = EcsSelf;
}
/* Cached query */
{
ecs_query_desc_t qd = {0};
qd.filter = fd;
/* Warmup */
ecs_query_t *f = ecs_query_init(world, &qd);
{
ecs_iter_t it = ecs_query_iter(world, f);
while ( ecs_query_next(&it) ) { }
}
uint64_t entity_sum = 0, entity_count = 0;
double sum = 0, min = 10e10, max = 0;
for (int s = 0; s < SAMPLE_COUNT; s ++) {
ecs_iter_t it = ecs_query_iter(world, f);
ecs_time_measure(&t);
while ( ecs_query_next(&it) ) {
for (int i = 0; i < it.count; i ++) {
entity_sum += it.entities[i];
entity_count ++;
}
}
double v = ecs_time_measure(&t);
min = v < min ? v : min;
max = v > max ? v : max;
sum += v;
}
report("Flecs - Cached", sum, min, max, entity_count, entity_sum);
ecs_query_fini(f);
}
/* Uncached query (rule) */
{
/* Warmup */
ecs_rule_t *f = ecs_rule_init(world, &fd);
{
ecs_iter_t it = ecs_rule_iter(world, f);
while ( ecs_rule_next(&it) ) { }
}
uint64_t entity_sum = 0, entity_count = 0;
double sum = 0, min = 10e10, max = 0;
for (int s = 0; s < SAMPLE_COUNT; s ++) {
ecs_iter_t it = ecs_rule_iter(world, f);
ecs_time_measure(&t);
while ( ecs_rule_next_instanced(&it) ) {
for (int i = 0; i < it.count; i ++) {
entity_sum += it.entities[i];
entity_count ++;
}
}
double v = ecs_time_measure(&t);
min = v < min ? v : min;
max = v > max ? v : max;
sum += v;
}
report("Flecs - Uncached", sum, min, max, entity_count, entity_sum);
ecs_rule_fini(f);
}
/* EnTT view */
if (QUERY_COUNT == 1) {
bench_view<Type<0>>(reg);
} else if (QUERY_COUNT == 2) {
bench_view<Type<0>, Type<1>>(reg);
} else if (QUERY_COUNT == 3) {
bench_view<Type<0>, Type<1>, Type<2>>(reg);
} else if (QUERY_COUNT == 4) {
bench_view<Type<0>, Type<1>, Type<2>, Type<3>>(reg);
} else if (QUERY_COUNT == 5) {
bench_view<Type<0>, Type<1>, Type<2>, Type<3>, Type<4>>(reg);
} else if (QUERY_COUNT == 6) {
bench_view<Type<0>, Type<1>, Type<2>, Type<3>, Type<4>, Type<5>>(reg);
}
ecs_log_set_level(-1);
return ecs_fini(world);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment