Skip to content

perf(core): reduce snapshot deserialization overhead#33321

Open
bartlomieju wants to merge 2 commits intomainfrom
perf/snapshot-startup
Open

perf(core): reduce snapshot deserialization overhead#33321
bartlomieju wants to merge 2 commits intomainfrom
perf/snapshot-startup

Conversation

@bartlomieju
Copy link
Copy Markdown
Member

Summary

Reduces V8 snapshot sidecar deserialization overhead at startup:

  • Eliminate ~2500 Url::parse calls: Module requests (ModuleRequest containing ModuleSpecifier / url::Url) were serialized into the snapshot but never used at runtime — snapshotted modules are already instantiated and linked. Now cleared before serialization.

  • Zero-copy string deserialization: Introduced snapshot-specific types (ModuleInfoSnapshot, SymbolicModuleSnapshot) that use Cow<'a, str> instead of FastString. Bincode borrows strings directly from the &'static snapshot buffer. cow_to_fast_string converts Cow::Borrowed to FastString::Static via transmute (safe because snapshot buffers are always Box::leak'd to 'static). Eliminates ~560 heap allocations.

  • Skip redundant ASCII validation: Uses create_external_onebyte_const_unchecked in externalize_sources for snapshot and extension sources that were already validated during snapshot creation / transpilation.

Context

Profiling showed Url::parse consuming ~12% of sidecar deserialization time during startup. The sidecar is 18KB vs 815KB for the V8 snapshot itself, but every byte of it was being actively processed (parsing URLs, allocating strings). These changes make sidecar deserialization nearly zero-cost.

Test plan

  • deno run, deno eval work correctly
  • Node.js compat (node:path, Buffer, process) works
  • cargo test -p deno_core --lib passes (402 passed, 1 pre-existing SIGSEGV in es_snapshot test unrelated to this change)

🤖 Generated with Claude Code

bartlomieju and others added 2 commits April 18, 2026 14:34
1. Clear module requests before snapshotting — the requests contain
   ModuleReference with parsed URLs (ModuleSpecifier) that trigger
   ~2500 Url::parse calls during deserialization. Snapshotted modules
   are already instantiated and linked, so their import requests are
   never needed at runtime.

2. Use create_external_onebyte_const_unchecked for snapshot and
   extension sources — these were already validated as ASCII during
   snapshot creation / transpilation, so the per-source ASCII check
   is redundant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace FastString/ModuleInfo/SymbolicModule in the snapshot data with
snapshot-specific types that use Cow<'a, str> for string fields. This
allows bincode to borrow strings directly from the snapshot buffer
during deserialization instead of heap-allocating copies via
FastString::Deserialize.

Also omits ModuleInfo.requests from the snapshot form entirely (moved
from the previous commit's clear-after-serialize to not serializing
them at all), eliminating the ~2500 Url::parse calls and their
associated ModuleRequest/ModuleReference allocations from the
serialized data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@nathanwhit
Copy link
Copy Markdown
Member

nathanwhit commented Apr 20, 2026

Does this actually improve startup time?

Also this segfault

cargo test -p deno_core --lib passes (402 passed, 1 pre-existing SIGSEGV in es_snapshot test unrelated to this change)

is because you need to use cargo nextest for running the deno core tests.

@bartlomieju
Copy link
Copy Markdown
Member Author

Does this actually improve startup time?

TBH I'm not sure anymore; the first few times I captured flamegraphs I saw quite a few percent spent in Url::parse and other op setup stuff but I can't reproduce it anymore. We can just close this one for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants