-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Faster elide middle implementation #2487
Conversation
ElideMiddle is only used in the unit tests? Can you move the declaration there then? I'm also not sure if it's a good idea to move so much code around because it breaks the Git history. Especially for the unit tests it would be helpful to see, that your changes don't change any behavior just by looking if you touched any tests. |
Forgot to say: Big thanks! I was thinking that std::regex would probably to slow and we should replace it before releasing 1.13.0. |
I can keep the unit-test at the same location, but I would rather move the function to its own source file, given its complexity (especially after the refactor). Another option is to make the unit-tests more readable first, using string macros to encode the ANSI sequences, so things look like:
Which is easier to read, then move to a different file. Wdyt? |
Use C string macros to hold ANSI escape sequences in order to make the test much easier to understand and follow. NOTE: This does not change the test in any way!
Move the function to its own source file since it is no longer trivial (and will get optimized in future patches). + Add a new ElideMiddleInPlace() function that doesn't do anything if no elision is required + use it in status.cc. + Add a new elide_middle_perftest program to benchmark the performance of the function. On my machine, the 'avg' result is 159.9ms.
Add a fast-path for the case where there is no escape sequence in the input, that avoids un-necessary string allocations. Properly handle ANSI color sequences that appear in the elided part of the input string. They *must* be preserved to ensure that the right span is printed with the right color. For example, consider the following input: |GREEN|aaaaaaaaaaaaa|RED|bbbbbbbbbbb|BLACK| With different levels of elision: max_width=0 -> |GREEN|RED|BLACK| instead of "" max_width=5 -> |GREEN|a..|RED|b|BLACK| (this was |GREEN|a..b|BLACK| before this fix) Moreoever, do not call std::regex_iterator::str() to avoid un-necessary string allocations and concatenations. With this patch, 'elide_middle_perftest' goes from 159.9ms to 87.1 ms on my machine (x1.8 faster)
Get rid of std::regex and std::vector usage in ElideMiddleInPlace() function. These are replaced with custom iterator classes that do not perform any memory allocation at runtime. Instead the input string will be parsed twice (once to determine the visible width, and another time to create the elided result string). With this patch, elide_middle_perftest goes from 87.1ms to 15.0ms on my machine (x5.8 faster!). This also removes 65 kiB from the stripped Linux binary for Ninja (!)
Just check for the ESC character in the `output` string, if none is present, or if the terminal supports color, avoid unnecessary busy work (string allocations and parsing).
0b8f610
to
1ba5570
Compare
I like it :) And since we now have to touch all lines anyway, moving it to its own file is fine. |
Thanks! |
1 similar comment
Thanks! |
PR ninja-build#2487 introduced a regression, where a completed command without an output would force a newline, preventing the next status update to appear on the same line in smart terminals. This fixes the issue by adding the missing `!outputs.empty()` condition + adding a proper regression test to catch future breaks. Fixed: ninja-build#2499
PR ninja-build#2487 introduced a regression, where a completed command without an output would force a newline, preventing the next status update to appear on the same line in smart terminals. This fixes the issue by adding the missing `!outputs.empty()` condition + adding a proper regression test to catch future breaks. Fixed: ninja-build#2499
Drastically speed up the implementation of the ElideMiddle() function by getting rid of
std::regex
usage abd unnecessary string allocations / reallocations. This also removes about 65 kiB from the stripped Linux binary.Overall performance test speedup is x10.6 (from 159ms to 15,0ms) on my machine