-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Incremental recompilation #2369
Comments
there is the code in trans that tries to determine dependencies for the purposes of metadata export. this seems like a reasonable starting point for such a graph. |
I think it would be nice if this was done in such a way that incremental compilation appears to the programmer no differently than full-compilation. For example I mean that:
|
@Dretch -- I totally agree; the only thing the programmer should notice is that recompilation will usually be much faster :-) (1) and (2) are great goals to have written down; if we don't achieve those (especially (2)), I won't consider this issue to be completed. |
If we do this at the level of "caching bitcode for distinguished subtreees of a crate", it's probably not too hard (though I'm not sure it'll make things a lot faster). If we do this at the level of "trying to cache target-machine object files", things get a fair bit weirder. We (or, well, I) chose the existing compilation model for crates based on the need to "eventually" do cross-module optimization (in particular, single-instantiation of monomorphic code and inlining). Crates were the optimization boundary, the compilation-unit boundary. We have subsequently grown cross-crate inlining. So this distinction is a bit less meaningful, at least in terms of optimization. There is still a meaningful (in the sense of "not easy to eliminate or blur") linkage boundary at work in two terms: monomorphization instantiations and version-insensitivity (the "anything called 1.0 with the same types is the same crate" rule discussed last week). Overall, this is one of a few bugs pointing in a similar direction: #2176, #1980, #2238, #558, #2166, #456 and even #552 to some extent. I am not saying these are all wrong. They are all pointing to similar sets of semantic weaknesses .. or "surprises" .. in the existing compilation model. I would like to have a conversation at some point (probably in public, or videoconf, or both) where we approach this problem as a design problem, and try to work out a new set of agreeable principles and plan-of-action for future work that spans the whole set of related bugs. I do want to fix them, but do not want to go much further down this road without having a map of where we're going. As an example: it could be that we wind up treating all inter-module functionality uniformly via a multiple dimensions of a single kind of |
Yes, don't worry, I won't jjump into this without some serious design discussions. |
issue appears to be properly classified |
High, not 1.0 |
Do we still want to attempt this, or is splitting into crates viewed as enough now that we have static linking? I guess the missing feature would be combining multiple static libraries into a dynamic library. |
Right... Ideally this should take into account whether compiler flags and/or environment variables have changed. Perhaps compiler version would be another thing to take care of. So a simple solution is to store output in directories which contain checksum of all the parameters we care about. |
Personally, I do still believe that compilers shouldn't be to claver about how to build projects and attempting to replace tools like make wouldn't likely end quite well. I know that clang has those JSON files, which I haven't quite looked into... Also wanted to point out that Qt's QBS certainly looks very appealing. |
Distributed build systems for Rust: http://discuss.rust-lang.org/t/distributed-build-systems-for-rust/400
Or maybe Rust should use Ninja… |
Shake is another interesting build system: https://github.com/ndmitchell/shake A comparaison with Ninja: http://neilmitchell.blogspot.fr/2014/05/build-system-performance-shake-vs-ninja.html |
Hi, let me give some pointers to the Haskell world. GHC has this solved - and probably the best working incremental recompilation engine on the planet. It can do:
GHC has documented its approach in high detail, and highlighted what the problems are. See here: https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/RecompilationAvoidance I believe that Rust is in perfect shape to reach the same level of incremental compilation, and can likely apply the same techniques. It's all there, we just have to copy it ;)
I once also thought so, but this is not the case. External build tools like |
An important side effect of incremental recompilation that was not mentioned, and I think it's more important than building environment enablement, is enabling tooling (like IDEs) to perform operations like on-the-fly code analysis and prompt the user for warnings, code completion, etc. |
The problem with 99% of build tools out there is that they duplicate the dependency management by either letting the user specify dependencies prior to target execution and/or by scanning them heuristically with prior knowledge over the type of executed target, and often doing so wrongly, failing to duplicate intrinsic logic, resulting in broken or failed builds. The builds are sometimes slower than they could be because the developer forsakes on specifying dependencies in favor of safe but slow re-execution, to avoid the aforementioned broken builds. I suggest that you look into a newer approach, where dependencies are detected reliably for any kind of intermediate. The developer only needs to worry about target invocation itself. So, if you neatly split the build process to a DAG of separate process invocations, even with extremely complicated dependencies, this tool can track them easily. |
@huonw Is this issue the right place to discuss the RFC? |
Even if not, I'll ask some questions here:
Why was this chosen? Wouldn't it make sense to include source files in the dependency graph as well, so that you can skip parsing and even reading the file contents if the file modification time suggests that the file has not changed?
I'm not familiar with codegen units, and where their boundaries would be set, but if it's typically on the library/crate level, that could make some inlining problematic. Take for example Haskell's |
Eventually perhaps yes. But for the initial versions, we're targeting the things in compilation that are most expensive: LLVM and type-checking. Hashing the HIR also means that we can avoid doing recompilation for smaller, trivial changes, like tweaking a comment -- at least in some cases (it turns out that because that affects the line/col number of all statements, we would need to change at least debuginfo, but we can hopefully isolate the effects of that in the future.) There are also just practical concerns. It's much easier to reduce the amount of the compiler we have to instrument.
Users can always add |
#34956 🎊 |
@nikomatsakis Thanks for your explanation. |
@michaelwoerister is this still the best tracking issue for incremental? What's the current status? |
I forgot this issue existed. The preferred tracker is rust-lang/rust-roadmap-2017#4. In fact, I'm just going to close this issue. |
@nh2: It's frustrating (and hardly uncommon) for people to show up and say we "just need to copy Haskell" while ignoring the very real differences between the languages. In this case, the most important difference is that a whole Rust crate is semantically a single compilation unit, in fact a single syntax tree. Any
The only way around this is the tedious and error-prone approach of writing a hs-boot file, equivalent to writing header files in C. In practice almost nobody does this; they simply structure their programs so the module dependency graph is acyclic. In terms of the build system (not in terms of packaging / versioning), this is like placing every So, most of the features you highlight in |
reborrow error: clarify that we are reborrowing *from* that tag `@saethlin` I found the current message not entirely clear, so what do you think about this?
I want to be able to recompile only dependencies (rather than everything in a crate), like SML's compilation manager or GHC's
--make
mode. I don't really care about the approach so long as it works and the outcome is that adding one#debug
call in one file doesn't trigger recompilation of 100 other files in the same crate :-) I am volunteering to work on this after 0.3, but suggestions are welcome. Patrick suggested a good place to start would be to generate a (visualizable) graph of item dependencies, which makes sense to me.The text was updated successfully, but these errors were encountered: