Skip to content

Commit 77631e6

Browse files
committed
tiny optimization for our fast path restore idea
1 parent a00df75 commit 77631e6

1 file changed

Lines changed: 150 additions & 62 deletions

File tree

turbopack/crates/turbo-tasks-macros/src/derive/task_storage_macro.rs

Lines changed: 150 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -644,8 +644,35 @@ impl GroupedFields {
644644
}
645645

646646
/// Returns an iterator over all lazy fields (both data and meta categories).
647+
///
648+
/// The order is **sorted by category** — transient variants first, then
649+
/// meta, then data — with schema declaration order preserved within each
650+
/// category. This grouping is load-bearing for codegen: contiguous
651+
/// categories in the generated `LazyField` enum let LLVM lower
652+
/// `is_persistent()` / `is_meta()` to a single integer range check on the
653+
/// discriminant tag instead of a per-variant jump table.
654+
///
655+
/// Every downstream generator (enum declaration, `index_and_persistence`,
656+
/// restore merge arms, `build_lazy_index`) iterates `all_lazy()` and uses
657+
/// `enumerate()` positions as the variant index, so they all pick up the
658+
/// same sorted order consistently. `persistent_lazy(category)` does not
659+
/// need to match this order because its consumers (bincode encode/decode,
660+
/// clone arms) use per-category enumeration and Rust match arms are
661+
/// order-independent.
647662
fn all_lazy(&self) -> impl Iterator<Item = &FieldInfo> {
648-
self.fields.iter().filter(|f| !f.is_flag() && f.lazy)
663+
let mut lazy: Vec<&FieldInfo> = self
664+
.fields
665+
.iter()
666+
.filter(|f| !f.is_flag() && f.lazy)
667+
.collect();
668+
// Stable sort by category rank; within a category, preserve schema
669+
// declaration order.
670+
lazy.sort_by_key(|f| match f.category {
671+
Category::Transient => 0u8,
672+
Category::Meta => 1,
673+
Category::Data => 2,
674+
});
675+
lazy.into_iter()
649676
}
650677

651678
/// Returns true if there are any lazy fields.
@@ -1009,41 +1036,66 @@ fn generate_lazy_field_enum(grouped_fields: &GroupedFields) -> TokenStream {
10091036
})
10101037
.collect();
10111038

1012-
// Generate is_persistent (transient check) method arms
1013-
let is_persistent_arms: Vec<_> = all_lazy_fields
1039+
// Or-pattern lists for `is_persistent` / `is_meta`. Because `all_lazy()`
1040+
// returns variants grouped by category (transient, then meta, then data),
1041+
// these lists cover contiguous runs of the enum, giving LLVM the clearest
1042+
// shape to lower each predicate to a single integer range check on the
1043+
// discriminant tag.
1044+
let persistent_patterns: Vec<_> = all_lazy_fields
10141045
.iter()
1015-
.map(|field| {
1016-
let variant_name = &field.variant_name;
1017-
let is_persistent = !field.is_transient();
1018-
quote! {
1019-
LazyField::#variant_name(_) => #is_persistent
1020-
}
1046+
.filter(|f| !f.is_transient())
1047+
.map(|f| {
1048+
let variant_name = &f.variant_name;
1049+
quote! { LazyField::#variant_name(_) }
10211050
})
10221051
.collect();
1023-
1024-
// Generate is_meta/is_data method arms
1025-
let is_meta_arms: Vec<_> = all_lazy_fields
1052+
let meta_patterns: Vec<_> = all_lazy_fields
10261053
.iter()
1027-
.map(|field| {
1028-
let variant_name = &field.variant_name;
1029-
let is_meta = field.category == Category::Meta;
1030-
quote! {
1031-
LazyField::#variant_name(_) => #is_meta
1032-
}
1054+
.filter(|f| f.category == Category::Meta)
1055+
.map(|f| {
1056+
let variant_name = &f.variant_name;
1057+
quote! { LazyField::#variant_name(_) }
1058+
})
1059+
.collect();
1060+
let data_patterns: Vec<_> = all_lazy_fields
1061+
.iter()
1062+
.filter(|f| f.category == Category::Data)
1063+
.map(|f| {
1064+
let variant_name = &f.variant_name;
1065+
quote! { LazyField::#variant_name(_) }
10331066
})
10341067
.collect();
10351068

1036-
// Generate discriminant_index method arms: each variant maps to its
1037-
// position in the enum definition. Used to index into a fixed-size array
1038-
// for O(1) variant lookup (e.g. when merging transient residue on restore).
1039-
let discriminant_arms: Vec<_> = all_lazy_fields
1069+
// `matches!(self, ... | ... )` requires at least one pattern. Fall back to
1070+
// `false` if the schema has no variants in a given category.
1071+
let is_persistent_body = if persistent_patterns.is_empty() {
1072+
quote! { false }
1073+
} else {
1074+
quote! { matches!(self, #(#persistent_patterns)|*) }
1075+
};
1076+
let is_meta_body = if meta_patterns.is_empty() {
1077+
quote! { false }
1078+
} else {
1079+
quote! { matches!(self, #(#meta_patterns)|*) }
1080+
};
1081+
let is_data_body = if data_patterns.is_empty() {
1082+
quote! { false }
1083+
} else {
1084+
quote! { matches!(self, #(#data_patterns)|*) }
1085+
};
1086+
1087+
// (discriminant, is_persistent) arms for the restore prescan — each
1088+
// variant maps to its position in the enum definition (used as a
1089+
// fixed-size array offset) paired with its persistence bit.
1090+
let index_and_persistence_arms: Vec<_> = all_lazy_fields
10401091
.iter()
10411092
.enumerate()
10421093
.map(|(idx, field)| {
10431094
let variant_name = &field.variant_name;
10441095
let idx = idx as u8;
1096+
let is_persistent = !field.is_transient();
10451097
quote! {
1046-
LazyField::#variant_name(_) => #idx
1098+
LazyField::#variant_name(_) => (#idx, #is_persistent)
10471099
}
10481100
})
10491101
.collect();
@@ -1072,32 +1124,43 @@ fn generate_lazy_field_enum(grouped_fields: &GroupedFields) -> TokenStream {
10721124
}
10731125
}
10741126

1075-
#[doc = "Returns true if this field should be persisted (not transient)"]
1127+
#[doc = "Returns true if this field should be persisted (not transient)."]
1128+
#[doc = ""]
1129+
#[doc = "Variants are sorted so persistent variants form a contiguous"]
1130+
#[doc = "range; LLVM can lower this `matches!` to a single integer"]
1131+
#[doc = "compare on the discriminant tag."]
1132+
#[inline]
10761133
pub fn is_persistent(&self) -> bool {
1077-
match self {
1078-
#(#is_persistent_arms),*
1079-
}
1134+
#is_persistent_body
10801135
}
10811136

1082-
#[doc = "Returns true if this field belongs to the meta category"]
1137+
#[doc = "Returns true if this field belongs to the meta category."]
1138+
#[doc = ""]
1139+
#[doc = "Meta variants form a contiguous range between the transient"]
1140+
#[doc = "prefix and the data suffix; expect a range-check lowering."]
1141+
#[inline]
10831142
pub fn is_meta(&self) -> bool {
1084-
match self {
1085-
#(#is_meta_arms),*
1086-
}
1143+
#is_meta_body
10871144
}
10881145

1089-
#[doc = "Returns true if this field belongs to the data category"]
1146+
#[doc = "Returns true if this field belongs to the data category."]
1147+
#[doc = ""]
1148+
#[doc = "Data variants form the trailing contiguous range of the"]
1149+
#[doc = "enum; expect a range-check lowering."]
1150+
#[inline]
10901151
pub fn is_data(&self) -> bool {
1091-
!self.is_meta()
1152+
#is_data_body
10921153
}
10931154

1094-
#[doc = "Variant index (position in the LazyField enum definition)."]
1155+
#[doc = "Variant index paired with whether this variant is persistent."]
10951156
#[doc = ""]
1096-
#[doc = "Stable per-variant index usable as an array offset; see"]
1097-
#[doc = "`NUM_VARIANTS` for the array size."]
1098-
pub const fn discriminant_index(&self) -> u8 {
1157+
#[doc = "Index is the variant's position in the LazyField enum"]
1158+
#[doc = "definition, usable as an array offset of size `NUM_VARIANTS`."]
1159+
#[doc = "Used by the restore prescan to answer both \"where does this"]
1160+
#[doc = "variant live?\" and \"do we care about it?\" in a single match."]
1161+
const fn index_and_persistence(&self) -> (u8, bool) {
10991162
match self {
1100-
#(#discriminant_arms),*
1163+
#(#index_and_persistence_arms),*
11011164
}
11021165
}
11031166
}
@@ -3571,13 +3634,17 @@ fn generate_snapshot_restore_methods(grouped_fields: &GroupedFields) -> TokenStr
35713634

35723635
#restore_meta_flags
35733636

3574-
// Merge persistent meta lazy fields. Build a discriminant → index
3575-
// map over `self.lazy` once, then merge each source variant in
3576-
// O(1) by discriminant.
3577-
let index = Self::build_lazy_index(&self.lazy);
3578-
for field in source.lazy {
3579-
debug_assert!(field.is_persistent());
3580-
self.restore_lazy_field(field, &index);
3637+
// Cold path when `self.lazy` has no persistent residue: bulk
3638+
// extend. Otherwise build a discriminant → position index and
3639+
// merge each source variant in O(1).
3640+
match Self::build_lazy_index(&self.lazy) {
3641+
None => self.lazy.extend(source.lazy),
3642+
Some(index) => {
3643+
for field in source.lazy {
3644+
debug_assert!(field.is_persistent());
3645+
self.restore_lazy_field(field, &index);
3646+
}
3647+
}
35813648
}
35823649
}
35833650

@@ -3591,10 +3658,14 @@ fn generate_snapshot_restore_methods(grouped_fields: &GroupedFields) -> TokenStr
35913658

35923659
#restore_data_flags
35933660

3594-
let index = Self::build_lazy_index(&self.lazy);
3595-
for field in source.lazy {
3596-
debug_assert!(field.is_persistent());
3597-
self.restore_lazy_field(field, &index);
3661+
match Self::build_lazy_index(&self.lazy) {
3662+
None => self.lazy.extend(source.lazy),
3663+
Some(index) => {
3664+
for field in source.lazy {
3665+
debug_assert!(field.is_persistent());
3666+
self.restore_lazy_field(field, &index);
3667+
}
3668+
}
35983669
}
35993670
}
36003671

@@ -3611,25 +3682,43 @@ fn generate_snapshot_restore_methods(grouped_fields: &GroupedFields) -> TokenStr
36113682

36123683
#restore_all_flags
36133684

3614-
let index = Self::build_lazy_index(&self.lazy);
3615-
for field in source.lazy {
3616-
debug_assert!(field.is_persistent());
3617-
self.restore_lazy_field(field, &index);
3685+
match Self::build_lazy_index(&self.lazy) {
3686+
None => self.lazy.extend(source.lazy),
3687+
Some(index) => {
3688+
for field in source.lazy {
3689+
debug_assert!(field.is_persistent());
3690+
self.restore_lazy_field(field, &index);
3691+
}
3692+
}
36183693
}
36193694
}
36203695

36213696
/// Build a discriminant → position lookup table over `lazy`.
36223697
///
3623-
/// `u8::MAX` marks "variant not present". Relies on `lazy.len() < 255`
3624-
/// which is trivially true (at most `LazyField::NUM_VARIANTS` entries,
3625-
/// which is well under 255).
3626-
fn build_lazy_index(lazy: &[LazyField]) -> [u8; LazyField::NUM_VARIANTS] {
3698+
/// Returns `None` if `lazy` contains no persistent entries — the hot
3699+
/// path for cold restores, where the caller can skip per-field
3700+
/// dispatch and bulk-extend directly. `Some(index)` is returned
3701+
/// only when persistent residue is present; `u8::MAX` marks
3702+
/// "variant not present".
3703+
///
3704+
/// Relies on `lazy.len() < 255`, which is trivially true (at most
3705+
/// `LazyField::NUM_VARIANTS` entries, well under 255).
3706+
fn build_lazy_index(
3707+
lazy: &[LazyField],
3708+
) -> Option<[u8; LazyField::NUM_VARIANTS]> {
36273709
debug_assert!(lazy.len() < u8::MAX as usize);
3710+
// Fast path: fresh task with no residue at all.
3711+
if lazy.is_empty() {
3712+
return None;
3713+
}
36283714
let mut index = [u8::MAX; LazyField::NUM_VARIANTS];
3715+
let mut any_persistent = false;
36293716
for (i, f) in lazy.iter().enumerate() {
3630-
index[f.discriminant_index() as usize] = i as u8;
3717+
let (d, is_persistent) = f.index_and_persistence();
3718+
index[d as usize] = i as u8;
3719+
any_persistent |= is_persistent;
36313720
}
3632-
index
3721+
any_persistent.then_some(index)
36333722
}
36343723

36353724
/// Merge a single persistent `LazyField` from a decoded snapshot into
@@ -3656,9 +3745,8 @@ fn generate_snapshot_restore_methods(grouped_fields: &GroupedFields) -> TokenStr
36563745

36573746
/// Generate a match arm for `restore_lazy_field` that merges an incoming
36583747
/// persistent `filter_transient` variant into `self.lazy` using the precomputed
3659-
/// discriminant → position `index`. `discriminant` must equal
3660-
/// `all_lazy().position(...)` for this variant — i.e. its
3661-
/// `discriminant_index()` value.
3748+
/// discriminant → position `index`. `discriminant` must equal the variant's
3749+
/// position in `all_lazy()` (and in the `LazyField` enum definition).
36623750
///
36633751
/// On residue hit: merge the incoming collection into the existing one.
36643752
/// On miss: push the variant. `source.lazy` never contains duplicate variants,

0 commit comments

Comments
 (0)