Skip to content

Commit 5754300

Browse files
authored
Fix excessive monomorphization of Dir::insert_asset (#24015)
## Objective Reduce code bloat caused by excessive monomorphization. ## Background `bevy_asset::Dir::insert` has the following signature: ```rust fn insert_asset(&self, path: &Path, value: impl Into<Value>) ``` The `value` parameter is usually a `Vec<u8>` or `&'static [u8; N]`, which is conveniently handled by `Into<Value>`. `Dir::insert_asset` is called by the `embedded_asset!` macro via `EmbeddedAssetRegistry::insert_asset`: ```rust embedded.insert_asset(watched_path, &path, include_bytes!($path)); ``` `include_bytes!` returns a `&'static [u8; N]`, which goes to the `value` parameter of `insert_asset`. This means that `insert_asset` gets monomorphized many times, since N is part of the generic type via `impl From<&'static [u8; N]> for Value`. ``` > cargo bloat --example 3d_scene --profile release --filter "insert_asset" -n 10 File .text Size Crate Name 0.0% 0.0% 1.1KiB bevy_anti_alias bevy_asset::io::embedded::EmbeddedAssetRegistry::insert_asset 0.0% 0.0% 1.1KiB bevy_anti_alias bevy_asset::io::embedded::EmbeddedAssetRegistry::insert_asset 0.0% 0.0% 1.1KiB bevy_anti_alias bevy_asset::io::embedded::EmbeddedAssetRegistry::insert_asset 0.0% 0.0% 1.1KiB bevy_anti_alias bevy_asset::io::embedded::EmbeddedAssetRegistry::insert_asset 0.0% 0.0% 1.1KiB bevy_anti_alias bevy_asset::io::embedded::EmbeddedAssetRegistry::insert_asset 0.0% 0.0% 1.1KiB bevy_anti_alias bevy_asset::io::embedded::EmbeddedAssetRegistry::insert_asset 0.0% 0.0% 983B bevy_core_pipeline bevy_asset::io::memory::Dir::insert_asset 0.0% 0.0% 983B bevy_core_pipeline bevy_asset::io::memory::Dir::insert_asset 0.0% 0.0% 983B bevy_core_pipeline bevy_asset::io::memory::Dir::insert_asset 0.0% 0.0% 983B bevy_core_pipeline bevy_asset::io::memory::Dir::insert_asset 0.1% 0.2% 106.3KiB And 111 smaller methods. Use -n N to show more. 0.1% 0.2% 116.5KiB filtered data size, the file size is 79.3MiB ``` ## Solution The fix is to split `insert_asset` into a generic front-end and a non-generic back-end: ```rust pub fn insert_asset(&self, path: &Path, value: impl Into<Value>) { self.insert_asset_internal(path, value.into()); } fn insert_asset_internal(&self, path: &Path, value: Value) { ... } ``` This drops a release build of `3d_scene` from 81,190KB to 81,029KB (-161KB, -0.2%). ## Testing ```sh cargo test -p bevy_asset cargo run --example 3d_scene cargo run --example 3d_scene --features "embedded_watcher" ```
1 parent edf38ba commit 5754300

2 files changed

Lines changed: 20 additions & 3 deletions

File tree

crates/bevy_asset/src/io/embedded/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,20 @@ impl EmbeddedAssetRegistry {
4040
/// running in a non-rust file). `asset_path` is the path that will be used to identify the asset in the `embedded`
4141
/// [`AssetSource`](crate::io::AssetSource). `value` is the bytes that will be returned for the asset. This can be
4242
/// _either_ a `&'static [u8]` or a [`Vec<u8>`](alloc::vec::Vec).
43+
pub fn insert_asset(&self, full_path: PathBuf, asset_path: &Path, value: impl Into<Value>) {
44+
self.insert_asset_internal(full_path, asset_path, value.into());
45+
}
46+
47+
// Implements `insert_asset`, but with a non-generic `value` parameter. This
48+
// stops the function from being duplicated many times by monomorphization.
4349
#[cfg_attr(
4450
not(feature = "embedded_watcher"),
4551
expect(
4652
unused_variables,
4753
reason = "The `full_path` argument is not used when `embedded_watcher` is disabled."
4854
)
4955
)]
50-
pub fn insert_asset(&self, full_path: PathBuf, asset_path: &Path, value: impl Into<Value>) {
56+
fn insert_asset_internal(&self, full_path: PathBuf, asset_path: &Path, value: Value) {
5157
#[cfg(feature = "embedded_watcher")]
5258
self.root_paths
5359
.write()

crates/bevy_asset/src/io/memory.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ impl Dir {
4848
}
4949

5050
pub fn insert_asset(&self, path: &Path, value: impl Into<Value>) {
51+
self.insert_asset_internal(path, value.into());
52+
}
53+
54+
// Implements `insert_asset`, but with a non-generic `value` parameter. This
55+
// stops the function from being duplicated many times by monomorphization.
56+
fn insert_asset_internal(&self, path: &Path, value: Value) {
5157
let mut dir = self.clone();
5258
if let Some(parent) = path.parent() {
5359
dir = self.get_or_insert_dir(parent);
@@ -59,7 +65,7 @@ impl Dir {
5965
.insert(
6066
path.file_name().unwrap().to_string_lossy().into(),
6167
Data {
62-
value: value.into(),
68+
value,
6369
path: path.to_owned(),
6470
},
6571
);
@@ -82,6 +88,11 @@ impl Dir {
8288
}
8389

8490
pub fn insert_meta(&self, path: &Path, value: impl Into<Value>) {
91+
self.insert_meta_internal(path, value.into());
92+
}
93+
94+
// Implements `insert_meta` - see `insert_asset_internal` for rationale.
95+
fn insert_meta_internal(&self, path: &Path, value: Value) {
8596
let mut dir = self.clone();
8697
if let Some(parent) = path.parent() {
8798
dir = self.get_or_insert_dir(parent);
@@ -93,7 +104,7 @@ impl Dir {
93104
.insert(
94105
path.file_name().unwrap().to_string_lossy().into(),
95106
Data {
96-
value: value.into(),
107+
value,
97108
path: path.to_owned(),
98109
},
99110
);

0 commit comments

Comments
 (0)