mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
This has been causing a lot of confusion when contributors have been debugging tree shaking, eg. https://github.com/vercel/next.js/issues/95698 and the linked issues. Turbopack's standard tree-shaking features are controlled through the `turbopackRemoveUnusedExports` and `turbopackRemoveUnusedImports` options which default to true.
166 lines
5.5 KiB
Rust
166 lines
5.5 KiB
Rust
use std::{path::PathBuf, time::Duration};
|
|
|
|
use anyhow::Result;
|
|
use criterion::{BatchSize, Bencher, BenchmarkId, Criterion, criterion_group, criterion_main};
|
|
use turbo_rcstr::rcstr;
|
|
use turbo_tasks::{
|
|
ResolvedVc, TurboTasks, Vc, unmark_top_level_task_may_leak_eventually_consistent_state,
|
|
};
|
|
use turbo_tasks_backend::{BackendOptions, TurboTasksBackend, noop_backing_storage};
|
|
use turbo_tasks_fs::{DiskFileSystem, FileSystem};
|
|
use turbopack_core::{
|
|
compile_time_info::CompileTimeInfo,
|
|
environment::{Environment, ExecutionEnvironment, NodeJsEnvironment},
|
|
file_source::FileSource,
|
|
ident::Layer,
|
|
};
|
|
use turbopack_ecmascript::{
|
|
AnalyzeMode, EcmascriptInputTransforms, EcmascriptModuleAsset, EcmascriptOptions,
|
|
references::analyze_ecmascript_module,
|
|
};
|
|
use turbopack_test_utils::noop_asset_context::NoopAssetContext;
|
|
|
|
#[global_allocator]
|
|
static ALLOC: turbo_tasks_malloc::TurboMalloc = turbo_tasks_malloc::TurboMalloc;
|
|
|
|
pub fn benchmark(c: &mut Criterion) {
|
|
let root_dir = String::leak(
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.join("tests/benches/")
|
|
.to_str()
|
|
.unwrap()
|
|
.to_string(),
|
|
);
|
|
|
|
let mut group = c.benchmark_group("references");
|
|
group.warm_up_time(Duration::from_secs(1));
|
|
group.measurement_time(Duration::from_secs(10));
|
|
|
|
for (file, trace_only) in [
|
|
(r#"packages-bundle.js"#, false),
|
|
(r#"packages-bundle.js"#, true),
|
|
(r#"app-page-turbo.runtime.prod.js"#, false),
|
|
(r#"app-page-turbo.runtime.prod.js"#, true),
|
|
(r#"react-dom-client.development.js"#, false),
|
|
(r#"react-dom-client.development.js"#, true),
|
|
(r#"jsonwebtoken.js"#, false),
|
|
(r#"jsonwebtoken.js"#, true),
|
|
] {
|
|
group.bench_with_input(
|
|
BenchmarkId::new(file, if trace_only { "tracing" } else { "full" }),
|
|
&BenchInput {
|
|
root_dir,
|
|
file,
|
|
analyze_mode: if trace_only {
|
|
AnalyzeMode::Tracing
|
|
} else {
|
|
AnalyzeMode::CodeGenerationAndTracing
|
|
},
|
|
},
|
|
bench_full,
|
|
);
|
|
}
|
|
}
|
|
|
|
async fn setup(
|
|
root_dir: &str,
|
|
file: &str,
|
|
analyze_mode: AnalyzeMode,
|
|
) -> Result<ResolvedVc<EcmascriptModuleAsset>> {
|
|
let fs = DiskFileSystem::new(rcstr!("project"), Vc::cell(root_dir.into()));
|
|
|
|
let environment = Environment::new(ExecutionEnvironment::NodeJsLambda(
|
|
NodeJsEnvironment::default().resolved_cell(),
|
|
));
|
|
let compile_time_info = CompileTimeInfo::new(environment).to_resolved().await?;
|
|
let layer = Layer::new(rcstr!("test"));
|
|
let module_asset_context = NoopAssetContext {
|
|
compile_time_info,
|
|
layer,
|
|
}
|
|
.resolved_cell();
|
|
|
|
let module = EcmascriptModuleAsset::builder(
|
|
ResolvedVc::upcast(
|
|
FileSource::new(fs.root().await?.join(file).unwrap())
|
|
.to_resolved()
|
|
.await?,
|
|
),
|
|
ResolvedVc::upcast(module_asset_context),
|
|
EcmascriptInputTransforms::empty().to_resolved().await?,
|
|
EcmascriptOptions {
|
|
follow_reexports: analyze_mode != AnalyzeMode::Tracing,
|
|
module_fragments_enabled: false,
|
|
analyze_mode,
|
|
..Default::default()
|
|
}
|
|
.resolved_cell(),
|
|
compile_time_info,
|
|
None,
|
|
)
|
|
.build()
|
|
.to_resolved()
|
|
.await?;
|
|
Ok(module)
|
|
}
|
|
|
|
struct BenchInput {
|
|
root_dir: &'static str,
|
|
file: &'static str,
|
|
analyze_mode: AnalyzeMode,
|
|
}
|
|
|
|
fn bench_full(b: &mut Bencher, input: &BenchInput) {
|
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
|
.worker_threads(2)
|
|
.enable_all()
|
|
.build()
|
|
.unwrap();
|
|
|
|
b.to_async(rt).iter_batched(
|
|
|| {
|
|
let tt = TurboTasks::new(TurboTasksBackend::new(
|
|
BackendOptions {
|
|
dependency_tracking: false,
|
|
storage_mode: None,
|
|
..Default::default()
|
|
},
|
|
noop_backing_storage(),
|
|
));
|
|
let BenchInput {
|
|
root_dir,
|
|
file,
|
|
analyze_mode,
|
|
} = *input;
|
|
let module = tokio::task::block_in_place(|| {
|
|
tokio::runtime::Handle::current()
|
|
.block_on(tt.run_once(async move {
|
|
// See note below: benchmarks may read eventually-consistent state at the
|
|
// top level, which is fine here but trips the debug assertion otherwise.
|
|
unmark_top_level_task_may_leak_eventually_consistent_state();
|
|
let module = setup(root_dir, file, analyze_mode).await?;
|
|
Ok(module)
|
|
}))
|
|
.unwrap()
|
|
});
|
|
(tt, module)
|
|
},
|
|
|(tt, module)| async move {
|
|
tt.run_once(async move {
|
|
// `analyze_ecmascript_module` performs eventually-consistent Vc reads. Reading
|
|
// not-yet-settled state at the top level is fine for a throughput benchmark, but
|
|
// trips the top-level-task assertion under debug-assertions otherwise.
|
|
unmark_top_level_task_may_leak_eventually_consistent_state();
|
|
analyze_ecmascript_module(*module, None).await?;
|
|
Ok(())
|
|
})
|
|
.await
|
|
.unwrap()
|
|
},
|
|
BatchSize::SmallInput,
|
|
);
|
|
}
|
|
|
|
criterion_group!(references_benches, benchmark);
|
|
criterion_main!(references_benches);
|