Make debugger significantly faster#12645
Conversation
All of this code doesn't actually depend on anything from libexpr. Because Pos is so tigtly coupled with Error, it makes sense to have in the same library.
This should provide context for follow-up commits in the patch series.
Previous implementation didn't actually check if
std::get_if returned a nullptr:
std::optional<SourcePath> getSourcePath() const {
return *std::get_if<SourcePath>(&origin);
}
| * into memory each time. Call this only if absolutely necessary. Prefer | ||
| * to keep PosIdx around instead of needlessly converting it into Pos by | ||
| * using this lookup method. | ||
| */ |
There was a problem hiding this comment.
thought:
Additionally it may make sense to keep a number of recent files in memory.
I doubt that it'd be noticeable after your fixes though.
There was a problem hiding this comment.
Yeah, using an LRUCache might make sense if we want to speed up the debugger eval even further. Though I don't think it's strictly necessary for this PR. There's much mess to untangle and premature optimizations might introduce unnecessary complications. ~5 minor releases seem to have this performance problem, so sticking to several orders of magnitute speedup should suffice for now.
In my profiling operator[] only gets called 3 times for valgrind --tool=callgrind src/nix/nix eval nixpkgs#hello --impure --ignore-try --no-eval-cache --debugger: (after this PR)
src/libexpr/eval-error.cc
Outdated
| .env = env, | ||
| .hint = HintFmt("Fake frame for debugging purposes"), | ||
| .isError = true}); | ||
| /*pos=*/expr.getPos(), |
There was a problem hiding this comment.
| /*pos=*/expr.getPos(), | |
| /*pos=*/noPos, |
IIUC this is equivalent and potentially slightly faster.
There was a problem hiding this comment.
Not sure if we want to be saving on matches here. A virtual function call is basically free and this function seems to only be called in case an EvalError is thrown. Then all bets are off and stack unwinding shadows any possible virtual function call.
src/libexpr/eval.hh
Outdated
| std::variant<Pos, PosIdx> pos_; | ||
| const Expr & expr_; | ||
| const Env & env_; | ||
| HintFmt hint_; | ||
| bool isError_; |
There was a problem hiding this comment.
Normally we don't use special field names. Not a blocker for me but will check with the others.
There was a problem hiding this comment.
Somewhat soon to say now, but I get the impression that we don't want this, or if we do, it'd be a bit of a project to use it consistently, handle some merge conflicts etc. Best to remove the _ suffixes for now. I feel like maybe opinions can be swayed, but let's keep that as something separate.
There was a problem hiding this comment.
it'd be a bit of a project to use it consistently
Sure, I don't have any strong feelings on this.
I've also reduced the diff significantly and dropped the not strictly necessary getters. I feel like it'd be suggested either way, so I'm getting this out of the way early.
As a side note, in an ideal world there would just be a clang-tidy config for ensuring naming conventions. That way no developer time has to be spent on stuff that tooling can do automatically.
|
Should also fix #10796 |
The underlying issue is that debugger code path was calling PosTable::operator[] in each eval method. This has become incredibly expensive since 5d9fdab. While we are it it, I've reworked the code to not use std::shared_ptr where it really isn't necessary. As I've documented in previous commits, this is actually more a workaround for recursive header dependencies now and is only necessary in `error.hh` code. Some ad-hoc benchmarking: After this commit: ``` Benchmark 1: nix eval nixpkgs#hello --impure --ignore-try --no-eval-cache --debugger Time (mean ± σ): 784.2 ms ± 7.1 ms [User: 561.4 ms, System: 147.7 ms] Range (min … max): 773.5 ms … 792.6 ms 10 runs ``` On master 3604c7c: ``` Benchmark 1: nix eval nixpkgs#hello --impure --ignore-try --no-eval-cache --debugger Time (mean ± σ): 22.914 s ± 0.178 s [User: 18.524 s, System: 4.151 s] Range (min … max): 22.738 s … 23.290 s 10 runs ```
| /* NOTE: position.hh recursively depends on source-path.hh -> source-accessor.hh | ||
| -> hash.hh -> config.hh -> experimental-features.hh -> error.hh -> Pos. | ||
| There are other such cycles. | ||
| Thus, Pos has to be an incomplete type in this header. But since ErrorInfo/Trace | ||
| have to refer to Pos, they have to use pointer indirection via std::shared_ptr | ||
| to break the recursive header dependency. | ||
| FIXME: Untangle this mess. Should there be AbstractPos as there used to be before | ||
| 4feb7d9f71? */ |
There was a problem hiding this comment.
I am guessing experimental-features.hh is the easy thing to make more abstract
I was planning on doing that anyways for a new settings system.
There was a problem hiding this comment.
Forgot to write down more thoughts.
While it would be good to slim down nix-util and have more things in libraries with well-defined scopes, I think it's ok to consider Pos to be part of a text handling component; not the evaluator. This frees it up to be used in nix.conf logic and other file types.
|
Thank you so much @xokdvium! |
…2645 Make debugger significantly faster (backport #12645)
…2645 Make debugger significantly faster (backport #12645)
…2645 Make debugger significantly faster (backport #12645)
…2645 Make debugger significantly faster (backport #12645)
|
Released in
Backported but end-of-life
|

Motivation
The underlying issue is that debugger code path was
calling
PosTable::operator[]in eachevalmethod.This has become incredibly expensive since 5d9fdab.
While we are it it, I've reworked the code to
not use std::shared_ptr where it really isn't necessary.
As I've documented in previous commits, this is actually
more a workaround for recursive header dependencies now
and is only necessary in
error.hhcode.Some ad-hoc benchmarking:
After this commit:
On master 3604c7c:
Context
nix build --debugger#10796cc @roberth for 50123f2
cc @tomberek, #12544 (comment)
Add 👍 to pull requests you find important.
The Nix maintainer team uses a GitHub project board to schedule and track reviews.