-
Notifications
You must be signed in to change notification settings - Fork 82
Propose: Crate Slicing for Faster Fresh Builds (2026H1) #438
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Remove invalid 'Tracking issue | TBD' row - Convert Team asks table to 2026 format (Team/Support level/Notes)
nikomatsakis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really cool and very interesting to me.
src/2026h1/crate-slicing.md
Outdated
| sqlx = { version = "0.8", features = ["postgres", "runtime-tokio"] } | ||
| ``` | ||
|
|
||
| This dependency graph expands to 200+ crates. The `tokio` crate alone contains 315 module files totaling ~90,000 lines of Rust source. Yet a typical application uses perhaps 5-10% of tokio's public API surface (e.g., `tokio::spawn`, `TcpListener::bind`, `select!`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yet a typical application uses perhaps 5-10% of tokio's public API surface (e.g.,
tokio::spawn,TcpListener::bind,select!).
Do you have a citation for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As anecdata, last time I checked https://github.com/tokio-rs/mini-redis, such slicing would have been able to avoid compiling 871 out of 3326 functions in tokio at that time (only 1977 functions were actually codegened in the first place). A lot more is used than 5%.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tokio definitely has more API's used than that of regex crates. Currently there is still some errors in the cargo-slicer POC, I am working to address them and find out the exact percentage of needed APIs, for the current target (i.e., Linux x86_64 on my dev server).
src/2026h1/crate-slicing.md
Outdated
|
|
||
| **Technical approach:** | ||
|
|
||
| 1. **Usage extraction**: Parse project source using `syn` to identify: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syn is not ideally suited to this project, since it gives you only static type information. Have you considered using the rust-analyzer crates or possibly rustc itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good suggestion! Yes, i am currently trying to use the SCIP, a new feature in rust-analyzer, which first creates an index of the code navigation. It was created by sourcegraph but integrated into rust-analyzer already. The protobuf based schema seems to be quite complete for dependency graph computation. At the moment I am focusing on the correctness, once it addresses most of the correctness problem, I will then focus on efficiency.
// I realize that SCIP may not be as fast as rust-analyzer on the first encounter of the crate, which may defeat the purpose of fresh build speedup. Perhaps the original implementation of rust-analyzer could do what SCIP is doing more efficiently. As a proof-of-concept, stage 1 let me just work on SCIP.
| | Team | Support level | Notes | | ||
| |------|---------------|-------| | ||
| | compiler | Medium | Consultation on approach feasibility and soundness concerns | | ||
| | cargo | Medium | Discussion on cargo integration options | | ||
| | types | Small | Consultation on trait coherence requirements for slicing | | ||
| | rust-analyzer | Small | Discussion on rust-analyzer integration potential | | ||
| | lang | Small | Discussion on review of research methodology and findings | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. So, for your current deliverables -- it seems to me that you really just need "vibes"-level support. That is, this is something you are doing entirely independent of the compiler.
A good milestone though might be coming to Rust Week (cc @m-ou-se) to present this work at the All Hands, and that could indeed mean that you need Medium-level support, if this were something we wanted to integrate first-hand into the compiler itself upstream (which I personally think might be great).
I'm thinking that the best thing for you at the moment would be gathering data from the compiler/cargo team on: what information would they want to know to assess how viable it would be to do this upstream, and are there things that you could do in your architecture to make that easier?
Does that sound right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it might be a tad earlier to propose it already as a 26H1 goal :-) I hope by the time of Rust week, this solution prototype would be ready to discuss with the vibes of colleagues. It might be matured into a useful tool with experts in both teams. My intention is to avoid changing the current implementation of the Rust compiler if possible to achieve the goal. However, if that is not feasible, minimal changes to the compiler/language would be discussed. All suggestions are welcome !
src/2026h1/crate-slicing.md
Outdated
| [features] | ||
| http1 = [] | ||
| http2 = ["h2"] | ||
| full = ["http1", "http2", "websocket", ...] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| full = ["http1", "http2", "websocket", ...] | |
| full = ["http1", "http2", "websocket"] # ... and so forth |
|
I'm going to cc a few people... cc @celinval ...to get their "quick takes" on this. |
src/2026h1/crate-slicing.md
Outdated
| The end state: `cargo build` automatically slices dependencies based on static usage analysis, achieving: | ||
|
|
||
| - **30-50% reduction in fresh build time** for dependency-heavy projects | ||
| - **Faster CI pipelines**: Cold builds complete in seconds, not minutes | ||
| - **Improved rust-analyzer responsiveness**: Less code to index and analyze | ||
| - **No ecosystem changes required**: Works with existing crates.io crates | ||
|
|
||
| This complements ongoing efforts: | ||
| - **Parallel frontend** reduces wall-clock time; slicing reduces total work | ||
| - **Cranelift** accelerates codegen; slicing reduces frontend overhead | ||
| - **build-std** optimizes std; slicing optimizes third-party deps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should happen when a set of sliced items has to be expanded in a subsequent build?
I take it that we would fall back to a fresh build of a dependency when a call to previously-unused function ends up being introduced incrementally? If so, that would be quite detrimental to incremental builds, especially around use of "fundamental" crates like serde. Picture a scenario where in Zed (a project with over 200 workspace members and 1.5k crates to build in total) we would have to re-slice serde: that'd be nearly-clean rebuild, as we would have to rebuild all of our dependencies that depend on serde and so on and so forth. Our experience would actually regress in such cases (compared to status quo, where clean builds take longer, but introducing a call to a function from a dependency doesn't require re-building that dependency).
In order to avoid that, you could slice just the transitive dependencies. Assuming that direct dependencies are stable, no change to user code could change a result of slicing for indirect dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the current idea is to create a "workspace" replacement of sliced crates in the source form, then make the compiler (fresh and incremental build) faster without changing the compiler itself :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me loop this back to see if I'm understanding the proposal. I understand the current plan is to experiment with this out of tree but I want to comment from the requested compiler team perspective with respect to soundness and feasibility of integration within rustc.
The plan as I understand it is approximately to:
- Add a pre-compilation static-analysis step which parses each crate in a dependency tree to identify the set of APIs it interacts with from its upstream/transitive dependencies.
- Unify these sets across a crate graph to identify the minimally subset of used APIs for each crate in a crate graph
- Generate minimal source "slices" of each crate in the crate graph that removes all code outside of their minimal set of code, doing essentially dead source code elim
- Start a fresh/normal compilation using the sliced versions of crates in place of their original full copies.
Assuming I got that all right, sounds cool and I hope that it shows some promising speedups.
As reflected in some of the other comments I left I am concerned that some of the areas of work you hope to make cuts from may be unavoidable without introducing inaccuracies into the calculated minimal slice leaving out APIs that are in fact in usage. Specifically avoiding parsing, name resolution, and macro expansion on the full crate. Assuming this ends up being a problem this may extend all the way into type checking being necessary due to type-relative name resolution, which depends on type information being available.
src/2026h1/crate-slicing.md
Outdated
| Current mitigations: | ||
| - **Incremental compilation**: Caches type-checking results but requires prior builds | ||
| - **sccache**: Caches compilation artifacts but requires cache hits | ||
| - **Cranelift**: Accelerates codegen (step 5) but not frontend (steps 1-3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mentions a step 5 but I'm guessing its been removed from the aforementioned list.
src/2026h1/crate-slicing.md
Outdated
| | Generics | Templates (header-only) | Monomorphization | Only used instantiations codegen'd | | ||
| | Trait impls | N/A | Coherence rules | Must include all impls for used types | | ||
| | Conditional compilation | `#ifdef` | `#[cfg]` + features | Must evaluate feature expressions | | ||
| | Macros | Preprocessor (textual) | Proc-macros (semantic) | Proc-macro crates kept as-is | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this also include macro rules?
| | Macros | Preprocessor (textual) | Proc-macros (semantic) | Proc-macro crates kept as-is | | |
| | Macros | Preprocessor (textual) | Proc-macros (semantic) + `macro_rules!` | Proc-macro crates kept as-is | |
src/2026h1/crate-slicing.md
Outdated
|
|
||
| 5. **Trait coherence**: Conservative inclusion of all trait impls where the implementing type or trait is used | ||
|
|
||
| 6. **Macro handling**: Skip `macro_rules!` bodies during re-export extraction; preserve proc-macro crate references |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this part work? I'm assuming this means you're definitely not doing full macro expansion. Are you parsing the definitions to find all potential usages that could be invoked from a macro and then just inserting all potential usages into each crate that invokes the macro in any way? I am also assuming you're not just ignoring macro_rules! entirely because it feels like that would guarantee that some rather important APIs get marked as unused and sliced out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the current implementation, I already find macro expansion unavoidable. So I am utilizing some of the functions in cargo expand to do just that, before the further processing. This could slow down the static analysis part, but my priority at the moment is to get the correctness right first, then checking whether we could speed up the toolchain next.
src/2026h1/crate-slicing.md
Outdated
|
|
||
| ### Open research questions | ||
|
|
||
| 1. **Monomorphization interaction**: Does slicing reduce monomorphization work (fewer generic items) or increase it (more crate boundaries)? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would slicing increase crate boundaries? My understanding based on the description in this RFC is that slicing would essentially generate minimized versions of source crates that are then fully recompiled. I would understand this to mean that there are the same number or fewer (if a transitive dep is marked fully unused) compilation units in total than the unsliced case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed ! Due to the cfg gate features in Cargo.toml and its transitive implications, the slicer tool could remove any features not related to the current compilation target, hence cut roughly 1/3 of the crates for the "self-slicing" case study of the slicer itself. Meanwhile, the situation for difficult crates depends really on the nature of the crate implementation, ranging from very effective to less effective. So eventually some tradeoffs could be made to generate the "right"-level of slices based on some "passthrough" heurstics. By passthrough, we simply copy the original crate code over because the heuristics may say there won't be much gain in performance. ... more experiments will help later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried that the approach described here cannot be made reliable enough to be worth significant investment. I agree that if we want really huge compile time improvements that approaches to intercept work as early as possible are the most likely to succeed.
But I don't understand why you wouldn't use the compiler to do the slicing. The proposal seems to suggest that doing the slicing without the compiler would be faster, but I tried out the prototype on one of my own projects with a modestly-sized dependency tree that includes tokio and some AWS SDK crates. Doing a full clean cargo check takes 15 seconds wall time or 87 seconds CPU time, and cargo build takes 20 seconds wall time or 167 seconds CPU time. Trying to run cargo-slice on the project takes 160 seconds wall time or 3848 seconds CPU time... then it crashes, and most of the sliced crates do not build.
So I think the idea has merit, but I don't see the reason for writing a completely separate tool that has to reimplement a significant chunk of the Rust language.
src/2026h1/crate-slicing.md
Outdated
| The Rust compiler must: | ||
| 1. **Parse** all 90,000 lines through `syn`/rustc's parser | ||
| 2. **Resolve** names and expand macros across all modules | ||
| 3. **Type-check** all items, including unused ones | ||
| 4. **Monomorphize** generic code (though only used instantiations) | ||
|
|
||
| Only step 4 naturally excludes unused code. Steps 1-3 process everything. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I'm aware, we are making a deliberate decision to report errors in dead code. So I think it is misleading to say the compiler "must" process everything. The compiler is designed to find and diagnose errors first and generate good code quickly second.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point ! The prototype tool is still very immature, I was just using it to illustrate the concept and potential savings, the tool itself still is under heavy debugging. As far as it is now, self-slicing cuts the dependent 161 crates down to 50, and the 2MLOC to 40KLOC, a saving of size by 98%. With respect to the timing, I agree a lot of time is currently wasted on unrelated computation due to the black-box use of rust-analyzer, I am working on getting the resulting code correct before further performance tuning.
src/2026h1/crate-slicing.md
Outdated
| We have a working implementation (`slice_crate`) demonstrating feasibility on major ecosystem crates: | ||
|
|
||
| | Crate | Version | Original Modules | Original LOC | Slice Time | Status | | ||
| |-------|---------|------------------|--------------|------------|--------| | ||
| | tokio | 1.48.0 | 315 | ~90,000 | — | ✅ Compiles | | ||
| | actix-web | 4.12.1 | 87 | ~28,000 | — | ✅ Compiles | | ||
| | hyper | 1.8.1 | 51 | ~19,000 | — | ✅ Compiles | | ||
| | axum | 0.7.9 | 45 | ~15,000 | — | ✅ Compiles | | ||
| | reqwest | 0.12.28 | 26 | ~13,000 | — | ✅ Compiles | | ||
| | syn | 2.0.x | 47 | ~15,000 | — | ✅ Compiles | | ||
| | rand | 0.9.2 | 31 | ~8,000 | — | ✅ Compiles | | ||
| | regex | 1.x | 21 | ~6,000 | 0.147s | ✅ Compiles | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The README for cargo-slicer, which I think is the project you are referring to here (not slice_crate) also mentions in its README that it has problems compiling nearly all of these crates. So I'm not sure I would say that your prototype is "working" and "demonstrates feasibility". You are proposing to build a tool that can handle (either by understanding or recognizing its own lack of understanding and bailing out) the full complexity of the Rust language, so at least I would rather see a serious argument that all of the problems with the approach are fixable.
src/2026h1/crate-slicing.md
Outdated
| This dependency graph expands to 200+ crates. The `tokio` crate alone contains 315 module files totaling ~90,000 lines of Rust source. Yet a typical application uses perhaps 5-10% of tokio's public API surface (e.g., `tokio::spawn`, `TcpListener::bind`, `select!`). | ||
|
|
||
| The Rust compiler must: | ||
| 1. **Parse** all 90,000 lines through `syn`/rustc's parser |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syn is not rustc's parser, and neither is it rust-analyzer's. Here, you are describing the behavior of rustc with the intent of distinguishing it from the behavior you say you want, so even if syn was functionally equal, you shouldn't use this slash here, as it muddles the rest of your motivation. And "(rustc must) parse code through rustc's parser" would be... well, redundant, so you can just say
| 1. **Parse** all 90,000 lines through `syn`/rustc's parser | |
| 1. **Parse** all 90,000 lines |
src/2026h1/crate-slicing.md
Outdated
| - Handle `#[path = "..."]` module redirections | ||
|
|
||
| 3. **Transitive closure**: Compute minimal item set: | ||
| - Include all trait impls for used types (coherence requirement) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"(coherence requirement)"?
Which requirement, exactly? If an item is unused, surely it is still, well, unused? ( editor's note: "it depends", see later. ) Hm, do we codegen traits that are never called...?
I could see applying this as a simplifying assumption, at least, since the way the trait graph works can be... complex. But that is mostly a statement that it is difficult to tell what is actually unused without being a full Rust compiler.
In fact, I think this proposed slicing... removing trait impls for unused types... would already cause code to compile (and maybe resolve differently?) that wouldn't, because it can remove an alternative implementation from the trait graph if the type is unused per se. That would, in some cases, allow inference to select a type where it would have to fail instead, because if there's only one impl it can "just" pick that one.
I am not sure if this slicing is correct to do for all code that currently compiles, as it can change inference reasoning which is subtle. Inference is something I do not fully understand and is subject to change between versions. But I am confident it would, right now, definitely be incorrect to do for all code without considering whether it already compiles according to rustc.
That means this proposal... requires running rustc first, in order to be correct, I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me add an additional task at the development stage to use rustc to verify the corner cases.
| - Include all trait impls for used types (coherence requirement) | ||
| - Follow type definitions for struct fields and enum variants | ||
| - Expand `pub use crate::*` glob re-exports | ||
| - Preserve visibility modifiers for cross-module access |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary to explicitly state? Surely copying an item means copying all of its source code, attributes, and doc comments, right?
src/2026h1/crate-slicing.md
Outdated
| Measured slice times for smaller utility crates: | ||
|
|
||
| | Crate | Slice Time | Items Needed | Module Files | Lines Generated | | ||
| |-------|------------|--------------|--------------|-----------------| | ||
| | regex | 0.147s | 309 | 20 | 10,671 | | ||
| | memchr | 0.198s | 8 | 35 | 14,475 | | ||
| | anyhow | 0.058s | 147 | 11 | 4,412 | | ||
| | futures | 0.1s | 3 | 0 | 91 | | ||
| | once_cell | 0.025s | 2 | 0 | 29 | | ||
| | thiserror | 0.010s | 2 | 0 | 28 | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performance numbers require considering the host environment. We use a very controlled benchmarking environment to minimize run-to-run noise when testing the Rust compiler, and it still is deeply imperfect in a variety of ways. I don't have any objection to you presenting a number here, but if it's being used as motivation then it should include how you got it. Even just the command line used, so we know what was actually measured and what tool was in play, would be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. At the moment while dealing with corner cases, the slicing time is already way longer than the very earlier prototype. I will remove these numbers for now, until the tool can self-slicing and improve its r-a custom computation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Such numbers are indicative, and it depends on the 48 core machine I am using in experiments. Let me remove them because other machines may not have the same numbers.
src/2026h1/crate-slicing.md
Outdated
| This complements ongoing efforts: | ||
| - **Parallel frontend** reduces wall-clock time; slicing reduces total work | ||
| - **Cranelift** accelerates codegen; slicing reduces frontend overhead | ||
| - **build-std** optimizes std; slicing optimizes third-party deps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That isn't really what build-std is "for", and it's not really equivalent to what you are thinking of here (optimizing compile times, specifically... build-std can easily make them worse compared to our precompiled standard libraries). I would recommend not including this example as it muddles your motivation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, removed the third bullet.
src/2026h1/crate-slicing.md
Outdated
| The end state: `cargo build` automatically slices dependencies based on static usage analysis, achieving: | ||
|
|
||
| - **30-50% reduction in fresh build time** for dependency-heavy projects | ||
| - **Faster CI pipelines**: Cold builds complete in seconds, not minutes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you want to achieve exactly? The first and second bullet point contradict each other, either it's 30-50% reduction or "seconds, not minutes".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
src/2026h1/crate-slicing.md
Outdated
|
|
||
| - **30-50% reduction in fresh build time** for dependency-heavy projects | ||
| - **Faster CI pipelines**: Cold builds complete in seconds, not minutes | ||
| - **Improved rust-analyzer responsiveness**: Less code to index and analyze |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt this approach is useful for r-a, it must index unused code too for it to be able to offer completions to use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indeed r-a has a lot of extra computation that may not be necessary for this purpose. As Niko suggested, using r-a rather than the rustc has the advantage that it might be possible to reduce the time for fresh builds. Using rustc to reduce the time of rustc currently would be slower than rustc in principle. I found that the hard way in the ICSM05 paper, where using gcc 3.4.0 I was trying to generate C/C++ code for gcc to be quicker. Although it does reduce the code size, due to the nature of invoking gcc twice for the compilation, it can't win for the fresh build scenarios. In the end, I found a lighter weight tool ctags could deliver the promise in the 2012 technical report. Taking this analogy, when the approach is applied to Rust compiler, we would need to find a lighter computation than Rustc to compute the dependencies. Hence the r-a. I believe later on we could customize the r-a computation for this purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have to rely on r-a and the intended customization of r-a is only going to be useful for this special purpose (fresh build performance) rather than general purpose, so I remove this bullet on improving r-a.
Let me work on the debugging and timing and see if we can deliver gains in the end. In my recent improvement of the precc, I found it possible to have a 1.8x speed up for C/C++ compilation, so fingers crossed whether it could work for Rust :-) |
Updated dependency graph details, refined compiler steps, and improved prototype results. Enhanced usage extraction and clarified technical challenges and open research questions with respect to the feedback received so far.
formatting the references
academics.istar2
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's up with all these random files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, that was from my internal analysis of project goals for a separate project. Let me clean it up!
…project-goals into crate-slicing-goal
nikomatsakis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yijunyu I think it would help to clarify the goal of this goal =)
In particular, I think one way to view this goal is as an "visibility" goal, where you are saying: I'm going to build a proof-of-concept to see try to get an upper bound on how much benefit we could get from this approach. The code is not meant to be upstreamed, we'll have to rewrite it from scratch to really move to prod, but it can help inform whether that work is worth doing or not.
Another is to say you are attemping to build something we expect to land upstream and ship -- a prod-ready version, in other words.
Based on my read of this goal, it feels more like the former, is that correct?
|
The current status of the goal is to assess how much could be gained on a fresh build by cutting unnecessary items in the source code, e.g. 98% for the slicer itself, for seriously large projects like Zed and see if it is worthwhile to build an efficient tool that delivers that. If we can achieve that for the top crates-io projects within 6 months, the next step would be to upstream some necessary changes for enabling more possibilities. I am keen to work on this Rust adoption pain point. |
|
As the aim is towards adoption (third-party or upstream), I'd be especially interested if you can validate the correctness of this approach. If people adopt it pervasively, it exposes the potential to significantly change what code compiles or doesn't in an actual build, yet we cannot assume that in a fresh build that any code has yet passed through the entire type system and survived without error. Not even in dependencies, which can easily be patched or have feature flags or target-specific code or etc. |
The planned activities now include an important step to use Rust compiler to check whether the original code and sliced code builds the semantically equivalent targets, and test in large-scale (something needs a continuous regression testing). Only when such checks pass we may allow it to be used in production. Fortunately all the rules used in the slicer can be modularised by their roles played in parsing and cfg gate evaluations, such a ‘closure’ can be reached and maintained, with the benefits of speeding up compilation time. It is my plan to grow the correctly tested scope gradually and incrementally in the approach. |
Summary
This PR proposes a new research goal for 2026H1: Crate Slicing for Faster Fresh Builds.
The goal is to research and prototype "crate slicing" — a static analysis technique that computes the transitive closure of items actually used from dependency crates and generates minimal sliced versions, reducing frontend parsing and type-checking overhead during fresh builds.
Motivation
Most projects use only a fraction of their dependencies' public API, yet compile 100% of the code. A project using
tokio::spawnand a few I/O primitives still compiles all 315 modules (~90,000 lines) of tokio.Prior Art
This builds on 20 years of C/C++ precompilation research:
Previously, there are two related Rust solutions:
Prototype Results
A working prototype (
cargo-slicer) successfully slices major ecosystem crates:Team Asks
References
Rendered