
# Objective #16132 introduced entity cloning functionality, and while it works and is useful, it can be made faster. This is the promised follow-up to improve performance. ## Solution **PREFACE**: This is my first time writing `unsafe` in rust and I have only vague idea about what I'm doing. I would encourage reviewers to scrutinize `unsafe` parts in particular. The solution is to clone component data to an intermediate buffer and use `EntityWorldMut::insert_by_ids` to insert components without additional archetype moves. To facilitate this, `EntityCloner::clone_entity` now reads all components of the source entity and provides clone handlers with the ability to read component data straight from component storage using `read_source_component` and write to an intermediate buffer using `write_target_component`. `ComponentId` is used to check that requested type corresponds to the type available on source entity. Reflect-based handler is a little trickier to pull of: we only have `&dyn Reflect` and no direct access to the underlying data. `ReflectFromPtr` can be used to get `&dyn Reflect` from concrete component data, but to write it we need to create a clone of the underlying data using `Reflect`. For this reason only components that have `ReflectDefault` or `ReflectFromReflect` or `ReflectFromWorld` can be cloned, all other components will be skipped. The good news is that this is actually only a temporary limitation: once #13432 lands we will be able to clone component without requiring one of these `type data`s. This PR also introduces `entity_cloning` benchmark to better compare changes between the PR and main, you can see the results in the **showcase** section. ## Testing - All previous tests passing - Added test for fast reflect clone path (temporary, will be removed after reflection-based cloning lands) - Ran miri ## Showcase Here's a table demonstrating the improvement: | **benchmark** | **main, avg** | **PR, avg** | **change, avg** | | ----------------------- | ------------- | ----------- | --------------- | | many components reflect | 18.505 µs | 2.1351 µs | -89.095% | | hierarchy wide reflect* | 22.778 ms | 4.1875 ms | -81.616% | | hierarchy tall reflect* | 107.24 µs | 26.322 µs | -77.141% | | hierarchy many reflect | 78.533 ms | 9.7415 ms | -87.596% | | many components clone | 1.3633 µs | 758.17 ns | -45.937% | | hierarchy wide clone* | 2.7716 ms | 3.3411 ms | +20.546% | | hierarchy tall clone* | 17.646 µs | 20.190 µs | +17.379% | | hierarchy many clone | 5.8779 ms | 4.2650 ms | -27.439% | *: these benchmarks have entities with only 1 component ## Considerations Once #10154 is resolved a large part of the functionality in this PR will probably become obsolete. It might still be a little bit faster than using command batching, but the complexity might not be worth it. ## Migration Guide - `&EntityCloner` in component clone handlers is changed to `&mut ComponentCloneCtx` to better separate data. - Changed `EntityCloneHandler` from enum to struct and added convenience functions to add default clone and reflect handler more easily. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
97 lines
2.4 KiB
TOML
97 lines
2.4 KiB
TOML
[package]
|
|
name = "benches"
|
|
edition = "2021"
|
|
description = "Benchmarks that test Bevy's performance"
|
|
publish = false
|
|
license = "MIT OR Apache-2.0"
|
|
# Do not automatically discover benchmarks, we specify them manually instead.
|
|
autobenches = false
|
|
|
|
[dev-dependencies]
|
|
# Bevy crates
|
|
bevy_app = { path = "../crates/bevy_app" }
|
|
bevy_ecs = { path = "../crates/bevy_ecs", features = ["multi_threaded"] }
|
|
bevy_hierarchy = { path = "../crates/bevy_hierarchy" }
|
|
bevy_math = { path = "../crates/bevy_math" }
|
|
bevy_picking = { path = "../crates/bevy_picking", features = [
|
|
"bevy_mesh_picking_backend",
|
|
] }
|
|
bevy_reflect = { path = "../crates/bevy_reflect", features = ["functions"] }
|
|
bevy_render = { path = "../crates/bevy_render" }
|
|
bevy_tasks = { path = "../crates/bevy_tasks" }
|
|
bevy_utils = { path = "../crates/bevy_utils" }
|
|
|
|
# Other crates
|
|
criterion = { version = "0.5.1", features = ["html_reports"] }
|
|
glam = "0.29"
|
|
rand = "0.8"
|
|
rand_chacha = "0.3"
|
|
|
|
# Make `bevy_render` compile on Linux with x11 windowing. x11 vs. Wayland does not matter here
|
|
# because the benches do not actually open any windows.
|
|
[target.'cfg(target_os = "linux")'.dev-dependencies]
|
|
bevy_winit = { path = "../crates/bevy_winit", features = ["x11"] }
|
|
|
|
[profile.release]
|
|
opt-level = 3
|
|
lto = true
|
|
|
|
[lints.clippy]
|
|
doc_markdown = "warn"
|
|
manual_let_else = "warn"
|
|
match_same_arms = "warn"
|
|
redundant_closure_for_method_calls = "warn"
|
|
redundant_else = "warn"
|
|
semicolon_if_nothing_returned = "warn"
|
|
type_complexity = "allow"
|
|
undocumented_unsafe_blocks = "warn"
|
|
unwrap_or_default = "warn"
|
|
needless_lifetimes = "allow"
|
|
|
|
ptr_as_ptr = "warn"
|
|
ptr_cast_constness = "warn"
|
|
ref_as_ptr = "warn"
|
|
|
|
# see: https://github.com/bevyengine/bevy/pull/15375#issuecomment-2366966219
|
|
too_long_first_doc_paragraph = "allow"
|
|
|
|
[lints.rust]
|
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(docsrs_dep)'] }
|
|
unsafe_op_in_unsafe_fn = "warn"
|
|
unused_qualifications = "warn"
|
|
|
|
[[bench]]
|
|
name = "entity_cloning"
|
|
path = "benches/bevy_ecs/entity_cloning.rs"
|
|
harness = false
|
|
|
|
[[bench]]
|
|
name = "ecs"
|
|
path = "benches/bevy_ecs/main.rs"
|
|
harness = false
|
|
|
|
[[bench]]
|
|
name = "math"
|
|
path = "benches/bevy_math/main.rs"
|
|
harness = false
|
|
|
|
[[bench]]
|
|
name = "picking"
|
|
path = "benches/bevy_picking/main.rs"
|
|
harness = false
|
|
|
|
[[bench]]
|
|
name = "reflect"
|
|
path = "benches/bevy_reflect/main.rs"
|
|
harness = false
|
|
|
|
[[bench]]
|
|
name = "render"
|
|
path = "benches/bevy_render/main.rs"
|
|
harness = false
|
|
|
|
[[bench]]
|
|
name = "tasks"
|
|
path = "benches/bevy_tasks/main.rs"
|
|
harness = false
|