Skip to content
This repository was archived by the owner on Nov 27, 2022. It is now read-only.

Commit 1782501

Browse files
committed
Initial benchmaks
0 parents  commit 1782501

35 files changed

+1479
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
Cargo.lock

Cargo.toml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
name = "ecs_bench_suite"
3+
version = "0.1.0"
4+
authors = ["Thomas Gillen <[email protected]>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
cgmath = { version = "0.17", feature = ["serde"] }
11+
bincode = "1.3"
12+
ron = "0.6"
13+
serde = { version = "1.0", features = ["derive"] }
14+
rayon = "1.3"
15+
legion = { git = "https://github.com/TomGillen/legion" }
16+
#legion_typeuuid = { git = "https://github.com/TomGillen/legion_typeuuid" }
17+
legion_2_4 = { package = "legion", version = "0.2.4" }
18+
bevy_ecs = "0.1"
19+
hecs = "0.2"
20+
shipyard = "0.4"
21+
22+
[dev-dependencies]
23+
criterion = "0.3"
24+
rayon = "1.3"
25+
26+
[[bench]]
27+
name = "benchmarks"
28+
harness = false

benches/benchmarks.rs

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use criterion::*;
2+
use ecs_bench_suite::*;
3+
4+
fn bench_simple_insert(c: &mut Criterion) {
5+
let mut group = c.benchmark_group("simple_insert");
6+
group.bench_function("legion", |b| {
7+
let mut bench = legion::simple_insert::Benchmark::new();
8+
b.iter(move || bench.run());
9+
});
10+
group.bench_function("legion_0.2.4", |b| {
11+
let mut bench = legion_2_4::simple_insert::Benchmark::new();
12+
b.iter(move || bench.run());
13+
});
14+
group.bench_function("bevy", |b| {
15+
let mut bench = bevy::simple_insert::Benchmark::new();
16+
b.iter(move || bench.run());
17+
});
18+
group.bench_function("shipyard", |b| {
19+
let mut bench = shipyard::simple_insert::Benchmark::new();
20+
b.iter(move || bench.run());
21+
});
22+
}
23+
24+
fn bench_simple_iter(c: &mut Criterion) {
25+
let mut group = c.benchmark_group("simple_iter");
26+
group.bench_function("legion", |b| {
27+
let mut bench = legion::simple_iter::Benchmark::new();
28+
b.iter(move || bench.run());
29+
});
30+
group.bench_function("legion_0.2.4", |b| {
31+
let mut bench = legion_2_4::simple_iter::Benchmark::new();
32+
b.iter(move || bench.run());
33+
});
34+
group.bench_function("bevy", |b| {
35+
let mut bench = bevy::simple_iter::Benchmark::new();
36+
b.iter(move || bench.run());
37+
});
38+
group.bench_function("shipyard", |b| {
39+
let mut bench = shipyard::simple_iter::Benchmark::new();
40+
b.iter(move || bench.run());
41+
});
42+
}
43+
44+
fn bench_frag_iter_bc(c: &mut Criterion) {
45+
let mut group = c.benchmark_group("fragmented_iter");
46+
group.bench_function("legion", |b| {
47+
let mut bench = legion::frag_iter::Benchmark::new();
48+
b.iter(move || bench.run());
49+
});
50+
group.bench_function("legion_0.2.4", |b| {
51+
let mut bench = legion_2_4::frag_iter::Benchmark::new();
52+
b.iter(move || bench.run());
53+
});
54+
group.bench_function("bevy", |b| {
55+
let mut bench = bevy::frag_iter::Benchmark::new();
56+
b.iter(move || bench.run());
57+
});
58+
group.bench_function("shipyard", |b| {
59+
let mut bench = shipyard::frag_iter::Benchmark::new();
60+
b.iter(move || bench.run());
61+
});
62+
}
63+
64+
fn bench_schedule(c: &mut Criterion) {
65+
let mut group = c.benchmark_group("schedule");
66+
group.bench_function("legion", |b| {
67+
let mut bench = legion::schedule::Benchmark::new();
68+
b.iter(move || bench.run());
69+
});
70+
group.bench_function("legion_0.2.4", |b| {
71+
let mut bench = legion_2_4::schedule::Benchmark::new();
72+
b.iter(move || bench.run());
73+
});
74+
group.bench_function("bevy", |b| {
75+
let mut bench = bevy::schedule::Benchmark::new();
76+
b.iter(move || bench.run());
77+
});
78+
group.bench_function("shipyard", |b| {
79+
let mut bench = shipyard::schedule::Benchmark::new();
80+
b.iter(move || bench.run());
81+
});
82+
}
83+
84+
fn bench_heavy_compute(c: &mut Criterion) {
85+
let mut group = c.benchmark_group("heavy_compute");
86+
group.bench_function("legion", |b| {
87+
let mut bench = legion::heavy_compute::Benchmark::new();
88+
b.iter(move || bench.run());
89+
});
90+
group.bench_function("legion_0.2.4", |b| {
91+
let mut bench = legion_2_4::heavy_compute::Benchmark::new();
92+
b.iter(move || bench.run());
93+
});
94+
group.bench_function("bevy", |b| {
95+
let mut bench = bevy::heavy_compute::Benchmark::new();
96+
b.iter(move || bench.run());
97+
});
98+
group.bench_function("shipyard", |b| {
99+
let mut bench = shipyard::heavy_compute::Benchmark::new();
100+
b.iter(move || bench.run());
101+
});
102+
}
103+
104+
fn bench_add_remove(c: &mut Criterion) {
105+
let mut group = c.benchmark_group("add_remove_component");
106+
group.bench_function("legion", |b| {
107+
let mut bench = legion::add_remove::Benchmark::new();
108+
b.iter(move || bench.run());
109+
});
110+
group.bench_function("legion_0.2.4", |b| {
111+
let mut bench = legion_2_4::add_remove::Benchmark::new();
112+
b.iter(move || bench.run());
113+
});
114+
group.bench_function("shipyard", |b| {
115+
let mut bench = shipyard::add_remove::Benchmark::new();
116+
b.iter(move || bench.run());
117+
});
118+
119+
// todo Bevy appears to crash in this benchmark
120+
// group.bench_function("bevy", |b| {
121+
// let mut bench = bevy::add_remove::Benchmark::new();
122+
// b.iter(move || bench.run());
123+
// });
124+
}
125+
126+
fn bench_serialize_text(c: &mut Criterion) {
127+
let mut group = c.benchmark_group("serialize_text");
128+
group.bench_function("legion", |b| {
129+
let mut bench = legion::serialize_text::Benchmark::new();
130+
b.iter(move || bench.run());
131+
});
132+
// group.bench_function("bevy", |b| {
133+
// let mut bench = bevy::serialize_text::Benchmark::new();
134+
// b.iter(move || bench.run());
135+
// });
136+
}
137+
138+
fn bench_serialize_binary(c: &mut Criterion) {
139+
let mut group = c.benchmark_group("serialize_binary");
140+
group.bench_function("legion", |b| {
141+
let mut bench = legion::serialize_binary::Benchmark::new();
142+
b.iter(move || bench.run());
143+
});
144+
// group.bench_function("bevy", |b| {
145+
// let mut bench = bevy::serialize_text::Benchmark::new();
146+
// b.iter(move || bench.run());
147+
// });
148+
}
149+
150+
criterion_group!(
151+
benchmarks,
152+
bench_simple_insert,
153+
bench_simple_iter,
154+
bench_frag_iter_bc,
155+
bench_schedule,
156+
bench_heavy_compute,
157+
bench_add_remove,
158+
bench_serialize_text,
159+
bench_serialize_binary,
160+
);
161+
criterion_main!(benchmarks);

readme.md

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# ECS Bench Suite
2+
3+
A suite of benchmarks designed to test and compare Rust ECS library performance across a variety of challenging circumstances.
4+
5+
## The Benchmarks
6+
7+
### Simple Insert
8+
9+
This benchmark is designed to test the base cost of constructing entities and moving components into the ECS.
10+
11+
Inserts 10,000 entities, each with 4 components: `Transform(mat4x4)`, `Position(vec3)`, `Rotation(vec3)` and `Velocity(vec3)`.
12+
13+
### Simple Iter
14+
15+
This benchmark is designed to test the core overheads involved in component iteration in best-case conditions. The iteration should occur on a single CPU core.
16+
17+
Dataset: 10,000 entities, each with 4 components: `Transform(mat4x4)`, `Position(vec3)`, `Rotation(vec3)` and `Velocity(vec3)`.
18+
19+
Test: Iterate through all entities with `Position` and `Velocity`, and add velocity onto position.
20+
21+
### Fragmented Iter
22+
23+
This benchmark is designed to test how the ECS handles iteration through a fragmented dataset. The iteration should occur on a single CPU core.
24+
25+
Dataset: 26 component types (`A(f32)` through `Z(f32)`), each with 20 entities plus a `Data(f32)` component.
26+
27+
Test: Iterate through all entities with a `Data` component and double its value.
28+
29+
### System Scheduling
30+
31+
This benchmark is designed to test how efficiently the ECS can schedule multiple independent systems on a multi-core CPU. Each system should execute on a single CPU core.
32+
33+
Dataset:
34+
35+
* 10,000 entities with `(A, B)` components.
36+
* 10,000 entities with `(A, B, C)` components.
37+
* 10,000 entities with `(A, B, C, D)` components.
38+
* 10,000 entities with `(A, B, C, E)` components.
39+
40+
Test:
41+
42+
Three systems accessing the following components mutably, where each system swaps the values stored in each component:
43+
44+
* `(A, B)`
45+
* `(C, D)`
46+
* `(C, E)`
47+
48+
### Heavy Compute
49+
50+
This benchmark is designed to test the ECS's ability to scale when it is allowed to run a system over multiple CPU cores.
51+
52+
Dataset: 10,000 entities with a `mat4x4` component.
53+
54+
Test: Iterate through all `mat4x4` components, and invert the matrix 10 times.
55+
56+
### Add/Remove Component
57+
58+
This benchmark is designed to test how quickly the ECS can add and then remove a component from an existing entity.
59+
60+
Dataset: 1,000 entities with a single `A` component.
61+
62+
Test: Iterate through all entities, adding a `B` component. Then iterate through all entities again, removing their `B` component.
63+
64+
### Serialize
65+
66+
This benchmark is designed to test how quickly the ECS and serialize and deserialize its entities in both text (RON) and binary (bincode) formats.
67+
68+
Dataset: 1000 entities with `Transform(mat4x4)`, `Position(vec3)`, `Rotation(vec3)` and `Velocity(vec3)` components.
69+
70+
Test: Serialize all entities to RON and bincode formats in-memory. Then deserialize back into the ECS. The RON and bincode formats should be separate benchmark tests.

src/bevy/add_remove.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use bevy_ecs::prelude::*;
2+
3+
struct A(f32);
4+
struct B(f32);
5+
6+
pub struct Benchmark(World, Vec<Entity>);
7+
8+
impl Benchmark {
9+
pub fn new() -> Self {
10+
let mut world = World::default();
11+
12+
let entities = world
13+
.spawn_batch((0..10000).map(|_| (A(0.0),)))
14+
.collect::<Vec<_>>();
15+
16+
Self(world, entities)
17+
}
18+
19+
pub fn run(&mut self) {
20+
for entity in &self.1 {
21+
self.0.insert_one(*entity, B(0.0)).unwrap();
22+
}
23+
24+
for entity in &self.1 {
25+
self.0.remove_one::<B>(*entity).unwrap();
26+
}
27+
}
28+
}

src/bevy/frag_iter.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use bevy_ecs::prelude::*;
2+
3+
macro_rules! create_entities {
4+
($world:ident; $( $variants:ident ),*) => {
5+
$(
6+
struct $variants(f32);
7+
$world.spawn_batch((0..20).map(|_| ($variants(0.0), Data(1.0))));
8+
)*
9+
};
10+
}
11+
12+
struct Data(f32);
13+
14+
pub struct Benchmark(World);
15+
16+
impl Benchmark {
17+
pub fn new() -> Self {
18+
let mut world = World::default();
19+
20+
create_entities!(world; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z);
21+
22+
Self(world)
23+
}
24+
25+
pub fn run(&mut self) {
26+
for mut data in self.0.query::<&mut Data>().iter() {
27+
data.0 *= 2.0;
28+
}
29+
}
30+
}

src/bevy/heavy_compute.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use bevy_ecs::prelude::*;
2+
use cgmath::*;
3+
4+
#[derive(Copy, Clone)]
5+
struct Position(Vector3<f32>);
6+
7+
#[derive(Copy, Clone)]
8+
struct Rotation(Vector3<f32>);
9+
10+
#[derive(Copy, Clone)]
11+
struct Velocity(Vector3<f32>);
12+
13+
pub struct Benchmark(World);
14+
15+
impl Benchmark {
16+
pub fn new() -> Self {
17+
let mut world = World::default();
18+
19+
world.spawn_batch((0..1000).map(|_| {
20+
(
21+
Matrix4::<f32>::from_angle_x(Rad(1.2)),
22+
Position(Vector3::unit_x()),
23+
Rotation(Vector3::unit_x()),
24+
Velocity(Vector3::unit_x()),
25+
)
26+
}));
27+
28+
Self(world)
29+
}
30+
31+
pub fn run(&mut self) {
32+
for (mut pos, mut mat) in self.0.query::<(&mut Position, &mut Matrix4<f32>)>().iter() {
33+
for _ in 0..100 {
34+
*mat = mat.invert().unwrap();
35+
}
36+
37+
pos.0 = mat.transform_vector(pos.0);
38+
}
39+
}
40+
}

src/bevy/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub mod add_remove;
2+
pub mod frag_iter;
3+
pub mod heavy_compute;
4+
pub mod schedule;
5+
pub mod simple_insert;
6+
pub mod simple_iter;

0 commit comments

Comments
 (0)