Skip to content

Commit 438606c

Browse files
authored
[turbopack] Remove the turbo_tasks::function attributes from a number of tasks that do not get cache hits. (#83241)
Drop `turbo_tasks::function` annotation from functions that never get cache hits. We only do this in cases where: * there are no cache hits in production or dev builds * There is a structural reason for it. Typically, because that task is 'dominated' by another one. * Or, the task being cached is so small that we shouldn't worry about even theoretical hits. Tasks being modified or removed (numbers are from a vercel-site build): * `turbopack-ecmascript::parse::parse` (63398 tasks) - this task gets no hits due to a few 'dominating' tasks. In this case we remove turbotasks::function from `EcmascriptModuleAsset::parse`, since there are some theoretical hits. * `EcmascriptChunkItemContent::module_factory` (55804 tasks) - This function is completely 'dominated' by `module_factory_with_code_generation_issue` so we don't need it, even in a PC environment we should expect this to be the case * `Module::style_type` (63237 tasks, though we don't save all of them) * Only gets called once for each module so there were no cache hits. However, it is useful for HMR and PC to ensure we don't recompute style groups. So move this to a new trait and call it conditionally which eliminates all overhead while preseving cacheability * `track_glob` (11432 tasks) * this is used to set up dependencies on files read by webpack loaders. This was made conditional on whether or not any dependencies are being tracked. at all * `apply_module_type` (152887 tasks) and `apply_reexport_treeshaking` (57527 tasks): - both of these are dominated by other tasks * `EcmascriptAnalyzable::module_content` (40310 tasks) - This one is trivially dominated and really just plugs two other Vcs together, so i dropped `turbo_tasks::function`, as default trait method this actually required a new feature in the macro gencode. So altogether this eliminates about 4.97% (444595/8939581) tasks from a build of vercel-site. From measuring the builds I also see a progression ![image.png](https://app.graphite.dev/user-attachments/assets/f7d960e1-30d6-4430-8712-ae94723adaae.png) Which is (@p50) 1.7 seconds faster (-2.6%) and uses 290 MB less memory (-2%). The micro benchmarks show bigger progressions, but that makes sense, they are more sensitive to overheads.
1 parent d054cac commit 438606c

File tree

20 files changed

+150
-138
lines changed

20 files changed

+150
-138
lines changed

crates/napi/src/next_api/project.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,7 @@ pub fn project_new(
438438
)?;
439439
let turbopack_ctx = NextTurbopackContext::new(turbo_tasks.clone(), napi_callbacks);
440440

441-
let stats_path = std::env::var_os("NEXT_TURBOPACK_TASK_STATISTICS");
442-
if let Some(stats_path) = stats_path {
441+
if let Some(stats_path) = std::env::var_os("NEXT_TURBOPACK_TASK_STATISTICS") {
443442
let task_stats = turbo_tasks.task_statistics().enable().clone();
444443
exit.on_exit(async move {
445444
tokio::task::spawn_blocking(move || {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3077,6 +3077,10 @@ impl<B: BackingStorage> Backend for TurboTasksBackend<B> {
30773077
fn task_statistics(&self) -> &TaskStatisticsApi {
30783078
&self.0.task_statistics
30793079
}
3080+
3081+
fn is_tracking_dependencies(&self) -> bool {
3082+
self.0.options.dependency_tracking
3083+
}
30803084
}
30813085

30823086
enum DebugTraceTransientTask {

turbopack/crates/turbo-tasks-testing/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ impl TurboTasksApi for VcStorage {
324324
fn send_compilation_event(&self, _event: Arc<dyn CompilationEvent>) {
325325
unimplemented!()
326326
}
327+
328+
fn is_tracking_dependencies(&self) -> bool {
329+
false
330+
}
327331
}
328332

329333
impl VcStorage {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,4 +726,6 @@ pub trait Backend: Sync + Send {
726726
fn dispose_root_task(&self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi<Self>);
727727

728728
fn task_statistics(&self) -> &TaskStatisticsApi;
729+
730+
fn is_tracking_dependencies(&self) -> bool;
729731
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ pub trait TurboTasksApi: TurboTasksCallApi + Sync + Send {
202202
event_types: Option<Vec<String>>,
203203
) -> Receiver<Arc<dyn CompilationEvent>>;
204204
fn send_compilation_event(&self, event: Arc<dyn CompilationEvent>);
205+
206+
// Returns true if TurboTasks is configured to track dependencies.
207+
fn is_tracking_dependencies(&self) -> bool;
205208
}
206209

207210
/// A wrapper around a value that is unused.
@@ -1442,6 +1445,10 @@ impl<B: Backend + 'static> TurboTasksApi for TurboTasks<B> {
14421445
tracing::warn!("Failed to send compilation event: {e}");
14431446
}
14441447
}
1448+
1449+
fn is_tracking_dependencies(&self) -> bool {
1450+
self.backend.is_tracking_dependencies()
1451+
}
14451452
}
14461453

14471454
impl<B: Backend + 'static> TurboTasksBackendApi<B> for TurboTasks<B> {

turbopack/crates/turbo-tasks/src/vc/resolved.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ where
243243
/// Returns `None` if the underlying value type does not implement `K`.
244244
///
245245
/// **Note:** if the trait `T` is required to implement `K`, use [`ResolvedVc::upcast`] instead.
246-
/// This provides stronger guarantees, removing the need for a [`Result`] return type.
246+
/// This provides stronger guarantees, removing the need for a [`Option`] return type.
247247
///
248248
/// See also: [`Vc::try_resolve_sidecast`].
249249
pub fn try_sidecast<K>(this: Self) -> Option<ResolvedVc<K>>

turbopack/crates/turbopack-core/src/module.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
use serde::{Deserialize, Serialize};
2-
use turbo_tasks::{NonLocalValue, ResolvedVc, Vc, trace::TraceRawVcs};
1+
use turbo_tasks::{ResolvedVc, TaskInput, Vc};
32

43
use crate::{asset::Asset, ident::AssetIdent, reference::ModuleReferences};
54

6-
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, TraceRawVcs, NonLocalValue)]
5+
#[derive(Clone, Copy, Debug, TaskInput, Hash)]
6+
#[turbo_tasks::value(shared)]
77
pub enum StyleType {
88
IsolatedStyle,
99
GlobalStyle,
1010
}
1111

12-
#[turbo_tasks::value(transparent)]
13-
pub struct OptionStyleType(Option<StyleType>);
14-
1512
/// A module. This usually represents parsed source code, which has references
1613
/// to other modules.
1714
#[turbo_tasks::value_trait]
@@ -33,12 +30,13 @@ pub trait Module: Asset {
3330
fn is_self_async(self: Vc<Self>) -> Vc<bool> {
3431
Vc::cell(false)
3532
}
33+
}
3634

35+
#[turbo_tasks::value_trait]
36+
pub trait StyleModule: Module + Asset {
3737
/// The style type of the module.
3838
#[turbo_tasks::function]
39-
fn style_type(self: Vc<Self>) -> Vc<OptionStyleType> {
40-
Vc::cell(None)
41-
}
39+
fn style_type(&self) -> Vc<StyleType>;
4240
}
4341

4442
#[turbo_tasks::value(transparent)]

turbopack/crates/turbopack-core/src/module_graph/style_groups.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{
1515
ChunkItem, ChunkItemBatchWithAsyncModuleInfo, ChunkItemWithAsyncModuleInfo, ChunkType,
1616
ChunkableModule, ChunkingContext, chunk_item_batch::attach_async_info_to_chunkable_module,
1717
},
18-
module::{Module, StyleType},
18+
module::{Module, StyleModule, StyleType},
1919
module_graph::{
2020
GraphTraversalAction, ModuleGraph, module_batch::ModuleOrBatch,
2121
module_batches::ModuleBatchesGraphEdge,
@@ -129,8 +129,10 @@ pub async fn compute_style_groups(
129129
}
130130
}
131131
Entry::Vacant(e) => {
132-
let style_type = *module.style_type().await?;
133-
if let Some(style_type) = style_type {
132+
if let Some(style_module) =
133+
ResolvedVc::try_sidecast::<Box<dyn StyleModule>>(module)
134+
{
135+
let style_type = *style_module.style_type().await?;
134136
let mut info =
135137
ModuleInfo::new(style_type, module.ident().to_string().owned().await?);
136138
info.chunk_group_indices.insert(idx, styles.len());

turbopack/crates/turbopack-css/src/asset.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use turbopack_core::{
88
context::AssetContext,
99
environment::Environment,
1010
ident::AssetIdent,
11-
module::{Module, OptionStyleType, StyleType},
11+
module::{Module, StyleModule, StyleType},
1212
module_graph::ModuleGraph,
1313
output::OutputAssets,
1414
reference::{ModuleReference, ModuleReferences},
@@ -148,14 +148,16 @@ impl Module for CssModuleAsset {
148148
ParseCssResult::NotFound => Ok(ModuleReferences::empty()),
149149
}
150150
}
151+
}
151152

153+
#[turbo_tasks::value_impl]
154+
impl StyleModule for CssModuleAsset {
152155
#[turbo_tasks::function]
153-
fn style_type(&self) -> Vc<OptionStyleType> {
154-
let style_type = match self.ty {
155-
CssModuleAssetType::Default => StyleType::GlobalStyle,
156-
CssModuleAssetType::Module => StyleType::IsolatedStyle,
157-
};
158-
Vc::cell(Some(style_type))
156+
fn style_type(&self) -> Vc<StyleType> {
157+
match self.ty {
158+
CssModuleAssetType::Default => StyleType::GlobalStyle.cell(),
159+
CssModuleAssetType::Module => StyleType::IsolatedStyle.cell(),
160+
}
159161
}
160162
}
161163

turbopack/crates/turbopack-ecmascript/src/chunk/item.rs

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ impl EcmascriptChunkItemContent {
8585
}
8686
.cell())
8787
}
88+
}
8889

89-
#[turbo_tasks::function]
90-
pub async fn module_factory(&self) -> Result<Vc<Code>> {
90+
impl EcmascriptChunkItemContent {
91+
async fn module_factory(&self) -> Result<ResolvedVc<Code>> {
9192
let mut code = CodeBuilder::default();
9293
for additional_id in self.additional_ids.iter().try_join().await? {
9394
writeln!(code, "{}, ", StringifyJs(&*additional_id))?;
@@ -130,7 +131,7 @@ impl EcmascriptChunkItemContent {
130131

131132
code += "})";
132133

133-
Ok(code.build().cell())
134+
Ok(code.build().resolved_cell())
134135
}
135136
}
136137

@@ -221,37 +222,37 @@ async fn module_factory_with_code_generation_issue(
221222
chunk_item: Vc<Box<dyn EcmascriptChunkItem>>,
222223
async_module_info: Option<Vc<AsyncModuleInfo>>,
223224
) -> Result<Vc<Code>> {
224-
Ok(
225-
match chunk_item
226-
.content_with_async_module_info(async_module_info)
227-
.module_factory()
228-
.resolve()
229-
.await
230-
{
231-
Ok(factory) => factory,
232-
Err(error) => {
233-
let id = chunk_item.asset_ident().to_string().await;
234-
let id = id.as_ref().map_or_else(|_| "unknown", |id| &**id);
235-
let error = error.context(format!(
236-
"An error occurred while generating the chunk item {id}"
237-
));
238-
let error_message = format!("{}", PrettyPrintError(&error)).into();
239-
let js_error_message = serde_json::to_string(&error_message)?;
240-
CodeGenerationIssue {
241-
severity: IssueSeverity::Error,
242-
path: chunk_item.asset_ident().path().owned().await?,
243-
title: StyledString::Text(rcstr!("Code generation for chunk item errored"))
244-
.resolved_cell(),
245-
message: StyledString::Text(error_message).resolved_cell(),
246-
}
247-
.resolved_cell()
248-
.emit();
249-
let mut code = CodeBuilder::default();
250-
code += "(() => {{\n\n";
251-
writeln!(code, "throw new Error({error});", error = &js_error_message)?;
252-
code += "\n}})";
253-
code.build().cell()
225+
let content = match chunk_item
226+
.content_with_async_module_info(async_module_info)
227+
.await
228+
{
229+
Ok(item) => item.module_factory().await,
230+
Err(err) => Err(err),
231+
};
232+
Ok(match content {
233+
Ok(factory) => *factory,
234+
Err(error) => {
235+
let id = chunk_item.asset_ident().to_string().await;
236+
let id = id.as_ref().map_or_else(|_| "unknown", |id| &**id);
237+
let error = error.context(format!(
238+
"An error occurred while generating the chunk item {id}"
239+
));
240+
let error_message = format!("{}", PrettyPrintError(&error)).into();
241+
let js_error_message = serde_json::to_string(&error_message)?;
242+
CodeGenerationIssue {
243+
severity: IssueSeverity::Error,
244+
path: chunk_item.asset_ident().path().owned().await?,
245+
title: StyledString::Text(rcstr!("Code generation for chunk item errored"))
246+
.resolved_cell(),
247+
message: StyledString::Text(error_message).resolved_cell(),
254248
}
255-
},
256-
)
249+
.resolved_cell()
250+
.emit();
251+
let mut code = CodeBuilder::default();
252+
code += "(() => {{\n\n";
253+
writeln!(code, "throw new Error({error});", error = &js_error_message)?;
254+
code += "\n}})";
255+
code.build().cell()
256+
}
257+
})
257258
}

0 commit comments

Comments
 (0)