mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-06 19:58:53 +00:00

This removes a lot of allocations and speeds up the compiler step a bit. Sadly, this patch is very invasive as it touches a lot of files. That said, each individual hunk is pretty trivial. For a non-trivial real-world example, the impact is significant, we get rid of ~29% of all allocations and improve the runtime by about 4.8% (measured until the viewer loop would start). Before: ``` Benchmark 1: ./target/release/slint-viewer ../slint-perf/app.slint Time (mean ± σ): 664.2 ms ± 6.7 ms [User: 589.2 ms, System: 74.0 ms] Range (min … max): 659.0 ms … 682.4 ms 10 runs allocations: 4886888 temporary allocations: 857508 ``` After: ``` Benchmark 1: ./target/release/slint-viewer ../slint-perf/app.slint Time (mean ± σ): 639.5 ms ± 17.8 ms [User: 556.9 ms, System: 76.2 ms] Range (min … max): 621.4 ms … 666.5 ms 10 runs allocations: 3544318 temporary allocations: 495685 ```
166 lines
5.6 KiB
Rust
166 lines
5.6 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
//! After inlining and moving declarations, all Element::base_type should be Type::BuiltinElement. This pass resolves them
|
|
//! to NativeClass and picking a variant that only contains the used properties.
|
|
|
|
use smol_str::SmolStr;
|
|
use std::collections::HashSet;
|
|
use std::rc::Rc;
|
|
|
|
use crate::langtype::{ElementType, NativeClass};
|
|
use crate::object_tree::{recurse_elem_including_sub_components, Component};
|
|
|
|
pub fn resolve_native_classes(component: &Component) {
|
|
recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
|
|
let new_native_class = {
|
|
let elem = elem.borrow();
|
|
|
|
let base_type = match &elem.base_type {
|
|
ElementType::Component(_) => {
|
|
// recurse_elem_including_sub_components will recurse into it
|
|
return;
|
|
}
|
|
ElementType::Builtin(b) => b,
|
|
ElementType::Native(_) => {
|
|
// already native
|
|
return;
|
|
}
|
|
ElementType::Global | ElementType::Error => panic!("This should not happen"),
|
|
};
|
|
|
|
let analysis = elem.property_analysis.borrow();
|
|
let native_properties_used: HashSet<_> = elem
|
|
.bindings
|
|
.keys()
|
|
.chain(analysis.iter().filter(|(_, v)| v.is_used()).map(|(k, _)| k))
|
|
.filter(|k| {
|
|
!elem.property_declarations.contains_key(*k)
|
|
&& base_type.as_ref().properties.contains_key(*k)
|
|
})
|
|
.collect();
|
|
|
|
select_minimal_class_based_on_property_usage(
|
|
&elem.base_type.as_builtin().native_class,
|
|
native_properties_used.into_iter(),
|
|
)
|
|
};
|
|
|
|
elem.borrow_mut().base_type = ElementType::Native(new_native_class);
|
|
})
|
|
}
|
|
|
|
fn lookup_property_distance(mut class: Rc<NativeClass>, name: &str) -> (usize, Rc<NativeClass>) {
|
|
let mut distance = 0;
|
|
loop {
|
|
if class.properties.contains_key(name)
|
|
|| (class.parent.is_none() && ["x", "y", "width", "height"].contains(&name))
|
|
{
|
|
return (distance, class);
|
|
}
|
|
distance += 1;
|
|
class = class.parent.as_ref().unwrap().clone();
|
|
}
|
|
}
|
|
|
|
fn select_minimal_class_based_on_property_usage<'a>(
|
|
class: &Rc<NativeClass>,
|
|
properties_used: impl Iterator<Item = &'a SmolStr>,
|
|
) -> Rc<NativeClass> {
|
|
let mut minimal_class = class.clone();
|
|
while let Some(class) = minimal_class.parent.clone() {
|
|
minimal_class = class;
|
|
}
|
|
let (_min_distance, minimal_class) = properties_used.fold(
|
|
(usize::MAX, minimal_class),
|
|
|(current_distance, current_class), prop_name| {
|
|
let (prop_distance, prop_class) = lookup_property_distance(class.clone(), prop_name);
|
|
|
|
if prop_distance < current_distance {
|
|
(prop_distance, prop_class)
|
|
} else {
|
|
(current_distance, current_class)
|
|
}
|
|
},
|
|
);
|
|
minimal_class
|
|
}
|
|
|
|
#[test]
|
|
fn test_select_minimal_class_based_on_property_usage() {
|
|
use crate::langtype::{BuiltinPropertyInfo, Type};
|
|
use smol_str::ToSmolStr;
|
|
let first = Rc::new(NativeClass::new_with_properties(
|
|
"first_class",
|
|
[("first_prop".to_smolstr(), BuiltinPropertyInfo::new(Type::Int32))].iter().cloned(),
|
|
));
|
|
|
|
let mut second = NativeClass::new_with_properties(
|
|
"second_class",
|
|
[("second_prop".to_smolstr(), BuiltinPropertyInfo::new(Type::Int32))].iter().cloned(),
|
|
);
|
|
second.parent = Some(first.clone());
|
|
let second = Rc::new(second);
|
|
|
|
let reduce_to_first =
|
|
select_minimal_class_based_on_property_usage(&second, ["first_prop".to_smolstr()].iter());
|
|
|
|
assert_eq!(reduce_to_first.class_name, first.class_name);
|
|
|
|
let reduce_to_second =
|
|
select_minimal_class_based_on_property_usage(&second, ["second_prop".to_smolstr()].iter());
|
|
|
|
assert_eq!(reduce_to_second.class_name, second.class_name);
|
|
|
|
let reduce_to_second = select_minimal_class_based_on_property_usage(
|
|
&second,
|
|
["first_prop".to_smolstr(), "second_prop".to_smolstr()].iter(),
|
|
);
|
|
|
|
assert_eq!(reduce_to_second.class_name, second.class_name);
|
|
}
|
|
|
|
#[test]
|
|
fn select_minimal_class() {
|
|
use smol_str::ToSmolStr;
|
|
let tr = crate::typeregister::TypeRegister::builtin();
|
|
let tr = tr.borrow();
|
|
let rect = tr.lookup_element("Rectangle").unwrap();
|
|
let rect = rect.as_builtin();
|
|
assert_eq!(
|
|
select_minimal_class_based_on_property_usage(
|
|
&rect.native_class,
|
|
["x".to_smolstr(), "width".to_smolstr()].iter()
|
|
)
|
|
.class_name,
|
|
"Empty",
|
|
);
|
|
assert_eq!(
|
|
select_minimal_class_based_on_property_usage(&rect.native_class, [].iter()).class_name,
|
|
"Empty",
|
|
);
|
|
assert_eq!(
|
|
select_minimal_class_based_on_property_usage(
|
|
&rect.native_class,
|
|
["border-width".to_smolstr()].iter()
|
|
)
|
|
.class_name,
|
|
"BasicBorderRectangle",
|
|
);
|
|
assert_eq!(
|
|
select_minimal_class_based_on_property_usage(
|
|
&rect.native_class,
|
|
["border-width".to_smolstr(), "x".to_smolstr()].iter()
|
|
)
|
|
.class_name,
|
|
"BasicBorderRectangle",
|
|
);
|
|
assert_eq!(
|
|
select_minimal_class_based_on_property_usage(
|
|
&rect.native_class,
|
|
["border-top-left-radius".to_smolstr(), "x".to_smolstr()].iter()
|
|
)
|
|
.class_name,
|
|
"BorderRectangle",
|
|
);
|
|
}
|