Skip to content

Commit e47d36f

Browse files
committed
libcc2rs optimizations to speedup Brunsli
1 parent 37bf61f commit e47d36f

3 files changed

Lines changed: 99 additions & 9 deletions

File tree

‎libcc2rs-macros/src/record.rs‎

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub fn expand(input: TokenStream) -> TokenStream {
3939
// The field that contains a byte is one that starts before it, usually
4040
// the last one, as fields are usually declared in the order of their
4141
// offsets.
42-
let locate = fields.iter().rev().map(|(ident, _)| {
42+
let locate = search_fields(&fields, &|ident| {
4343
quote! {
4444
if offset >= Self::OFFSETS.#ident {
4545
if let ::core::option::Option::Some(field) = ::libcc2rs::__locate_field!(
@@ -49,7 +49,7 @@ pub fn expand(input: TokenStream) -> TokenStream {
4949
}
5050
}
5151
});
52-
let locate_mut = fields.iter().rev().map(|(ident, _)| {
52+
let locate_mut = search_fields(&fields, &|ident| {
5353
quote! {
5454
if offset >= Self::OFFSETS.#ident {
5555
if let ::core::option::Option::Some(field) = ::libcc2rs::__locate_field_mut!(
@@ -59,7 +59,7 @@ pub fn expand(input: TokenStream) -> TokenStream {
5959
}
6060
}
6161
});
62-
let len_at = fields.iter().rev().map(|(ident, _)| {
62+
let len_at = search_fields(&fields, &|ident| {
6363
quote! {
6464
if offset >= Self::OFFSETS.#ident {
6565
if let ::core::option::Option::Some(len) = ::libcc2rs::__len_field!(
@@ -91,7 +91,7 @@ pub fn expand(input: TokenStream) -> TokenStream {
9191
if offset == 0 && ty == ::std::any::TypeId::of::<Self>() {
9292
return ::core::option::Option::Some(self);
9393
}
94-
#(#locate)*
94+
#locate
9595
::core::option::Option::None
9696
}
9797

@@ -105,16 +105,42 @@ pub fn expand(input: TokenStream) -> TokenStream {
105105
if offset == 0 && ty == ::std::any::TypeId::of::<Self>() {
106106
return ::core::option::Option::Some(self);
107107
}
108-
#(#locate_mut)*
108+
#locate_mut
109109
::core::option::Option::None
110110
}
111111

112112
#[allow(unused_comparisons)]
113113
fn len_at(&self, offset: usize) -> ::core::option::Option<usize> {
114-
#(#len_at)*
114+
#len_at
115115
::core::option::Option::None
116116
}
117117
}
118118
}
119119
.into()
120120
}
121+
122+
// The searches of `fields`, from the last one to the first one, for the field
123+
// that contains byte `offset`. Those in the second half of the fields are
124+
// skipped if all of them start after the byte, which is checked against the
125+
// smallest of their offsets, so the fields can be in any order.
126+
fn search_fields(
127+
fields: &[(Ident, Expr)],
128+
search: &dyn Fn(&Ident) -> proc_macro2::TokenStream,
129+
) -> proc_macro2::TokenStream {
130+
if fields.len() <= 4 {
131+
let searches = fields.iter().rev().map(|(ident, _)| search(ident));
132+
return quote! { #(#searches)* };
133+
}
134+
let (first, second) = fields.split_at(fields.len() / 2);
135+
let starts = second
136+
.iter()
137+
.map(|(ident, _)| quote! { Self::OFFSETS.#ident });
138+
let first = search_fields(first, search);
139+
let second = search_fields(second, search);
140+
quote! {
141+
if offset >= const { ::libcc2rs::__field::min_offset(&[#(#starts),*]) } {
142+
#second
143+
}
144+
#first
145+
}
146+
}

‎libcc2rs/src/field.rs‎

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,16 @@ where
449449
G: Fn(&S) -> &Vec<T> + Copy + 'static,
450450
M: Fn(&mut S) -> &mut Vec<T> + Copy + 'static,
451451
{
452+
// The elements are reached through the struct, rather than through a
453+
// pointer to the vector, which would be a parent of every access to them.
452454
#[inline]
453455
fn decay(&self) -> Ptr<T> {
454-
self.as_pointer().decay()
456+
match &self.base.kind {
457+
PtrKind::Field(_, _, FieldShape::Single) => self.as_pointer().decay(),
458+
_ => self
459+
.base
460+
.elems_of(self.get, self.get_mut, (self.offset)(&S::OFFSETS), false),
461+
}
455462
}
456463
}
457464

@@ -1020,7 +1027,6 @@ impl<S: Record, T: ByteRepr> VecElem<'_, S, T> {
10201027
let elems: Ptr<T> = self
10211028
.base
10221029
.field_ref(self.get, self.get_mut, self.offset)
1023-
.as_pointer()
10241030
.decay();
10251031
elems.offset(self.idx).upgrade()
10261032
}
@@ -1201,6 +1207,21 @@ macro_rules! __len_field {
12011207
}};
12021208
}
12031209

1210+
// The smallest of the offsets of some fields, which Record::locate compares
1211+
// the offset that it looks for with to skip all of them.
1212+
#[doc(hidden)]
1213+
pub const fn min_offset(offsets: &[usize]) -> usize {
1214+
let mut min = usize::MAX;
1215+
let mut i = 0;
1216+
while i < offsets.len() {
1217+
if offsets[i] < min {
1218+
min = offsets[i];
1219+
}
1220+
i += 1;
1221+
}
1222+
min
1223+
}
1224+
12041225
impl<T: 'static> Ptr<T> {
12051226
// Borrows the field at byte `offset` of `root`.
12061227
#[inline]
@@ -1655,6 +1676,49 @@ mod tests {
16551676
assert_ne!(a.offset(2), array_field_ptr!(other, b));
16561677
}
16571678

1679+
// A struct with enough fields for Record::locate to search them in
1680+
// halves, which are not in the order of their offsets, as in some libc
1681+
// structs.
1682+
#[derive(Default, Record, ByteRepr)]
1683+
#[byte_size(28)]
1684+
struct Shuffled {
1685+
#[offset(20)]
1686+
f: i32,
1687+
#[offset(0)]
1688+
a: i32,
1689+
#[offset(4)]
1690+
b: i32,
1691+
#[offset(16)]
1692+
e: i32,
1693+
#[offset(8)]
1694+
inner: Inner,
1695+
#[offset(24)]
1696+
g: i32,
1697+
}
1698+
1699+
#[test]
1700+
fn fields_out_of_order() {
1701+
let s: Value<Shuffled> = Rc::new(RefCell::new(Shuffled::default()));
1702+
let fields = [
1703+
field_ptr!(s, a),
1704+
field_ptr!(s, b),
1705+
field_ptr!(field_ptr!(s, inner), a),
1706+
field_ptr!(field_ptr!(s, inner), b),
1707+
field_ptr!(s, e),
1708+
field_ptr!(s, f),
1709+
field_ptr!(s, g),
1710+
];
1711+
for (i, p) in fields.iter().enumerate() {
1712+
p.write(i as i32 + 1);
1713+
}
1714+
for (i, p) in fields.iter().enumerate() {
1715+
assert_eq!(p.read(), i as i32 + 1);
1716+
}
1717+
let s = s.borrow();
1718+
assert_eq!((s.a, s.b, s.inner.a, s.inner.b), (1, 2, 3, 4));
1719+
assert_eq!((s.e, s.f, s.g), (5, 6, 7));
1720+
}
1721+
16581722
#[test]
16591723
fn pointer_size() {
16601724
// Pointers to elements of fields are no larger than other pointers.

‎libcc2rs/src/lib.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub use field::{
1818
pub mod __field {
1919
pub use crate::field::{
2020
LenLeaf, LenOf, LenRecord, LenSlice, Locate, LocateLeaf, LocateLeafMut, LocateMut,
21-
LocateRecord, LocateRecordMut, size_of_pointee,
21+
LocateRecord, LocateRecordMut, min_offset, size_of_pointee,
2222
};
2323
}
2424

0 commit comments

Comments
 (0)