Skip to content

Commit 4e694ae

Browse files
authored
ECS: put strings only used for debug behind a feature (#19558)
# Objective - Many strings in bevy_ecs are created but only used for debug: system name, component name, ... - Those strings make a significant part of the final binary and are no use in a released game ## Solution - Use [`strings`](https://linux.die.net/man/1/strings) to find ... strings in a binary - Try to find where they come from - Many are made from `type_name::<T>()` and only used in error / debug messages - Add a new structure `DebugName` that holds no value if `debug` feature is disabled - Replace `core::any::type_name::<T>()` by `DebugName::type_name::<T>()` ## Testing Measurements were taken without the new feature being enabled by default, to help with commands ### File Size I tried building the `breakout` example with `cargo run --release --example breakout` |`debug` enabled|`debug` disabled| |-|-| |81621776 B|77735728B| |77.84MB|74.13MB| ### Compilation time `hyperfine --min-runs 15 --prepare "cargo clean && sleep 5" 'RUSTC_WRAPPER="" cargo build --release --example breakout' 'RUSTC_WRAPPER="" cargo build --release --example breakout --features debug'` ``` breakout' 'RUSTC_WRAPPER="" cargo build --release --example breakout --features debug' Benchmark 1: RUSTC_WRAPPER="" cargo build --release --example breakout Time (mean ± σ): 84.856 s ± 3.565 s [User: 1093.817 s, System: 32.547 s] Range (min … max): 78.038 s … 89.214 s 15 runs Benchmark 2: RUSTC_WRAPPER="" cargo build --release --example breakout --features debug Time (mean ± σ): 92.303 s ± 2.466 s [User: 1193.443 s, System: 33.803 s] Range (min … max): 90.619 s … 99.684 s 15 runs Summary RUSTC_WRAPPER="" cargo build --release --example breakout ran 1.09 ± 0.05 times faster than RUSTC_WRAPPER="" cargo build --release --example breakout --features debug ```
1 parent 2119838 commit 4e694ae

Some content is hidden

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

52 files changed

+363
-231
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ jobs:
9595
- name: CI job
9696
# To run the tests one item at a time for troubleshooting, use
9797
# cargo --quiet test --lib -- --list | sed 's/: test$//' | MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation" xargs -n1 cargo miri test -p bevy_ecs --lib -- --exact
98-
run: cargo miri test -p bevy_ecs
98+
run: cargo miri test -p bevy_ecs --features bevy_utils/debug
9999
env:
100100
# -Zrandomize-layout makes sure we dont rely on the layout of anything that might change
101101
RUSTFLAGS: -Zrandomize-layout

Cargo.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ default = [
165165
"vorbis",
166166
"webgl2",
167167
"x11",
168+
"debug",
168169
]
169170

170171
# Recommended defaults for no_std applications
@@ -507,7 +508,10 @@ file_watcher = ["bevy_internal/file_watcher"]
507508
embedded_watcher = ["bevy_internal/embedded_watcher"]
508509

509510
# Enable stepping-based debugging of Bevy systems
510-
bevy_debug_stepping = ["bevy_internal/bevy_debug_stepping"]
511+
bevy_debug_stepping = [
512+
"bevy_internal/bevy_debug_stepping",
513+
"bevy_internal/debug",
514+
]
511515

512516
# Enables the meshlet renderer for dense high-poly scenes (experimental)
513517
meshlet = ["bevy_internal/meshlet"]
@@ -551,6 +555,9 @@ web = ["bevy_internal/web"]
551555
# Enable hotpatching of Bevy systems
552556
hotpatching = ["bevy_internal/hotpatching"]
553557

558+
# Enable collecting debug information about systems and components to help with diagnostics
559+
debug = ["bevy_internal/debug"]
560+
554561
[dependencies]
555562
bevy_internal = { path = "crates/bevy_internal", version = "0.16.0-dev", default-features = false }
556563
tracing = { version = "0.1", default-features = false, optional = true }
@@ -2098,6 +2105,7 @@ wasm = false
20982105
name = "dynamic"
20992106
path = "examples/ecs/dynamic.rs"
21002107
doc-scrape-examples = true
2108+
required-features = ["debug"]
21012109

21022110
[package.metadata.example.dynamic]
21032111
name = "Dynamic ECS"

crates/bevy_ecs/Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ backtrace = ["std"]
3535

3636
## Enables `tracing` integration, allowing spans and other metrics to be reported
3737
## through that framework.
38-
trace = ["std", "dep:tracing"]
38+
trace = ["std", "dep:tracing", "bevy_utils/debug"]
3939

4040
## Enables a more detailed set of traces which may be noisy if left on by default.
4141
detailed_trace = ["trace"]
@@ -63,9 +63,9 @@ std = [
6363
"bevy_reflect?/std",
6464
"bevy_tasks/std",
6565
"bevy_utils/parallel",
66+
"bevy_utils/std",
6667
"bitflags/std",
6768
"concurrent-queue/std",
68-
"disqualified/alloc",
6969
"fixedbitset/std",
7070
"indexmap/std",
7171
"serde?/std",
@@ -98,7 +98,6 @@ bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-fea
9898
] }
9999

100100
bitflags = { version = "2.3", default-features = false }
101-
disqualified = { version = "1.0", default-features = false }
102101
fixedbitset = { version = "0.5", default-features = false }
103102
serde = { version = "1", default-features = false, features = [
104103
"alloc",

crates/bevy_ecs/src/bundle.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -550,10 +550,9 @@ impl BundleInfo {
550550
// SAFETY: the caller ensures component_id is valid.
551551
unsafe { components.get_info_unchecked(id).name() }
552552
})
553-
.collect::<Vec<_>>()
554-
.join(", ");
553+
.collect::<Vec<_>>();
555554

556-
panic!("Bundle {bundle_type_name} has duplicate components: {names}");
555+
panic!("Bundle {bundle_type_name} has duplicate components: {names:?}");
557556
}
558557

559558
// handle explicit components

crates/bevy_ecs/src/component.rs

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use bevy_platform::{
2424
use bevy_ptr::{OwningPtr, UnsafeCellDeref};
2525
#[cfg(feature = "bevy_reflect")]
2626
use bevy_reflect::Reflect;
27-
use bevy_utils::TypeIdMap;
27+
use bevy_utils::{prelude::DebugName, TypeIdMap};
2828
use core::{
2929
alloc::Layout,
3030
any::{Any, TypeId},
@@ -34,7 +34,6 @@ use core::{
3434
mem::needs_drop,
3535
ops::{Deref, DerefMut},
3636
};
37-
use disqualified::ShortName;
3837
use smallvec::SmallVec;
3938
use thiserror::Error;
4039

@@ -678,8 +677,8 @@ impl ComponentInfo {
678677

679678
/// Returns the name of the current component.
680679
#[inline]
681-
pub fn name(&self) -> &str {
682-
&self.descriptor.name
680+
pub fn name(&self) -> DebugName {
681+
self.descriptor.name.clone()
683682
}
684683

685684
/// Returns `true` if the current component is mutable.
@@ -836,7 +835,7 @@ impl SparseSetIndex for ComponentId {
836835
/// A value describing a component or resource, which may or may not correspond to a Rust type.
837836
#[derive(Clone)]
838837
pub struct ComponentDescriptor {
839-
name: Cow<'static, str>,
838+
name: DebugName,
840839
// SAFETY: This must remain private. It must match the statically known StorageType of the
841840
// associated rust component type if one exists.
842841
storage_type: StorageType,
@@ -882,7 +881,7 @@ impl ComponentDescriptor {
882881
/// Create a new `ComponentDescriptor` for the type `T`.
883882
pub fn new<T: Component>() -> Self {
884883
Self {
885-
name: Cow::Borrowed(core::any::type_name::<T>()),
884+
name: DebugName::type_name::<T>(),
886885
storage_type: T::STORAGE_TYPE,
887886
is_send_and_sync: true,
888887
type_id: Some(TypeId::of::<T>()),
@@ -907,7 +906,7 @@ impl ComponentDescriptor {
907906
clone_behavior: ComponentCloneBehavior,
908907
) -> Self {
909908
Self {
910-
name: name.into(),
909+
name: name.into().into(),
911910
storage_type,
912911
is_send_and_sync: true,
913912
type_id: None,
@@ -923,7 +922,7 @@ impl ComponentDescriptor {
923922
/// The [`StorageType`] for resources is always [`StorageType::Table`].
924923
pub fn new_resource<T: Resource>() -> Self {
925924
Self {
926-
name: Cow::Borrowed(core::any::type_name::<T>()),
925+
name: DebugName::type_name::<T>(),
927926
// PERF: `SparseStorage` may actually be a more
928927
// reasonable choice as `storage_type` for resources.
929928
storage_type: StorageType::Table,
@@ -938,7 +937,7 @@ impl ComponentDescriptor {
938937

939938
fn new_non_send<T: Any>(storage_type: StorageType) -> Self {
940939
Self {
941-
name: Cow::Borrowed(core::any::type_name::<T>()),
940+
name: DebugName::type_name::<T>(),
942941
storage_type,
943942
is_send_and_sync: false,
944943
type_id: Some(TypeId::of::<T>()),
@@ -964,8 +963,8 @@ impl ComponentDescriptor {
964963

965964
/// Returns the name of the current component.
966965
#[inline]
967-
pub fn name(&self) -> &str {
968-
self.name.as_ref()
966+
pub fn name(&self) -> DebugName {
967+
self.name.clone()
969968
}
970969

971970
/// Returns whether this component is mutable.
@@ -1854,13 +1853,10 @@ impl Components {
18541853
///
18551854
/// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value.
18561855
#[inline]
1857-
pub fn get_name<'a>(&'a self, id: ComponentId) -> Option<Cow<'a, str>> {
1856+
pub fn get_name<'a>(&'a self, id: ComponentId) -> Option<DebugName> {
18581857
self.components
18591858
.get(id.0)
1860-
.and_then(|info| {
1861-
info.as_ref()
1862-
.map(|info| Cow::Borrowed(info.descriptor.name()))
1863-
})
1859+
.and_then(|info| info.as_ref().map(|info| info.descriptor.name()))
18641860
.or_else(|| {
18651861
let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner);
18661862
// first check components, then resources, then dynamic
@@ -2813,13 +2809,13 @@ pub fn enforce_no_required_components_recursion(
28132809
"Recursive required components detected: {}\nhelp: {}",
28142810
recursion_check_stack
28152811
.iter()
2816-
.map(|id| format!("{}", ShortName(&components.get_name(*id).unwrap())))
2812+
.map(|id| format!("{}", components.get_name(*id).unwrap().shortname()))
28172813
.collect::<Vec<_>>()
28182814
.join(" → "),
28192815
if direct_recursion {
28202816
format!(
28212817
"Remove require({}).",
2822-
ShortName(&components.get_name(requiree).unwrap())
2818+
components.get_name(requiree).unwrap().shortname()
28232819
)
28242820
} else {
28252821
"If this is intentional, consider merging the components.".into()

crates/bevy_ecs/src/entity/clone_entities.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec};
22
use bevy_platform::collections::{HashMap, HashSet};
33
use bevy_ptr::{Ptr, PtrMut};
4+
use bevy_utils::prelude::DebugName;
45
use bumpalo::Bump;
56
use core::any::TypeId;
67

@@ -171,7 +172,8 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
171172
/// - `ComponentId` of component being written does not match expected `ComponentId`.
172173
pub fn write_target_component<C: Component>(&mut self, mut component: C) {
173174
C::map_entities(&mut component, &mut self.mapper);
174-
let short_name = disqualified::ShortName::of::<C>();
175+
let debug_name = DebugName::type_name::<C>();
176+
let short_name = debug_name.shortname();
175177
if self.target_component_written {
176178
panic!("Trying to write component '{short_name}' multiple times")
177179
}

crates/bevy_ecs/src/error/command_handling.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use core::{any::type_name, fmt};
1+
use core::fmt;
2+
3+
use bevy_utils::prelude::DebugName;
24

35
use crate::{
46
entity::Entity,
@@ -31,7 +33,7 @@ where
3133
Err(err) => (error_handler)(
3234
err.into(),
3335
ErrorContext::Command {
34-
name: type_name::<C>().into(),
36+
name: DebugName::type_name::<C>(),
3537
},
3638
),
3739
}
@@ -43,7 +45,7 @@ where
4345
Err(err) => world.default_error_handler()(
4446
err.into(),
4547
ErrorContext::Command {
46-
name: type_name::<C>().into(),
48+
name: DebugName::type_name::<C>(),
4749
},
4850
),
4951
}

crates/bevy_ecs/src/error/handler.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::fmt::Display;
22

33
use crate::{component::Tick, error::BevyError, prelude::Resource};
4-
use alloc::borrow::Cow;
4+
use bevy_utils::prelude::DebugName;
55
use derive_more::derive::{Deref, DerefMut};
66

77
/// Context for a [`BevyError`] to aid in debugging.
@@ -10,26 +10,26 @@ pub enum ErrorContext {
1010
/// The error occurred in a system.
1111
System {
1212
/// The name of the system that failed.
13-
name: Cow<'static, str>,
13+
name: DebugName,
1414
/// The last tick that the system was run.
1515
last_run: Tick,
1616
},
1717
/// The error occurred in a run condition.
1818
RunCondition {
1919
/// The name of the run condition that failed.
20-
name: Cow<'static, str>,
20+
name: DebugName,
2121
/// The last tick that the run condition was evaluated.
2222
last_run: Tick,
2323
},
2424
/// The error occurred in a command.
2525
Command {
2626
/// The name of the command that failed.
27-
name: Cow<'static, str>,
27+
name: DebugName,
2828
},
2929
/// The error occurred in an observer.
3030
Observer {
3131
/// The name of the observer that failed.
32-
name: Cow<'static, str>,
32+
name: DebugName,
3333
/// The last tick that the observer was run.
3434
last_run: Tick,
3535
},
@@ -54,12 +54,12 @@ impl Display for ErrorContext {
5454

5555
impl ErrorContext {
5656
/// The name of the ECS construct that failed.
57-
pub fn name(&self) -> &str {
57+
pub fn name(&self) -> DebugName {
5858
match self {
5959
Self::System { name, .. }
6060
| Self::Command { name, .. }
6161
| Self::Observer { name, .. }
62-
| Self::RunCondition { name, .. } => name,
62+
| Self::RunCondition { name, .. } => name.clone(),
6363
}
6464
}
6565

crates/bevy_ecs/src/hierarchy.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ use alloc::{format, string::String, vec::Vec};
2222
use bevy_reflect::std_traits::ReflectDefault;
2323
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
2424
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
25+
use bevy_utils::prelude::DebugName;
2526
use core::ops::Deref;
2627
use core::slice;
27-
use disqualified::ShortName;
2828
use log::warn;
2929

3030
/// Stores the parent entity of this child entity with this component.
@@ -461,11 +461,12 @@ pub fn validate_parent_has_component<C: Component>(
461461
{
462462
// TODO: print name here once Name lives in bevy_ecs
463463
let name: Option<String> = None;
464+
let debug_name = DebugName::type_name::<C>();
464465
warn!(
465466
"warning[B0004]: {}{name} with the {ty_name} component has a parent without {ty_name}.\n\
466467
This will cause inconsistent behaviors! See: https://bevy.org/learn/errors/b0004",
467468
caller.map(|c| format!("{c}: ")).unwrap_or_default(),
468-
ty_name = ShortName::of::<C>(),
469+
ty_name = debug_name.shortname(),
469470
name = name.map_or_else(
470471
|| format!("Entity {entity}"),
471472
|s| format!("The {s} entity")

crates/bevy_ecs/src/observer/runner.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use alloc::{borrow::Cow, boxed::Box, vec};
1+
use alloc::{boxed::Box, vec};
2+
use bevy_utils::prelude::DebugName;
23
use core::any::Any;
34

45
use crate::{
@@ -301,7 +302,7 @@ impl Observer {
301302
}
302303

303304
/// Returns the name of the [`Observer`]'s system .
304-
pub fn system_name(&self) -> Cow<'static, str> {
305+
pub fn system_name(&self) -> DebugName {
305306
self.system.system_name()
306307
}
307308
}
@@ -420,11 +421,11 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
420421
}
421422

422423
trait AnyNamedSystem: Any + Send + Sync + 'static {
423-
fn system_name(&self) -> Cow<'static, str>;
424+
fn system_name(&self) -> DebugName;
424425
}
425426

426427
impl<T: Any + System> AnyNamedSystem for T {
427-
fn system_name(&self) -> Cow<'static, str> {
428+
fn system_name(&self) -> DebugName {
428429
self.name()
429430
}
430431
}

crates/bevy_ecs/src/query/access.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use crate::world::World;
44
use alloc::{format, string::String, vec, vec::Vec};
55
use core::{fmt, fmt::Debug, marker::PhantomData};
66
use derive_more::From;
7-
use disqualified::ShortName;
87
use fixedbitset::FixedBitSet;
98
use thiserror::Error;
109

@@ -999,12 +998,11 @@ impl AccessConflicts {
999998
.map(|index| {
1000999
format!(
10011000
"{}",
1002-
ShortName(
1003-
&world
1004-
.components
1005-
.get_name(ComponentId::get_sparse_set_index(index))
1006-
.unwrap()
1007-
)
1001+
world
1002+
.components
1003+
.get_name(ComponentId::get_sparse_set_index(index))
1004+
.unwrap()
1005+
.shortname()
10081006
)
10091007
})
10101008
.collect::<Vec<_>>()

0 commit comments

Comments
 (0)