Repository: vercel/next.js
Pull request: perf(turbopack): Introduce runtime analysis for immutable tasks

### What?

After the execution of a turbo task function, we can mark it as immutable if it does not have a way to invalidate itself. With the immutable flag, we optimize some overhead of turbo tasks by skipping activeness tracking.

### Why?

### How?



Closes PACK-4844

Changed files:
filename: turbopack/crates/turbo-tasks-backend/src/backend/mod.rs
patch: @@ -1671,6 +1671,12 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
 
         let has_children = !new_children.is_empty();
 
+        // If the task is not stateful and has no children, it does not have a way to be invalidated
+        // and we can mark it as immutable.
+        if !stateful && !has_children {
+            task.mark_as_immutable();
+        }
+
         // Prepare all new children
         if has_children {
             prepare_new_children(task_id, &mut task, &new_children, &mut queue);
@@ -1787,15 +1793,17 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
         let mut queue = AggregationUpdateQueue::new();
 
         if has_children {
-            let has_active_count = ctx.should_track_activeness()
+            let is_immutable = task.is_immutable();
+            let has_active_count = !is_immutable
+                && ctx.should_track_activeness()
                 && get!(task, Activeness).map_or(false, |activeness| activeness.active_counter > 0);
             connect_children(
                 task_id,
                 &mut task,
                 new_children,
                 &mut queue,
                 has_active_count,
-                ctx.should_track_activeness(),
+                !is_immutable && ctx.should_track_activeness(),
             );
         }
 
@@ -2281,7 +2289,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
                     effective: u32::MAX,
                 },
             });
-            if self.should_track_activeness() {
+            if !task.state().is_immutable() && self.should_track_activeness() {
                 task.add(CachedDataItem::Activeness {
                     value: ActivenessState::new_root(root_type, task_id),
                 });

filename: turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_child.rs
patch: @@ -31,6 +31,9 @@ impl ConnectChildOperation {
     ) {
         if !ctx.should_track_children() {
             let mut task = ctx.task(child_task_id, TaskDataCategory::All);
+            if is_immutable {
+                task.mark_as_immutable();
+            }
             if !task.has_key(&CachedDataItemKey::Output {}) {
                 let description = ctx.get_task_desc_fn(child_task_id);
                 let should_schedule = task.add(CachedDataItem::new_scheduled(description));
@@ -78,6 +81,10 @@ impl ConnectChildOperation {
             });
         } else {
             let mut task = ctx.task(child_task_id, TaskDataCategory::All);
+            if is_immutable {
+                task.mark_as_immutable();
+            }
+
             if !task.has_key(&CachedDataItemKey::Output {}) {
                 let description = ctx.get_task_desc_fn(child_task_id);
                 let should_schedule = task.add(CachedDataItem::new_scheduled(description));

filename: turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs
patch: @@ -406,6 +406,8 @@ pub trait TaskGuard: Debug {
     where
         F: for<'a> FnMut(CachedDataItemKey, CachedDataItemValueRef<'a>) -> bool + 'l;
     fn invalidate_serialization(&mut self);
+    fn is_immutable(&self) -> bool;
+    fn mark_as_immutable(&mut self);
 }
 
 struct TaskGuardImpl<'a, B: BackingStorage> {
@@ -593,6 +595,13 @@ impl<B: BackingStorage> TaskGuard for TaskGuardImpl<'_, B> {
             self.task.track_modification(SpecificTaskDataCategory::Meta);
         }
     }
+
+    fn is_immutable(&self) -> bool {
+        self.task.state().is_immutable()
+    }
+    fn mark_as_immutable(&mut self) {
+        self.task.state_mut().set_is_immutable(true);
+    }
 }
 
 macro_rules! impl_operation {

filename: turbopack/crates/turbo-tasks-backend/src/backend/storage.rs
patch: @@ -98,6 +98,7 @@ bitfield! {
     /// Item was modified after snapshot mode was entered. A snapshot was taken.
     pub meta_snapshot, set_meta_snapshot: 4;
     pub data_snapshot, set_data_snapshot: 5;
+    pub is_immutable, set_is_immutable: 6;
 }
 
 impl InnerStorageState {