-
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
Macro expansion is quadratic in the number of let
statements.
#10607
Comments
From my own tests, I can see 4 * N * N from to_bytes::IterBytes$__extensions__::iter_bytes::anon::expr_fn::Uzadaf
2 * N * N from hash::Hash$A::hash_keyed::h463e3d8e059f5319Cac::v0.9$x2dpre I have two callgrind dumps, one for |
I seems the problem starts with ast_util::new_rename::h39e8665ceaa551c1aF::v0.9$x2dpre originating from ext::expand::ast_fold$IdentRenamer::fold_ident::hf98b2d77f07645a0v1a9::v0.9$x2dpre EDIT: IdentRenamer::fold_ident is |
The relevant part of cc @jbclements, looks like hygiene comes at a cost and/or needs more caching. |
Yep, everything here makes sense. I'm not surprised that compilation time here is n^2 in the depth of 'let' nesting. It appears to me that compilation of programs that nest identifiers to a depth of 5000 is likely to take an extra ten seconds. There are additional forms of laziness that could be applied to address this problem, but I think that there are other more pressing issues. Agree/disagree? |
@jbclements More pressing issues with respect to efficiency of rustc? Or in getting macro-expansion/hygiene fixed up and/or finalized? My thinking: As long as the experts are confident that the slowdowns here can be addressed without changing the semantics of macro expansion, then I agree we don't need to fix this immediately. (Except if not fixing it is going to make it difficult to identify other sources of inefficiency within rustc, i.e. by making it difficult to construct similar micro-benchmark inputs to rustc...) |
I see nothing that would prevent (additional) laziness here, though it would be a bit of a headache; I think the most natural solution would be to introduce a "delayed-rename" AST node, so that you can be sure you don't forget to apply the rename everywhere. I take your micro-benchmark comment, though. In this case, I'm guessing that you wouldn't really want a local stack frame with 5K variables, though you might be checking to make sure that they all get optimized away. In that case, I'm guessing that 50 variables would work as well as 5000. |
(A bit of context here: the "laziness" is the faster-but-more-complex algorithm described in the classic paper on hygiene in Scheme: http://portal.acm.org/citation.cfm?id=173617.173620 ) |
I was thinking of Clinger and Rees "Macros that Work", but its been a while since I looked at either carefully, maybe Dybvig "Syntactic Abstraction in Scheme" is the better reference to use at this point. my memory is that the differences between the papers are most relevant when you start having procedural macros. Rust's macros are still much like Scheme's (If nothing else, at least these links are not behind a paywall.) |
The smoking test time has just been halved: 12563 comment. Most likely, it has to do with improved |
Another 2x speedup by specializing hasher for |
It matters when the code in question spends minutes in hygiene, thanks @edwardw :D. |
Triage: don't think anything has happened with this in a while. |
let
statements.let
statements.
I fixed this in #34570. |
Simplify the macro hygiene algorithm This PR removes renaming from the hygiene algorithm and treats differently marked identifiers as unequal. This change makes the scope of identifiers in `macro_rules!` items empty. That is, identifiers in `macro_rules!` definitions do not inherit any semantics from the `macro_rules!`'s scope. Since `macro_rules!` macros are items, the scope of their identifiers "should" be the same as that of other items; in particular, the scope should contain only items. Since all items are unhygienic today, this would mean the scope should be empty. However, the scope of an identifier in a `macro_rules!` statement today is the scope that the identifier would have if it replaced the `macro_rules!` (excluding anything unhygienic, i.e. locals only). To continue to support this, this PR tracks the scope of each `macro_rules!` and uses it in `resolve` to ensure that an identifier expanded from a `macro_rules!` gets a chance to resolve to the locals in the `macro_rules!`'s scope. This PR is a pure refactoring. After this PR, - `syntax::ext::expand` is much simpler. - We can expand macros in any order without causing problems for hygiene (needed for macro modularization). - We can deprecate or remove today's `macro_rules!` scope easily. - Expansion performance improves by 25%, post-expansion memory usage decreases by ~5%. - Expanding a block is no longer quadratic in the number of `let` statements (fixes #10607). r? @nrc
Simplify the macro hygiene algorithm This PR removes renaming from the hygiene algorithm and treats differently marked identifiers as unequal. This change makes the scope of identifiers in `macro_rules!` items empty. That is, identifiers in `macro_rules!` definitions do not inherit any semantics from the `macro_rules!`'s scope. Since `macro_rules!` macros are items, the scope of their identifiers "should" be the same as that of other items; in particular, the scope should contain only items. Since all items are unhygienic today, this would mean the scope should be empty. However, the scope of an identifier in a `macro_rules!` statement today is the scope that the identifier would have if it replaced the `macro_rules!` (excluding anything unhygienic, i.e. locals only). To continue to support this, this PR tracks the scope of each `macro_rules!` and uses it in `resolve` to ensure that an identifier expanded from a `macro_rules!` gets a chance to resolve to the locals in the `macro_rules!`'s scope. This PR is a pure refactoring. After this PR, - `syntax::ext::expand` is much simpler. - We can expand macros in any order without causing problems for hygiene (needed for macro modularization). - We can deprecate or remove today's `macro_rules!` scope easily. - Expansion performance improves by 25%, post-expansion memory usage decreases by ~5%. - Expanding a block is no longer quadratic in the number of `let` statements (fixes #10607). r? @nrc
Add spans to `clippy.toml` error messages Adds spans to errors and warnings encountered when parsing `clippy.toml`. changelog: Errors and warnings generated when parsing `clippy.toml` now point to the location in the TOML file the error/warning occurred.
The following (bash) shell snippet prints
let _x: int; /* <number> */
5000 times, which makes the compiler very unhappy.(the total time is 10.3s.)
First few lines of
perf report
:i.e. ~70% of the time is spend on hashing/hashmap look-ups. (cc #10586, although that's not the full problem.)
The text was updated successfully, but these errors were encountered: