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

Commit dd9e00b

Browse files
authored
Add planck_ecs, replace table with violin graphs
1 parent 28e4e1a commit dd9e00b

File tree

1,355 files changed

+155603
-156501
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,355 files changed

+155603
-156501
lines changed

Cargo.toml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ edition = "2018"
77
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
88

99
[dependencies]
10-
cgmath = { version = "0.17", feature = ["serde"] }
10+
bevy_ecs = "0.3"
1111
bincode = "1.3"
12+
cgmath = { version = "0.17", feature = ["serde"] }
13+
hecs = { version = "0.5", features = ["column-serialize", "row-serialize"] }
14+
legion = "0.3"
15+
planck_ecs = { version = "1.1.0", features = ["parallel"] }
16+
rayon = "1.3"
1217
ron = "0.6"
1318
serde = { version = "1.0", features = ["derive"] }
14-
rayon = "1.3"
15-
legion = "0.3"
16-
bevy_ecs = "0.3"
17-
hecs = { version = "0.5", features = ["column-serialize", "row-serialize"] }
1819
shipyard = "0.4"
1920
specs = {version = "0.16.1", features = ["serde"] }
2021
specs-derive = "0.4.1"
@@ -25,4 +26,4 @@ rayon = "1.3"
2526

2627
[[bench]]
2728
name = "benchmarks"
28-
harness = false
29+
harness = false

benches/benchmarks.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ fn bench_simple_insert(c: &mut Criterion) {
1515
let mut bench = hecs::simple_insert::Benchmark::new();
1616
b.iter(move || bench.run());
1717
});
18+
group.bench_function("planck_ecs", |b| {
19+
let mut bench = planck_ecs::simple_insert::Benchmark::new();
20+
b.iter(move || bench.run());
21+
});
1822
group.bench_function("shipyard", |b| {
1923
let mut bench = shipyard::simple_insert::Benchmark::new();
2024
b.iter(move || bench.run());
@@ -43,6 +47,10 @@ fn bench_simple_iter(c: &mut Criterion) {
4347
let mut bench = hecs::simple_iter::Benchmark::new();
4448
b.iter(move || bench.run());
4549
});
50+
group.bench_function("planck_ecs", |b| {
51+
let mut bench = planck_ecs::simple_iter::Benchmark::new();
52+
b.iter(move || bench.run());
53+
});
4654
group.bench_function("shipyard", |b| {
4755
let mut bench = shipyard::simple_iter::Benchmark::new();
4856
b.iter(move || bench.run());
@@ -71,6 +79,10 @@ fn bench_frag_iter_bc(c: &mut Criterion) {
7179
let mut bench = hecs::frag_iter::Benchmark::new();
7280
b.iter(move || bench.run());
7381
});
82+
group.bench_function("planck_ecs", |b| {
83+
let mut bench = planck_ecs::frag_iter::Benchmark::new();
84+
b.iter(move || bench.run());
85+
});
7486
group.bench_function("shipyard", |b| {
7587
let mut bench = shipyard::frag_iter::Benchmark::new();
7688
b.iter(move || bench.run());
@@ -95,6 +107,10 @@ fn bench_schedule(c: &mut Criterion) {
95107
let mut bench = bevy::schedule::Benchmark::new();
96108
b.iter(move || bench.run());
97109
});
110+
group.bench_function("planck_ecs", |b| {
111+
let mut bench = planck_ecs::schedule::Benchmark::new();
112+
b.iter(move || bench.run());
113+
});
98114
group.bench_function("shipyard", |b| {
99115
let mut bench = shipyard::schedule::Benchmark::new();
100116
b.iter(move || bench.run());
@@ -151,6 +167,10 @@ fn bench_add_remove(c: &mut Criterion) {
151167
let mut bench = hecs::add_remove::Benchmark::new();
152168
b.iter(move || bench.run());
153169
});
170+
group.bench_function("planck_ecs", |b| {
171+
let mut bench = planck_ecs::add_remove::Benchmark::new();
172+
b.iter(move || bench.run());
173+
});
154174
group.bench_function("shipyard", |b| {
155175
let mut bench = shipyard::add_remove::Benchmark::new();
156176
b.iter(move || bench.run());

readme.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,6 @@ A suite of benchmarks designed to test and compare Rust ECS library performance
44

55
The full benchmark report is available [here](https://rust-gamedev.github.io/ecs_bench_suite/target/criterion/report/index.html).
66

7-
| | legion (\*) | bevy | hecs | shipyard (\*) | specs |
8-
|------------------|:----------------------|:----------:|:----------:|:---------------------:|:-----------:|
9-
| simple_insert | **383μs** | 636μs | 640μs | 2.08ms | 1.90ms |
10-
| simple_iter | 13.2μs (**11.2μs**) | 12.9μs | **12.0μs** | 86.3μs (24.2μs) | 28.8ms |
11-
| frag_iter | 441ns | 554ns | 452ns | **121ns** | 1.41μs |
12-
| heavy_compute | **686μs** (687μs) | 958μs | 972μs | **693μs** (693μs) | 968μs |
13-
| schedule | **54.3μs** (53.7μs) | 80.3μs | - | 372μs (132μs) | 155μs |
14-
| add_remove | 4.45ms | 6.71ms | 7.86ms | 237μs | **123μs** |
15-
| serialize_text | **12.5ms** | - | - | - | - |
16-
| serialize_binary | **6.50ms** | - | - | - | - |
17-
18-
(*): The values in parentheses are results where per-benchmark storage optimizations were applied. Some of these are mutually exclusive, so with and without "packing" typically represent best and worst-case performance for the ECS.
19-
20-
The best result for each benchmark is marked in bold text. Note that run to run variance for these benchmarks is typically 2-3%, with outliers as much as 10%. All micro-benchmarks should be taken with a grain of salt, and any benchmarks within a few percent of each other should be considered "effectively equal".
21-
227
## The Benchmarks
238

249
### Simple Insert
@@ -27,6 +12,8 @@ This benchmark is designed to test the base cost of constructing entities and mo
2712

2813
Inserts 10,000 entities, each with 4 components: `Transform(mat4x4)`, `Position(vec3)`, `Rotation(vec3)` and `Velocity(vec3)`.
2914

15+
![](./target/criterion/simple_insert/report/violin.svg)
16+
3017
### Simple Iter
3118

3219
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.
@@ -35,6 +22,8 @@ Dataset: 10,000 entities, each with 4 components: `Transform(mat4x4)`, `Position
3522

3623
Test: Iterate through all entities with `Position` and `Velocity`, and add velocity onto position.
3724

25+
![](./target/criterion/simple_iter/report/violin.svg)
26+
3827
### Fragmented Iter
3928

4029
This benchmark is designed to test how the ECS handles iteration through a fragmented dataset. The iteration should occur on a single CPU core.
@@ -43,6 +32,8 @@ Dataset: 26 component types (`A(f32)` through `Z(f32)`), each with 20 entities p
4332

4433
Test: Iterate through all entities with a `Data` component and double its value.
4534

35+
![](./target/criterion/fragmented_iter/report/violin.svg)
36+
4637
### System Scheduling
4738

4839
This benchmark is designed to test how efficiently the ECS can schedule multiple independent systems on a multi-core CPU. This is primarily an outer-parallelism test. Each system should execute on a single CPU core.
@@ -62,6 +53,8 @@ Three systems accessing the following components mutably, where each system swap
6253
* `(C, D)`
6354
* `(C, E)`
6455

56+
![](./target/criterion/schedule/report/violin.svg)
57+
6558
### Heavy Compute
6659

6760
This benchmark is designed to test the ECS's ability to scale when it is allowed to run a system over multiple CPU cores. This is primarily an inner-parallelism test.
@@ -70,6 +63,8 @@ Dataset: 10,000 entities with a `mat4x4` component.
7063

7164
Test: Iterate through all `mat4x4` components, and invert the matrix 10 times.
7265

66+
![](./target/criterion/heavy_compute/report/violin.svg)
67+
7368
### Add/Remove Component
7469

7570
This benchmark is designed to test how quickly the ECS can add and then remove a component from an existing entity.
@@ -78,10 +73,15 @@ Dataset: 1,000 entities with a single `A` component.
7873

7974
Test: Iterate through all entities, adding a `B` component. Then iterate through all entities again, removing their `B` component.
8075

76+
![](./target/criterion/add_remove_component/report/violin.svg)
77+
8178
### Serialize
8279

8380
This benchmark is designed to test how quickly the ECS and serialize and deserialize its entities in both text (RON) and binary (bincode) formats.
8481

8582
Dataset: 1000 entities with `Transform(mat4x4)`, `Position(vec3)`, `Rotation(vec3)` and `Velocity(vec3)` components.
8683

8784
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.
85+
86+
![](./target/criterion/serialize_text/report/violin.svg)
87+
![](./target/criterion/serialize_binary/report/violin.svg)

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod bevy;
44
pub mod hecs;
55
pub mod legion;
66
pub mod legion_packed;
7+
pub mod planck_ecs;
78
pub mod shipyard;
89
pub mod shipyard_packed;
910
pub mod specs;

src/planck_ecs/add_remove.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use planck_ecs::*;
2+
struct A(f32);
3+
struct B(f32);
4+
5+
pub struct Benchmark(Vec<Entity>, Components<A>, Components<B>);
6+
7+
impl Benchmark {
8+
pub fn new() -> Self {
9+
let mut entities = Entities::default();
10+
let mut comp1 = Components::<A>::default();
11+
let comp2 = Components::<B>::default();
12+
13+
let entities = (0..10000)
14+
.map(|_| {
15+
let e = entities.create();
16+
comp1.insert(e, A(0.0));
17+
e
18+
})
19+
.collect();
20+
Self(entities, comp1, comp2)
21+
}
22+
23+
pub fn run(&mut self) {
24+
let b_storage = &mut self.2;
25+
for entity in &self.0 {
26+
b_storage.insert(*entity, B(0.0));
27+
}
28+
29+
for entity in &self.0 {
30+
b_storage.remove(*entity);
31+
}
32+
}
33+
}

src/planck_ecs/frag_iter.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use planck_ecs::*;
2+
3+
macro_rules! create_entities {
4+
($world:ident; $( $variants:ident ),*) => {
5+
$(
6+
struct $variants(f32);
7+
$world.initialize::<Components<$variants>>();
8+
(0..20)
9+
.for_each(|_| {
10+
let e = $world.get_mut::<Entities>().unwrap().create();
11+
$world.get_mut::<Components<_>>().unwrap().insert(e, $variants(0.0));
12+
$world.get_mut::<Components<_>>().unwrap().insert(e, Data(1.0));
13+
});
14+
)*
15+
};
16+
}
17+
struct Data(f32);
18+
19+
fn frag_iter_system(data_storage: &mut Components<Data>) -> SystemResult {
20+
for data in join!(&mut data_storage) {
21+
data.0 *= 2.0;
22+
}
23+
Ok(())
24+
}
25+
26+
pub struct Benchmark(World, System);
27+
28+
impl Benchmark {
29+
pub fn new() -> Self {
30+
let mut world = World::default();
31+
world.initialize::<Entities>();
32+
world.initialize::<Components<Data>>();
33+
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);
34+
35+
Self(world, frag_iter_system.system())
36+
}
37+
38+
pub fn run(&mut self) {
39+
self.1.run(&self.0).unwrap();
40+
}
41+
}

src/planck_ecs/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pub mod add_remove;
2+
pub mod frag_iter;
3+
// We don't have inner parallelism, only outer.
4+
//pub mod heavy_compute;
5+
pub mod schedule;
6+
pub mod simple_insert;
7+
pub mod simple_iter;

src/planck_ecs/schedule.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use planck_ecs::*;
2+
struct A(f32);
3+
struct B(f32);
4+
struct C(f32);
5+
struct D(f32);
6+
struct E(f32);
7+
8+
fn ab_system(a_store: &mut Components<A>, b_store: &mut Components<B>) -> SystemResult {
9+
for (a, b) in join!(&mut a_store && &mut b_store) {
10+
std::mem::swap(&mut a.unwrap().0, &mut b.unwrap().0);
11+
}
12+
Ok(())
13+
}
14+
15+
fn cd_system(c_store: &mut Components<C>, d_store: &mut Components<D>) -> SystemResult {
16+
for (c, d) in join!(&mut c_store && &mut d_store) {
17+
std::mem::swap(&mut c.unwrap().0, &mut d.unwrap().0);
18+
}
19+
Ok(())
20+
}
21+
22+
fn ce_system(c_store: &mut Components<C>, e_store: &mut Components<E>) -> SystemResult {
23+
for (c, e) in join!(&mut c_store && &mut e_store) {
24+
std::mem::swap(&mut c.unwrap().0, &mut e.unwrap().0);
25+
}
26+
Ok(())
27+
}
28+
29+
pub struct Benchmark(World, Dispatcher);
30+
31+
impl Benchmark {
32+
pub fn new() -> Self {
33+
let mut world = World::default();
34+
world.initialize::<Entities>();
35+
world.initialize::<Components<A>>();
36+
world.initialize::<Components<B>>();
37+
world.initialize::<Components<C>>();
38+
world.initialize::<Components<D>>();
39+
world.initialize::<Components<E>>();
40+
(0..10000).for_each(|_| {
41+
let e = world.get_mut::<Entities>().unwrap().create();
42+
world.get_mut::<Components<_>>().unwrap().insert(e, A(0.0));
43+
});
44+
(0..10000).for_each(|_| {
45+
let e = world.get_mut::<Entities>().unwrap().create();
46+
world.get_mut::<Components<_>>().unwrap().insert(e, A(0.0));
47+
world.get_mut::<Components<_>>().unwrap().insert(e, B(0.0));
48+
});
49+
(0..10000).for_each(|_| {
50+
let e = world.get_mut::<Entities>().unwrap().create();
51+
world.get_mut::<Components<_>>().unwrap().insert(e, A(0.0));
52+
world.get_mut::<Components<_>>().unwrap().insert(e, B(0.0));
53+
world.get_mut::<Components<_>>().unwrap().insert(e, C(0.0));
54+
});
55+
(0..10000).for_each(|_| {
56+
let e = world.get_mut::<Entities>().unwrap().create();
57+
world.get_mut::<Components<_>>().unwrap().insert(e, A(0.0));
58+
world.get_mut::<Components<_>>().unwrap().insert(e, B(0.0));
59+
world.get_mut::<Components<_>>().unwrap().insert(e, C(0.0));
60+
world.get_mut::<Components<_>>().unwrap().insert(e, D(0.0));
61+
});
62+
(0..10000).for_each(|_| {
63+
let e = world.get_mut::<Entities>().unwrap().create();
64+
world.get_mut::<Components<_>>().unwrap().insert(e, A(0.0));
65+
world.get_mut::<Components<_>>().unwrap().insert(e, B(0.0));
66+
world.get_mut::<Components<_>>().unwrap().insert(e, C(0.0));
67+
world.get_mut::<Components<_>>().unwrap().insert(e, E(0.0));
68+
});
69+
70+
let dispatcher = DispatcherBuilder::new()
71+
.add(ab_system)
72+
.add(cd_system)
73+
.add(ce_system)
74+
.build(&mut world);
75+
76+
Self(world, dispatcher)
77+
}
78+
79+
pub fn run(&mut self) {
80+
self.1.run_par(&self.0).unwrap();
81+
}
82+
}

src/planck_ecs/simple_insert.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use cgmath::*;
2+
use planck_ecs::*;
3+
4+
#[derive(Copy, Clone)]
5+
struct Transform(Matrix4<f32>);
6+
#[derive(Copy, Clone)]
7+
struct Position(Vector3<f32>);
8+
#[derive(Copy, Clone)]
9+
struct Rotation(Vector3<f32>);
10+
#[derive(Copy, Clone)]
11+
struct Velocity(Vector3<f32>);
12+
13+
pub struct Benchmark;
14+
15+
impl Benchmark {
16+
pub fn new() -> Self {
17+
Self
18+
}
19+
20+
pub fn run(&mut self) {
21+
let mut entities = Entities::default();
22+
let mut comp1 = Components::<Transform>::default();
23+
let mut comp2 = Components::<Position>::default();
24+
let mut comp3 = Components::<Rotation>::default();
25+
let mut comp4 = Components::<Velocity>::default();
26+
27+
let en = (0..10000).map(|_| entities.create()).collect::<Vec<_>>();
28+
en.iter().for_each(|e| {comp1.insert(*e, Transform(Matrix4::<f32>::from_scale(1.0)));});
29+
en.iter().for_each(|e| {comp2.insert(*e, Position(Vector3::unit_x()));});
30+
en.iter().for_each(|e| {comp3.insert(*e, Rotation(Vector3::unit_x()));});
31+
en.iter().for_each(|e| {comp4.insert(*e, Velocity(Vector3::unit_x()));});
32+
33+
/*(0..10000).for_each(|_| {
34+
let e = entities.create();
35+
comp1.insert(e, Transform(Matrix4::<f32>::from_scale(1.0)));
36+
comp2.insert(e, Position(Vector3::unit_x()));
37+
comp3.insert(e, Rotation(Vector3::unit_x()));
38+
comp4.insert(e, Velocity(Vector3::unit_x()));
39+
});*/
40+
}
41+
}

0 commit comments

Comments
 (0)