Skip to content

Commit c6775ae

Browse files
authored
perf(turbopack): Introduce static analysis for immutable tasks (vercel#80415)
### What? Implement a basic form of Immutable turbo tasks. At this stage, we only detect statically analyzble turbo tasks, and handle them specially. We only skip activeness tracking with this PR.
1 parent d76f0b1 commit c6775ae

File tree

9 files changed

+47
-12
lines changed

9 files changed

+47
-12
lines changed

turbopack/crates/turbo-tasks-backend/src/backend/mod.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,10 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
407407
tx: Option<&'l B::ReadTransaction<'tx>>,
408408
parent_task: TaskId,
409409
child_task: TaskId,
410+
is_immutable: bool,
410411
turbo_tasks: &'l dyn TurboTasksBackendApi<TurboTasksBackend<B>>,
411412
) {
412-
operation::ConnectChildOperation::run(parent_task, child_task, unsafe {
413+
operation::ConnectChildOperation::run(parent_task, child_task, is_immutable, unsafe {
413414
self.execute_context_with_tx(tx, turbo_tasks)
414415
});
415416
}
@@ -418,11 +419,13 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
418419
&self,
419420
parent_task: TaskId,
420421
child_task: TaskId,
422+
is_immutable: bool,
421423
turbo_tasks: &dyn TurboTasksBackendApi<TurboTasksBackend<B>>,
422424
) {
423425
operation::ConnectChildOperation::run(
424426
parent_task,
425427
child_task,
428+
is_immutable,
426429
self.execute_context(turbo_tasks),
427430
);
428431
}
@@ -1135,11 +1138,12 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
11351138
&self,
11361139
task_type: CachedTaskType,
11371140
parent_task: TaskId,
1141+
is_immutable: bool,
11381142
turbo_tasks: &dyn TurboTasksBackendApi<TurboTasksBackend<B>>,
11391143
) -> TaskId {
11401144
if let Some(task_id) = self.task_cache.lookup_forward(&task_type) {
11411145
self.track_cache_hit(&task_type);
1142-
self.connect_child(parent_task, task_id, turbo_tasks);
1146+
self.connect_child(parent_task, task_id, is_immutable, turbo_tasks);
11431147
return task_id;
11441148
}
11451149

@@ -1179,7 +1183,9 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
11791183
};
11801184

11811185
// Safety: `tx` is a valid transaction from `self.backend.backing_storage`.
1182-
unsafe { self.connect_child_with_tx(tx.as_ref(), parent_task, task_id, turbo_tasks) };
1186+
unsafe {
1187+
self.connect_child_with_tx(tx.as_ref(), parent_task, task_id, is_immutable, turbo_tasks)
1188+
};
11831189

11841190
task_id
11851191
}
@@ -1188,6 +1194,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
11881194
&self,
11891195
task_type: CachedTaskType,
11901196
parent_task: TaskId,
1197+
is_immutable: bool,
11911198
turbo_tasks: &dyn TurboTasksBackendApi<TurboTasksBackend<B>>,
11921199
) -> TaskId {
11931200
if !parent_task.is_transient() {
@@ -1199,7 +1206,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
11991206
}
12001207
if let Some(task_id) = self.task_cache.lookup_forward(&task_type) {
12011208
self.track_cache_hit(&task_type);
1202-
self.connect_child(parent_task, task_id, turbo_tasks);
1209+
self.connect_child(parent_task, task_id, is_immutable, turbo_tasks);
12031210
return task_id;
12041211
}
12051212

@@ -1211,11 +1218,11 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
12111218
unsafe {
12121219
self.transient_task_id_factory.reuse(task_id);
12131220
}
1214-
self.connect_child(parent_task, existing_task_id, turbo_tasks);
1221+
self.connect_child(parent_task, existing_task_id, is_immutable, turbo_tasks);
12151222
return existing_task_id;
12161223
}
12171224

1218-
self.connect_child(parent_task, task_id, turbo_tasks);
1225+
self.connect_child(parent_task, task_id, is_immutable, turbo_tasks);
12191226

12201227
task_id
12211228
}
@@ -2249,7 +2256,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
22492256
turbo_tasks: &dyn TurboTasksBackendApi<TurboTasksBackend<B>>,
22502257
) {
22512258
self.assert_not_persistent_calling_transient(parent_task, task, None);
2252-
ConnectChildOperation::run(parent_task, task, self.execute_context(turbo_tasks));
2259+
ConnectChildOperation::run(parent_task, task, false, self.execute_context(turbo_tasks));
22532260
}
22542261

22552262
fn create_transient_task(&self, task_type: TransientTaskType) -> TaskId {
@@ -2584,20 +2591,22 @@ impl<B: BackingStorage> Backend for TurboTasksBackend<B> {
25842591
&self,
25852592
task_type: CachedTaskType,
25862593
parent_task: TaskId,
2594+
is_immutable: bool,
25872595
turbo_tasks: &dyn TurboTasksBackendApi<Self>,
25882596
) -> TaskId {
25892597
self.0
2590-
.get_or_create_persistent_task(task_type, parent_task, turbo_tasks)
2598+
.get_or_create_persistent_task(task_type, parent_task, is_immutable, turbo_tasks)
25912599
}
25922600

25932601
fn get_or_create_transient_task(
25942602
&self,
25952603
task_type: CachedTaskType,
25962604
parent_task: TaskId,
2605+
is_immutable: bool,
25972606
turbo_tasks: &dyn TurboTasksBackendApi<Self>,
25982607
) -> TaskId {
25992608
self.0
2600-
.get_or_create_transient_task(task_type, parent_task, turbo_tasks)
2609+
.get_or_create_transient_task(task_type, parent_task, is_immutable, turbo_tasks)
26012610
}
26022611

26032612
fn invalidate_task(&self, task_id: TaskId, turbo_tasks: &dyn TurboTasksBackendApi<Self>) {

turbopack/crates/turbo-tasks-backend/src/backend/operation/connect_child.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ pub enum ConnectChildOperation {
2323
}
2424

2525
impl ConnectChildOperation {
26-
pub fn run(parent_task_id: TaskId, child_task_id: TaskId, mut ctx: impl ExecuteContext) {
26+
pub fn run(
27+
parent_task_id: TaskId,
28+
child_task_id: TaskId,
29+
is_immutable: bool,
30+
mut ctx: impl ExecuteContext,
31+
) {
2732
if !ctx.should_track_children() {
2833
let mut task = ctx.task(child_task_id, TaskDataCategory::All);
2934
if !task.has_key(&CachedDataItemKey::Output {}) {
@@ -66,7 +71,8 @@ impl ConnectChildOperation {
6671
});
6772
}
6873

69-
if ctx.should_track_activeness() {
74+
// Immutable tasks cannot be invalidated, meaning that we never reschedule them.
75+
if !is_immutable && ctx.should_track_activeness() {
7076
queue.push(AggregationUpdateJob::IncreaseActiveCount {
7177
task: child_task_id,
7278
});

turbopack/crates/turbo-tasks-macros/src/func.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,7 @@ pub struct NativeFn {
10991099
pub filter_trait_call_args: Option<FilterTraitCallArgsTokens>,
11001100
pub local: bool,
11011101
pub invalidator: bool,
1102+
pub immutable: bool,
11021103
}
11031104

11041105
impl NativeFn {
@@ -1115,6 +1116,7 @@ impl NativeFn {
11151116
filter_trait_call_args,
11161117
local,
11171118
invalidator,
1119+
immutable,
11181120
} = self;
11191121

11201122
if *is_method {
@@ -1142,6 +1144,7 @@ impl NativeFn {
11421144
turbo_tasks::macro_helpers::FunctionMeta {
11431145
local: #local,
11441146
invalidator: #invalidator,
1147+
immutable: #immutable,
11451148
},
11461149
#arg_filter,
11471150
#function_path,
@@ -1157,6 +1160,7 @@ impl NativeFn {
11571160
turbo_tasks::macro_helpers::FunctionMeta {
11581161
local: #local,
11591162
invalidator: #invalidator,
1163+
immutable: #immutable,
11601164
},
11611165
#arg_filter,
11621166
#function_path,
@@ -1173,6 +1177,7 @@ impl NativeFn {
11731177
turbo_tasks::macro_helpers::FunctionMeta {
11741178
local: #local,
11751179
invalidator: #invalidator,
1180+
immutable: #immutable,
11761181
},
11771182
#function_path,
11781183
)

turbopack/crates/turbo-tasks-macros/src/function_macro.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub fn function(args: TokenStream, input: TokenStream) -> TokenStream {
6767
filter_trait_call_args: None, // not a trait method
6868
local,
6969
invalidator,
70+
immutable: sig.asyncness.is_none() && !invalidator,
7071
};
7172
let native_function_ident = get_native_function_ident(ident);
7273
let native_function_ty = native_fn.ty();

turbopack/crates/turbo-tasks-macros/src/value_impl_macro.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream {
119119
filter_trait_call_args: None, // not a trait method
120120
local,
121121
invalidator,
122+
immutable: sig.asyncness.is_none() && !invalidator,
122123
};
123124

124125
let native_function_ident = get_inherent_impl_function_ident(ty_ident, ident);
@@ -248,6 +249,7 @@ pub fn value_impl(args: TokenStream, input: TokenStream) -> TokenStream {
248249
filter_trait_call_args: turbo_fn.filter_trait_call_args(),
249250
local,
250251
invalidator,
252+
immutable: sig.asyncness.is_none() && !invalidator,
251253
};
252254

253255
let native_function_ident =

turbopack/crates/turbo-tasks-macros/src/value_trait_macro.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ pub fn value_trait(args: TokenStream, input: TokenStream) -> TokenStream {
194194
// - This only makes sense when a default implementation is present.
195195
local: false,
196196
invalidator: func_args.invalidator.is_some(),
197+
immutable: sig.asyncness.is_none() && func_args.invalidator.is_none(),
197198
};
198199

199200
let native_function_ident = get_trait_default_impl_function_ident(trait_ident, ident);

turbopack/crates/turbo-tasks/src/backend.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,13 +667,15 @@ pub trait Backend: Sync + Send {
667667
&self,
668668
task_type: CachedTaskType,
669669
parent_task: TaskId,
670+
is_immutable: bool,
670671
turbo_tasks: &dyn TurboTasksBackendApi<Self>,
671672
) -> TaskId;
672673

673674
fn get_or_create_transient_task(
674675
&self,
675676
task_type: CachedTaskType,
676677
parent_task: TaskId,
678+
is_immutable: bool,
677679
turbo_tasks: &dyn TurboTasksBackendApi<Self>,
678680
) -> TaskId;
679681

turbopack/crates/turbo-tasks/src/manager.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,18 +603,24 @@ impl<B: Backend + 'static> TurboTasks<B> {
603603
self.schedule_local_task(task_type, persistence)
604604
}
605605
TaskPersistence::Transient => {
606+
let immutable = registry::get_function(fn_type).function_meta.immutable;
606607
let task_type = CachedTaskType { fn_type, this, arg };
608+
607609
RawVc::TaskOutput(self.backend.get_or_create_transient_task(
608610
task_type,
609611
current_task("turbo_function calls"),
612+
immutable,
610613
self,
611614
))
612615
}
613616
TaskPersistence::Persistent => {
617+
let immutable = registry::get_function(fn_type).function_meta.immutable;
614618
let task_type = CachedTaskType { fn_type, this, arg };
619+
615620
RawVc::TaskOutput(self.backend.get_or_create_persistent_task(
616621
task_type,
617622
current_task("turbo_function calls"),
623+
immutable,
618624
self,
619625
))
620626
}

turbopack/crates/turbo-tasks/src/native_function.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,12 @@ pub struct FunctionMeta {
145145
/// the parent task.
146146
pub local: bool,
147147

148-
/// If true, the function will be allowed to call `get_invalidator` . If this is false, the
148+
/// If true, the function will be allowed to call `get_invalidator`. If this is false, the
149149
/// `get_invalidator` function will panic on calls.
150150
pub invalidator: bool,
151+
152+
/// If true, the function is statically analyzable immutable.
153+
pub immutable: bool,
151154
}
152155

153156
/// A native (rust) turbo-tasks function. It's used internally by

0 commit comments

Comments
 (0)