mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
perf: defer behaviour construction and test fresh resource loads
This commit is contained in:
1 parent
a01d47f28f
commit
cf837118e9
7 files changed
+487
-16
No files matched your search
@@ -0,0 +1,227 @@
|
||||
{
|
||||
"scope": "Assets + block facts + behaviours loader only; excludes full startup",
|
||||
"resources": ".local/horizon-client/26.3",
|
||||
"repeats": 5,
|
||||
"metrics": {
|
||||
"baseline": {
|
||||
"phase_ms": {
|
||||
"median": 1996.921,
|
||||
"samples": [
|
||||
2213.335,
|
||||
1453.207,
|
||||
1637.901,
|
||||
1996.921,
|
||||
2010.335
|
||||
],
|
||||
"range": [
|
||||
1453.207,
|
||||
2213.335
|
||||
]
|
||||
},
|
||||
"total_ms": {
|
||||
"median": 4728.136,
|
||||
"samples": [
|
||||
5628.975,
|
||||
2147.329,
|
||||
2162.696,
|
||||
4728.136,
|
||||
6404.524
|
||||
],
|
||||
"range": [
|
||||
2147.329,
|
||||
6404.524
|
||||
]
|
||||
},
|
||||
"cpu_ms": {
|
||||
"median": 2514.776,
|
||||
"samples": [
|
||||
2919.908,
|
||||
2088.239,
|
||||
2212.892,
|
||||
2716.104,
|
||||
2514.776
|
||||
],
|
||||
"range": [
|
||||
2088.239,
|
||||
2919.908
|
||||
]
|
||||
},
|
||||
"peak_rss_kib": {
|
||||
"median": 819824,
|
||||
"samples": [
|
||||
819824,
|
||||
820872,
|
||||
820744,
|
||||
818980,
|
||||
818816
|
||||
]
|
||||
}
|
||||
},
|
||||
"pr607": {
|
||||
"phase_ms": {
|
||||
"median": 586.707,
|
||||
"samples": [
|
||||
464.424,
|
||||
739.67,
|
||||
586.707,
|
||||
1373.307,
|
||||
509.266
|
||||
],
|
||||
"range": [
|
||||
464.424,
|
||||
1373.307
|
||||
]
|
||||
},
|
||||
"total_ms": {
|
||||
"median": 1128.6,
|
||||
"samples": [
|
||||
954.73,
|
||||
1764.614,
|
||||
1128.6,
|
||||
4312.528,
|
||||
1030.735
|
||||
],
|
||||
"range": [
|
||||
954.73,
|
||||
4312.528
|
||||
]
|
||||
},
|
||||
"cpu_ms": {
|
||||
"median": 1145.826,
|
||||
"samples": [
|
||||
981.058,
|
||||
1465.79,
|
||||
1145.826,
|
||||
1736.807,
|
||||
1056.992
|
||||
],
|
||||
"range": [
|
||||
981.058,
|
||||
1736.807
|
||||
]
|
||||
},
|
||||
"peak_rss_kib": {
|
||||
"median": 351900,
|
||||
"samples": [
|
||||
352164,
|
||||
351816,
|
||||
351900,
|
||||
352348,
|
||||
350492
|
||||
]
|
||||
}
|
||||
},
|
||||
"shared": {
|
||||
"phase_ms": {
|
||||
"median": 560.117,
|
||||
"samples": [
|
||||
522.284,
|
||||
1255.75,
|
||||
560.117,
|
||||
2151.064,
|
||||
479.329
|
||||
],
|
||||
"range": [
|
||||
479.329,
|
||||
2151.064
|
||||
]
|
||||
},
|
||||
"total_ms": {
|
||||
"median": 1101.305,
|
||||
"samples": [
|
||||
1011.715,
|
||||
2565.449,
|
||||
1101.305,
|
||||
2960.186,
|
||||
1006.817
|
||||
],
|
||||
"range": [
|
||||
1006.817,
|
||||
2960.186
|
||||
]
|
||||
},
|
||||
"cpu_ms": {
|
||||
"median": 1128.214,
|
||||
"samples": [
|
||||
1035.19,
|
||||
1565.383,
|
||||
1128.214,
|
||||
1494.36,
|
||||
1031.198
|
||||
],
|
||||
"range": [
|
||||
1031.198,
|
||||
1565.383
|
||||
]
|
||||
},
|
||||
"peak_rss_kib": {
|
||||
"median": 351532,
|
||||
"samples": [
|
||||
351624,
|
||||
350540,
|
||||
351532,
|
||||
351716,
|
||||
350076
|
||||
]
|
||||
}
|
||||
},
|
||||
"deferred": {
|
||||
"phase_ms": {
|
||||
"median": 564.046,
|
||||
"samples": [
|
||||
433.35,
|
||||
665.149,
|
||||
587.456,
|
||||
564.046,
|
||||
497.422
|
||||
],
|
||||
"range": [
|
||||
433.35,
|
||||
665.149
|
||||
]
|
||||
},
|
||||
"total_ms": {
|
||||
"median": 1143.129,
|
||||
"samples": [
|
||||
885.053,
|
||||
1143.129,
|
||||
1195.869,
|
||||
1074.27,
|
||||
1162.103
|
||||
],
|
||||
"range": [
|
||||
885.053,
|
||||
1195.869
|
||||
]
|
||||
},
|
||||
"cpu_ms": {
|
||||
"median": 1114.261,
|
||||
"samples": [
|
||||
910.946,
|
||||
1128.96,
|
||||
1194.638,
|
||||
1114.261,
|
||||
1073.823
|
||||
],
|
||||
"range": [
|
||||
910.946,
|
||||
1194.638
|
||||
]
|
||||
},
|
||||
"peak_rss_kib": {
|
||||
"median": 273776,
|
||||
"samples": [
|
||||
274320,
|
||||
273860,
|
||||
272548,
|
||||
273776,
|
||||
272520
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"baseline_commit": "e422bd13",
|
||||
"pr607_commit": "a01d47f2",
|
||||
"measurement": "same optimized binary, isolated processes in alternating forward/reverse four-mode runs",
|
||||
"limitations": "Includes Assets::load, BlockFacts, Behaviours; excludes complete client startup, pack/mod root resolution, graphics and first frame. Different modes emulate the original independent, PR #607 at a01d47f2, paired and deferred startup sequences in the same release build. Machine was not idle. Elapsed times show high variance; no statistically established gain between PR, paired and deferred modes."
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
//! Profile the fact and behaviour table phase of client startup.
|
||||
//! Run in separate processes for baseline and shared JSON modes to capture peak RSS.
|
||||
//! Run each mode in a separate process to capture CPU usage and peak RSS.
|
||||
//! The deferred mode evaluates both phases contiguously for fair loader comparison;
|
||||
//! actual client startup performs other initialization between these phases.
|
||||
use horizon_client::{assets::Assets, blocks::BlockFacts};
|
||||
use horizon_world::level::Behaviours;
|
||||
use std::{path::Path, time::Instant};
|
||||
@@ -7,9 +9,9 @@ use std::{path::Path, time::Instant};
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut args = std::env::args().skip(1);
|
||||
let root = args.next().ok_or("expected resource directory")?;
|
||||
let mode = args.next().ok_or("expected baseline or shared")?;
|
||||
if mode != "baseline" && mode != "shared" {
|
||||
return Err("expected baseline or shared".into());
|
||||
let mode = args.next().ok_or("expected baseline, pr607 or shared")?;
|
||||
if !["baseline", "pr607", "shared", "deferred"].contains(&mode.as_str()) {
|
||||
return Err("expected baseline, pr607 or shared".into());
|
||||
}
|
||||
let root = Path::new(&root);
|
||||
let total = Instant::now();
|
||||
@@ -20,11 +22,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
BlockFacts::load(root, assets.registry)?,
|
||||
Behaviours::load(assets.registry, root)?,
|
||||
)
|
||||
} else {
|
||||
} else if mode == "pr607" {
|
||||
// Original PR #607 sequence at a01d47f2, retained as a same-build comparator.
|
||||
let json: serde_json::Value = serde_json::from_reader(std::io::BufReader::new(
|
||||
std::fs::File::open(root.join("block-behaviours.json"))?,
|
||||
))?;
|
||||
BlockFacts::load_with_behaviours(root, assets.registry, &json)?
|
||||
} else if mode == "shared" {
|
||||
BlockFacts::load_pair(root, assets.registry)?
|
||||
} else {
|
||||
let json: serde_json::Value = serde_json::from_reader(std::io::BufReader::new(
|
||||
std::fs::File::open(root.join("block-behaviours.json"))?,
|
||||
))?;
|
||||
let (facts, gameplay) = BlockFacts::load_with_projection(root, assets.registry, &json)?;
|
||||
let behaviours = Behaviours::load_with_gameplay(assets.registry, root, &json, &gameplay)?;
|
||||
(facts, behaviours)
|
||||
};
|
||||
println!("phase_ms={:.3}", started.elapsed().as_secs_f64() * 1000.0);
|
||||
println!("total_ms={:.3}", total.elapsed().as_secs_f64() * 1000.0);
|
||||
|
||||
@@ -110,6 +110,107 @@ impl StateFacts {
|
||||
self.classes.iter().any(|c| &**c == name)
|
||||
}
|
||||
}
|
||||
/// Compact, owned behaviour inputs retained until the normal behaviour startup stage.
|
||||
/// No borrowed gameplay JSON or process-wide cache survives the fact loader.
|
||||
pub struct GameplayProjection {
|
||||
flags: u16,
|
||||
emission: u64,
|
||||
dampening: u64,
|
||||
hardness: f64,
|
||||
push: Box<str>,
|
||||
volume: f64,
|
||||
pitch: f64,
|
||||
place: Box<str>,
|
||||
break_sound: Box<str>,
|
||||
fall: Box<str>,
|
||||
}
|
||||
|
||||
impl GameplayProjection {
|
||||
fn from_entry(entry: &decode::Entry<'_>) -> Self {
|
||||
const FLAGS: [&str; 11] = [
|
||||
"replaceable",
|
||||
"random_ticks",
|
||||
"solid_render",
|
||||
"liquid",
|
||||
"block_entity",
|
||||
"ignited_by_lava",
|
||||
"can_occlude",
|
||||
"full_collision",
|
||||
"skylight_down",
|
||||
"requires_tool",
|
||||
"shape_occlusion",
|
||||
];
|
||||
Self {
|
||||
flags: FLAGS.iter().enumerate().fold(0, |flags, (i, name)| {
|
||||
flags | ((u16::from(entry.flag(name))) << i)
|
||||
}),
|
||||
emission: entry.unsigned("emission"),
|
||||
dampening: entry.unsigned("dampening"),
|
||||
hardness: entry.number("hardness"),
|
||||
push: entry.text("push").into(),
|
||||
volume: entry.sound_number("volume"),
|
||||
pitch: entry.sound_number("pitch"),
|
||||
place: entry.sound_id("place").into(),
|
||||
break_sound: entry.sound_id("break").into(),
|
||||
fall: entry.sound_id("fall").into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GameplayStateSource for GameplayProjection {
|
||||
fn flag(&self, name: &str) -> bool {
|
||||
const FLAGS: [&str; 11] = [
|
||||
"replaceable",
|
||||
"random_ticks",
|
||||
"solid_render",
|
||||
"liquid",
|
||||
"block_entity",
|
||||
"ignited_by_lava",
|
||||
"can_occlude",
|
||||
"full_collision",
|
||||
"skylight_down",
|
||||
"requires_tool",
|
||||
"shape_occlusion",
|
||||
];
|
||||
FLAGS
|
||||
.iter()
|
||||
.position(|field| *field == name)
|
||||
.is_some_and(|index| self.flags & (1 << index) != 0)
|
||||
}
|
||||
fn unsigned(&self, name: &str) -> u64 {
|
||||
match name {
|
||||
"emission" => self.emission,
|
||||
"dampening" => self.dampening,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
fn number(&self, name: &str) -> f64 {
|
||||
if name == "hardness" {
|
||||
self.hardness
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
fn text(&self, name: &str) -> &str {
|
||||
if name == "push" { &self.push } else { "" }
|
||||
}
|
||||
fn sound_number(&self, name: &str) -> f64 {
|
||||
match name {
|
||||
"volume" => self.volume,
|
||||
"pitch" => self.pitch,
|
||||
_ => 1.0,
|
||||
}
|
||||
}
|
||||
fn sound_id(&self, name: &str) -> &str {
|
||||
match name {
|
||||
"place" => &self.place,
|
||||
"break" => &self.break_sound,
|
||||
"fall" => &self.fall,
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlockFacts {
|
||||
pub states: Vec<StateFacts>,
|
||||
pub air: BlockStateId,
|
||||
@@ -122,6 +223,26 @@ impl BlockFacts {
|
||||
Self::load_with_behaviour_json(root, registry, &behaviours)
|
||||
}
|
||||
|
||||
/// Load both tables from one resource root, sharing each JSON decode.
|
||||
/// No data is cached across calls: changes to prepared packs or mods are read again.
|
||||
pub fn load_pair(root: &Path, registry: &BlockRegistry) -> Result<(Self, Behaviours)> {
|
||||
let behaviours: Value = serde_json::from_reader(BufReader::new(File::open(
|
||||
root.join("block-behaviours.json"),
|
||||
)?))?;
|
||||
Self::load_with_behaviours(root, registry, &behaviours)
|
||||
}
|
||||
|
||||
/// Decode state facts once, retaining only the small owned fields needed later.
|
||||
pub fn load_with_projection(
|
||||
root: &Path,
|
||||
registry: &BlockRegistry,
|
||||
behaviours: &Value,
|
||||
) -> Result<(Self, Vec<GameplayProjection>)> {
|
||||
Self::load_with_callback(root, registry, behaviours, |entries| {
|
||||
Ok(entries.iter().map(GameplayProjection::from_entry).collect())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_with_behaviour_json(
|
||||
root: &Path,
|
||||
registry: &BlockRegistry,
|
||||
|
||||
@@ -10172,8 +10172,8 @@ pub fn run() -> Result<()> {
|
||||
let behaviour_json: serde_json::Value = serde_json::from_reader(std::io::BufReader::new(
|
||||
std::fs::File::open(root.join("block-behaviours.json"))?,
|
||||
))?;
|
||||
let (facts, behaviours) =
|
||||
BlockFacts::load_with_behaviours(&root, assets.registry, &behaviour_json)?;
|
||||
let (facts, gameplay_projection) =
|
||||
BlockFacts::load_with_projection(&root, assets.registry, &behaviour_json)?;
|
||||
let mob_data = horizon_client::mobs::MobData::load(&root, &facts);
|
||||
horizon_client::mobs::trial_spawner::load_registry(&root);
|
||||
log::info!("load facts: {:.2}s", started.elapsed().as_secs_f32());
|
||||
@@ -10240,7 +10240,12 @@ pub fn run() -> Result<()> {
|
||||
ambient: horizon_client::ambience::AmbientTables::load(&root),
|
||||
nether_environment: Environment::load_dimension(&root, "the_nether", false)?,
|
||||
end_environment: Environment::load_dimension(&root, "the_end", false)?,
|
||||
behaviours,
|
||||
behaviours: Behaviours::load_with_gameplay(
|
||||
registry_owner.registry,
|
||||
&root,
|
||||
&behaviour_json,
|
||||
&gameplay_projection,
|
||||
)?,
|
||||
mob_types: MobTypes::load(&root)?
|
||||
.types
|
||||
.into_iter()
|
||||
|
||||
@@ -149,9 +149,27 @@ fn content_root_loads_with_every_table() {
|
||||
let (shared_facts, shared_behaviours) =
|
||||
BlockFacts::load_with_behaviours(&root, new, &shared_json).unwrap();
|
||||
let independent_behaviours = horizon_world::level::Behaviours::load(new, &root).unwrap();
|
||||
let (deferred_facts, gameplay) =
|
||||
BlockFacts::load_with_projection(&root, new, &shared_json).unwrap();
|
||||
let deferred_behaviours =
|
||||
horizon_world::level::Behaviours::load_with_gameplay(new, &root, &shared_json, &gameplay)
|
||||
.unwrap();
|
||||
assert_eq!(shared_facts.states.len(), facts.states.len());
|
||||
for state in [ruby, ruby_door, BlockStateId(n + 65)] {
|
||||
assert_eq!(shared_facts.get(state).hardness, facts.get(state).hardness);
|
||||
assert_eq!(
|
||||
deferred_facts.get(state).hardness,
|
||||
facts.get(state).hardness
|
||||
);
|
||||
assert_eq!(deferred_facts.get(state).classes, facts.get(state).classes);
|
||||
assert_eq!(
|
||||
deferred_behaviours.facts(state).flags,
|
||||
independent_behaviours.facts(state).flags
|
||||
);
|
||||
assert_eq!(
|
||||
deferred_behaviours.data(state).sounds.place,
|
||||
independent_behaviours.data(state).sounds.place
|
||||
);
|
||||
assert_eq!(shared_facts.get(state).emission, facts.get(state).emission);
|
||||
assert_eq!(shared_facts.get(state).classes, facts.get(state).classes);
|
||||
assert_eq!(
|
||||
@@ -254,6 +272,16 @@ fn shared_behaviour_document_matches_independent_loaders() {
|
||||
.unwrap();
|
||||
let original = BlockFacts::load(&root, assets.registry).unwrap();
|
||||
let (shared, b) = BlockFacts::load_with_behaviours(&root, assets.registry, &doc).unwrap();
|
||||
let (deferred_facts, projection) =
|
||||
BlockFacts::load_with_projection(&root, assets.registry, &doc).unwrap();
|
||||
let deferred = horizon_world::level::Behaviours::load_with_gameplay(
|
||||
assets.registry,
|
||||
&root,
|
||||
&doc,
|
||||
&projection,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(deferred_facts.states.len(), shared.states.len());
|
||||
assert_eq!(original.states.len(), shared.states.len());
|
||||
for (a, b) in original.states.iter().zip(&shared.states) {
|
||||
assert_eq!(a.classes, b.classes);
|
||||
@@ -269,6 +297,12 @@ fn shared_behaviour_document_matches_independent_loaders() {
|
||||
for id in 0..assets.registry.len() {
|
||||
let state = horizon_world::registry::BlockStateId(id as u32);
|
||||
let (a_state, b_state) = (a.facts(state), b.facts(state));
|
||||
let deferred_state = deferred.facts(state);
|
||||
assert_eq!(b_state.flags, deferred_state.flags);
|
||||
assert_eq!(b_state.emission, deferred_state.emission);
|
||||
assert_eq!(b_state.dampening, deferred_state.dampening);
|
||||
assert_eq!(b_state.hardness, deferred_state.hardness);
|
||||
assert_eq!(b_state.push, deferred_state.push);
|
||||
assert_eq!(a_state.flags, b_state.flags);
|
||||
assert_eq!(a_state.sturdy, b_state.sturdy);
|
||||
assert_eq!(a_state.weak, b_state.weak);
|
||||
@@ -288,3 +322,73 @@ fn shared_behaviour_document_matches_independent_loaders() {
|
||||
assert_eq!(a_sound.fall, b_sound.fall);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn independent_reloads_read_changed_behaviour_json_and_report_invalid_files() {
|
||||
let Some(base) = prepared() else { return };
|
||||
let scratch = std::env::temp_dir().join(format!("horizon-json-reloads-{}", std::process::id()));
|
||||
let _ = fs::remove_dir_all(&scratch);
|
||||
fs::create_dir_all(&scratch).unwrap();
|
||||
// Keep the remaining resource tree intact without copying a huge prepared client.
|
||||
for entry in fs::read_dir(&base).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::symlink(entry.path(), scratch.join(entry.file_name())).unwrap();
|
||||
}
|
||||
let assets = Assets::load(&base).unwrap();
|
||||
let registry = assets.registry;
|
||||
let original = fs::read(base.join("block-behaviours.json")).unwrap();
|
||||
fs::remove_file(scratch.join("block-behaviours.json")).unwrap();
|
||||
fs::write(scratch.join("block-behaviours.json"), &original).unwrap();
|
||||
let (facts, _) = BlockFacts::load_pair(&scratch, registry).unwrap();
|
||||
assert_eq!(facts.states.len(), registry.len());
|
||||
assert_eq!(
|
||||
BlockFacts::load(&scratch, registry).unwrap().states.len(),
|
||||
registry.len()
|
||||
);
|
||||
assert!(horizon_world::level::Behaviours::load(registry, &scratch).is_ok());
|
||||
|
||||
fs::write(scratch.join("block-behaviours.json"), b"{").unwrap();
|
||||
assert!(BlockFacts::load_pair(&scratch, registry).is_err());
|
||||
assert!(BlockFacts::load(&scratch, registry).is_err());
|
||||
assert!(horizon_world::level::Behaviours::load(registry, &scratch).is_err());
|
||||
|
||||
fs::write(scratch.join("block-behaviours.json"), b"{}").unwrap();
|
||||
assert!(BlockFacts::load_pair(&scratch, registry).is_err());
|
||||
assert!(BlockFacts::load(&scratch, registry).is_err());
|
||||
assert!(horizon_world::level::Behaviours::load(registry, &scratch).is_err());
|
||||
|
||||
fs::write(scratch.join("block-behaviours.json"), original).unwrap();
|
||||
assert_eq!(
|
||||
BlockFacts::load_pair(&scratch, registry)
|
||||
.unwrap()
|
||||
.0
|
||||
.states
|
||||
.len(),
|
||||
registry.len()
|
||||
);
|
||||
fs::remove_file(scratch.join("gameplay-facts.json")).unwrap();
|
||||
fs::write(scratch.join("gameplay-facts.json"), b"{\"states\":[]}").unwrap();
|
||||
assert!(BlockFacts::load_pair(&scratch, registry).is_err());
|
||||
assert!(BlockFacts::load(&scratch, registry).is_err());
|
||||
assert!(horizon_world::level::Behaviours::load(registry, &scratch).is_err());
|
||||
fs::write(scratch.join("gameplay-facts.json"), b"{").unwrap();
|
||||
assert!(BlockFacts::load_pair(&scratch, registry).is_err());
|
||||
assert!(BlockFacts::load(&scratch, registry).is_err());
|
||||
assert!(horizon_world::level::Behaviours::load(registry, &scratch).is_err());
|
||||
fs::write(
|
||||
scratch.join("gameplay-facts.json"),
|
||||
fs::read(base.join("gameplay-facts.json")).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
BlockFacts::load_pair(&scratch, registry)
|
||||
.unwrap()
|
||||
.0
|
||||
.states
|
||||
.len(),
|
||||
registry.len()
|
||||
);
|
||||
fs::remove_dir_all(&scratch).unwrap();
|
||||
}
|
||||
@@ -155,7 +155,7 @@ mod tick_parity {
|
||||
position::{BlockPos, ChunkPos},
|
||||
random::{LegacyRandom, RandomSource},
|
||||
};
|
||||
use horizon_world::level::{Behaviours, LevelAccess, LevelEvent, Ticks, data};
|
||||
use horizon_world::level::{LevelAccess, LevelEvent, Ticks, data};
|
||||
use std::{cell::RefCell, path::PathBuf};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -218,8 +218,7 @@ mod tick_parity {
|
||||
return;
|
||||
}
|
||||
let registry = data::load_registry(&root).unwrap();
|
||||
let mut facts = BlockFacts::load(&root, ®istry).unwrap();
|
||||
let behaviours = Behaviours::load(®istry, &root).unwrap();
|
||||
let (mut facts, behaviours) = BlockFacts::load_pair(&root, ®istry).unwrap();
|
||||
let grass = registry.default_state("minecraft:grass_block").unwrap();
|
||||
let chunk = |x| {
|
||||
let mut c = Chunk::empty(x, 0);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare baseline and shared JSON parsing in the client startup table phase.
|
||||
"""Compare four JSON loading sequences in the client startup table phase.
|
||||
|
||||
Build first with `cargo build --release -p horizon-client --example shared_json_startup_profile`.
|
||||
This covers Assets::load plus BlockFacts::load and Behaviours::load, not full game
|
||||
startup, rendering, resource pack setup, or first frame. Peak RSS includes Assets.
|
||||
Modes: baseline uses independent public loaders, pr607 reproduces a01d47f2,
|
||||
shared uses the paired wrapper, and deferred retains the projection until the
|
||||
normal behaviour stage. All modes use one optimized executable. This covers
|
||||
Assets::load plus BlockFacts and Behaviours loading; it excludes full startup,
|
||||
rendering, resource-pack resolution and first frame. Peak RSS includes Assets.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
@@ -44,9 +47,9 @@ def main():
|
||||
binary, resources = args.binary.resolve(), args.resources.resolve()
|
||||
if not binary.is_file() or not resources.is_dir():
|
||||
parser.error("binary or resources missing; build the example and prepare resources first")
|
||||
samples = {"baseline": [], "shared": []}
|
||||
samples = {"baseline": [], "pr607": [], "shared": [], "deferred": []}
|
||||
for i in range(args.repeats):
|
||||
for mode in (("baseline", "shared") if i % 2 == 0 else ("shared", "baseline")):
|
||||
for mode in (("baseline", "pr607", "shared", "deferred") if i % 2 == 0 else ("deferred", "shared", "pr607", "baseline")):
|
||||
samples[mode].append(run(binary, resources, mode))
|
||||
result = {
|
||||
"scope": "Assets + block facts + behaviours loader only; excludes full startup",
|
||||
|
||||
Reference in new issue
Block a user