Closed amoldeshpande closed 3 years ago
There also seem to be a couple of go libraries for git. Those might be better options than spawning processes. e.g., https://github.com/go-git/go-git/
+1 to this. Here's a video of before and after (uploading as a zip because GitHub said I had to):
I don't expect it to be as fast as the before... but certainly not as slow as it is unfortunately.
I played around with go-git a little. For a simple case of trying to open a repo where it does not exist it takes 24ms instead of 80+.
I couldn't figure out how to get intellisense working in Sublime Text so that I could understand the module better, so I was not able to replace the other functionality (detecting status, branch, etc.)
In any case, I think it looks like a promising avenue to investigate.
I tried go-git before, there was a huge delay when running the status command at the time. Maybe that has been resolved, I even still have the branch with those changes.
Did you try the latest version? We did some improvements in term of performance, curious to see if that resolves anything.
@amoldeshpande I also have a working implementation of a rust library for doing the command calls, which everyone keeps saying is faster (the issue is the implementation of go on Windows, as on MacOS or unix there's no issue at all). I could provide a test build to you to see of that changes things for the better? It does for me, but I need some input from others.
We already went from +-350ms to 200ms for the git segment rendering(on windows). In wsl2 it takes 10ms at most.
Clearly, if we could get the same performance on windows as on linux, that would be fantastic.
@amoldeshpande I did a quick restore of the changes I did for go-git, the slow status is still present so that's a no go (it's a few seconds, not even ms). On top of that, it can't handle folders inside a repo, it always reports as not in a repo which isn't what I expect.
That said, here's a version of oh-my-posh that has an experimental lib to do cmd executions, I'm looking forward to feedback on this one.
This is an interesting issue, performance for executing all "Language segments" is also not optimal (Takes ~100MS per segment when it is "in context". The latest release has improved it a lot (Mainly for git, but also for the .NET language segment, less for the other segments)
@JanDeDobbeleer i tried your experimental build, but i could not get it to work.
When i download the .zip and extract it, powershell/cmd complains it is missing posh3.dll
I tried to build the branch "https://github.com/JanDeDobbeleer/oh-my-posh3/tree/validate-lib" myself, and after installing GCC i get the warning the .h file could not be found (Which could be explained by the gitignore which excludes .h files)
btw: Loving oh-my-posh, gets better every day :)
Same error as royvou here
i tried your experimental build, but i could not get it to work
@royvou aha. That's interesting as I assumed the binary would have packed it. I'll upload a new zip with that included 👍🏻
i tried your experimental build, but i could not get it to work
@royvou aha. That's interas I assumed the binary would have packed it. I'll upload a new zip with that included 👍🏻
As the binary increased from ~10MB to ~17MB i would have assumed as well! But looks like it hasn't :(
Don’t forget that the segments processing is done in //. So the prompt is always as slow as the slowest segment(more or less).
@royvou @TravisTX I updated the original comment with a new zip file. Now tbh, if I can't pack these dependencies into the exe, I'm not inclined to go down this path.
1 is from main 2 is your experimental build
@TravisTX that's not enough to say we're about to have a party. Confirms what I'm seeing. Better, but not by major margins.
I think you only need the .DLL in the zip if you were to distribute it.
A check with the --debug parameter does not show significant improvements, am i doing something wrong (As multiple people are mentioning it should be a lot faster?)?
(Screenshoht is from the PowerToys Repo, Oh-my-posh3 repo takes about ~120-140MS)
Experimental VS Latest
Also tried replacing the current exe to looks if it "feels" faster, but i would not say so. @lnu Are you sure, the timings above seem to assume it is not processed in parallel.
Ah looks like TravisTX beat me to it ;)
Seems to be ~5% faster.
ok, so I won't try the experimental build since others have already reported back.
It's disappointing to hear about go-git as well :(
Doesn't go have C interop ? maybe a wrapper around just the git commands we need ? using the C libgit.
I have a proposal. I just forked go and removed the timeout that's present on Windows. It's stated that removing it does work, meaning we can give it a "go" and see what the effect is of oh-my-posh built with that fork. Managing that should be doable if it provides to be a major improvement without side effects.
Very, very fast in between everything. I could have made some mistakes here 😓 posh-windows-amd64-experimental.exe.zip
It's a LOT better ! Overall still a bit slower than Linux, but maybe parallelizing git commands will help with that ?
maybe parallelizing git commands will help with that
Depends, most information is fetched by context, so not all are needed. But worthwhile to investigate! Can you tell me what the timing are now? I get 100ms on my machine which previously has 180ms. Looking at @TravisTX's machine that one's a bit faster than most...
Sounds like a great idea Jan 👍
I'm wondering if the change to a remote exe also has (some) effect on performance? Since now the exe is created/destroyed after each command is created. I assume this would prevent some forms of caching as well?
I got about 40ms with my tests above. So that I don't sound ungrateful, this version by itself makes it eminently usable for me now. Anything else on top of that is just gravy.
Now we have to wonder if the margin is enough to maintain an own fork of go?
I like to go for gravy. But 40ms is blazing fast compared to where we came from. If more people can confirm this, I'll do the necessary changes to ensure we can build with that fork on Windows.
Also tried replacing the current exe to looks if it "feels" faster, but i would not say so. @lnu Are you sure, the timings above seem to assume it is not processed in parallel.
With --debug each segment is processed one by one. But you can assume the most expensive segment will be the bottleneck when processed in //.
Am i missing something:
S H:\github\oh-my-posh3> C:\bin\posh-windows-amd64-experimental.exe --shell zsh --config C:\users\amol\.poshthemes\jandedobbeleer.omp.json --debug
Here are the timings of segments in your prompt:
session(true) - 0 ms - %{%}%{%}%{%}█%{%}%{%}amol%{%}%{%}@%{%}%{%}amolhomevista%{%}%{%}█%{%}%{%}%{%}
spotify(false) - 12 ms -
path(true) - 0 ms - %{%}%{%}%{%} oh-my-posh3%{%}%{%}█%{%}%{%}%{%}
git(false) - 15 ms -
battery(true) - 2 ms - %{%}%{%}%{%}█%{%}%{%}100 %{%}%{%}%{%}
node(false) - 0 ms -
shell(true) - 0 ms - %{%}%{%}%{%} ﲵ zsh%{%}%{%}█%{%}%{%}%{%}
root(false) - 0 ms -
exit(true) - 0 ms - %{%}%{%}%{%} %{%}%{%}█%{%}%{%}%{%}
PS H:\github\oh-my-posh3>
I haven't set my powershell up to render unicode correctly, but the segment timings seem in line with what I'm seeing.
Edit: as an aside, it's interesting that it thinks my desktop has a battery :) No need to derail the issue though.
Edit2: With powershell so I'm running the same command
PS H:\github\oh-my-posh3> C:\bin\posh-windows-amd64-experimental.exe --config C:\users\amol\.poshthemes\jandedobbeleer.omp.json --debug
Here are the timings of segments in your prompt:
session(true) - 0 ms - █amol@amolhomevista█
spotify(false) - 10 ms -
path(true) - 0 ms - oh-my-posh3█
git(false) - 16 ms -
battery(true) - 3 ms - █100
node(false) - 0 ms -
shell(true) - 0 ms - ﲵ powershell█
root(false) - 0 ms -
exit(true) - 0 ms - █
PS H:\github\oh-my-posh3>
So far i can't say its any faster.
S H:\github\oh-my-posh3> C:\bin\posh-windows-amd64-experimental.exe --shell zsh --config C:\users\amol\.poshthemes\jandedobbeleer.omp.json --debug Here are the timings of segments in your prompt: session(true) - 0 ms - %{%}%{%}%{%}█%{%}%{%}amol%{%}%{%}@%{%}%{%}amolhomevista%{%}%{%}█%{%}%{%}%{%} spotify(false) - 12 ms - path(true) - 0 ms - %{%}%{%}%{%} oh-my-posh3%{%}%{%}█%{%}%{%}%{%} git(false) - 15 ms - battery(true) - 2 ms - %{%}%{%}%{%}█%{%}%{%}100 %{%}%{%}%{%} node(false) - 0 ms - shell(true) - 0 ms - %{%}%{%}%{%} ﲵ zsh%{%}%{%}█%{%}%{%}%{%} root(false) - 0 ms - exit(true) - 0 ms - %{%}%{%}%{%} %{%}%{%}█%{%}%{%}%{%} PS H:\github\oh-my-posh3>
I haven't set my powershell up to render unicode correctly, but the segment timings seem in line with what I'm seeing.
Edit: as an aside, it's interesting that it thinks my desktop has a battery :) No need to derail the issue though.
Edit2: With powershell so I'm running the same command
PS H:\github\oh-my-posh3> C:\bin\posh-windows-amd64-experimental.exe --config C:\users\amol\.poshthemes\jandedobbeleer.omp.json --debug Here are the timings of segments in your prompt: session(true) - 0 ms - █amol@amolhomevista█ spotify(false) - 10 ms - path(true) - 0 ms - oh-my-posh3█ git(false) - 16 ms - battery(true) - 3 ms - █100 node(false) - 0 ms - shell(true) - 0 ms - ﲵ powershell█ root(false) - 0 ms - exit(true) - 0 ms - █ PS H:\github\oh-my-posh3>
You should use --shell universal
or --shell pwsh
hmm, measure-command in powershell shows significantly higher times for the same commands. Maybe there is an additional startup time factor ?
It's definitely 1/2 the time as measured by powershell and I can feel the difference interactively.
Can't get screenshots to paste
S C:\Users\amol> measure-command {C:\bin\posh-windows-amd64-experimental.exe --config C:\users\amol\.poshthemes\amol.omp.json --debug --shell pwsh}
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 41
Ticks : 410704
TotalDays : 4.75351851851852E-07
TotalHours : 1.14084444444444E-05
TotalMinutes : 0.000684506666666667
TotalSeconds : 0.0410704
TotalMilliseconds : 41.0704
PS C:\Users\amol> C:\bin\posh-windows-amd64-experimental.exe --config C:\users\amol\.poshthemes\amol.omp.json --debug --shell pwsh
Here are the timings of segments in your prompt:
text(true) - 0 ms - █%c03█
git(false) - 17 ms -
text(true) - 0 ms - █%T█
exit(true) - 0 ms - █
PS C:\Users\amol> C:\bin\posh-windows-amd64.exe --config C:\users\amol\.poshthemes\amol.omp.json --debug --shell pwsh
█%c03█(text:0s)██(git:17.0074ms)█%T█(text:0s) █(exit:0s)
PS C:\Users\amol> measure-command {C:\bin\posh-windows-amd64.exe --config C:\users\amol\.poshthemes\amol.omp.json --debug --shell pwsh}
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 80
Ticks : 807655
TotalDays : 9.3478587962963E-07
TotalHours : 2.24348611111111E-05
TotalMinutes : 0.00134609166666667
TotalSeconds : 0.0807655
TotalMilliseconds : 80.7655
So the times measured by posh are almost the same for both versions.
If i'm correct changing to a custom Go build, should remove the 5 MS delay when it's cleaning up the processes, when you are not inside a git repo this should be once (The check "Is in git repo").
I replaced the original exe in my modules, and yes, this does remove that 5 MS delay.
18:02:07 ███Roy██E:█ ﮫ 1ms█C:\Users\Roy\Documents\PowerShell\Modules\oh-my-posh\3.68.0\bin\posh-windows-amd64.exe --debug --shell universal
Here are the timings of segments in your prompt:
session(true) - 0 ms - █Roy@HOMEPC-ROY█
path(true) - 0 ms - \█
git(false) - 41 ms -
battery(true) - 2 ms - █100
node(false) - 0 ms -
shell(true) - 0 ms - ﲵ universal█
root(true) - 0 ms - ██
text(true) - 0 ms - █NO CONFIG█
exit(true) - 0 ms - █
18:02:10 ███Roy██E:█ ﮫ 67ms█C:\Users\Roy\Documents\PowerShell\Modules\oh-my-posh\3.68.0\bin\posh-windows-amd64-2.exe --debug --shell universal
Here are the timings of segments in your prompt:
session(true) - 0 ms - █Roy@HOMEPC-ROY█
path(true) - 0 ms - \█
git(false) - 47 ms -
battery(true) - 3 ms - █100
node(false) - 0 ms -
shell(true) - 0 ms - ﲵ universal█
root(true) - 0 ms - ██
text(true) - 0 ms - █NO CONFIG█
exit(true) - 0 ms - █
When running it in the PowerToys Repo, it is within margin of error (and more dependant on the git performance itself).
ugh, ok I'm so dumb. I was running the powershell measurements in a shell that did not have git in the path. (Sidebar: maybe --debug should print if git was not found)
With git in the path, the measurements for both versions are almost identical at ~170ms.
I am still hallucinating that it feels faster in the new version though.
Edit: With Measure-command there is definitely a 40ms difference in the two, so that goes back to the startup/cleanup delays
Here is output with measure-command and --debug. It's interesting to see the 60-70ms overhead
PS H:\github\oh-my-posh3> measure-command { C:\bin\posh-windows-amd64.exe --config C:\users\amol\.poshthemes\amol.omp.json --debug --shell pwsh | Out-Default}
█%c03█(text:0s)█ main ≡  ~5 ?5█(git:164.0001ms)█%T█(text:0s) █(exit:0s)
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 231
Ticks : 2317607
TotalDays : 2.68241550925926E-06
TotalHours : 6.43779722222222E-05
TotalMinutes : 0.00386267833333333
TotalSeconds : 0.2317607
TotalMilliseconds : 231.7607
PS H:\github\oh-my-posh3> measure-command { C:\bin\posh-windows-amd64-experimental.exe --config C:\users\amol\.poshthemes\amol.omp.json --debug --shell pwsh | Out-Default}
Here are the timings of segments in your prompt:
text(true) - 0 ms - Γûê%c03Γûê
git(true) - 173 ms - █ main ≡  ~5 ?5█
text(true) - 0 ms - █%T█
exit(true) - 0 ms -  █
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 217
Ticks : 2170004
TotalDays : 2.5115787037037E-06
TotalHours : 6.02778888888889E-05
TotalMinutes : 0.00361667333333333
TotalSeconds : 0.2170004
TotalMilliseconds : 217.0004
@amoldeshpande that "old" version of OMP is also a bit outdated looking at the debug output. You might want to update that one to get a more up-to-date pov.
done. no idea where I picked up the old version since I only started using posh about a week ago :-) Anyway, no change in timings probably as expected.
done. no idea where I picked up the old version since I only started using posh about a week ago :-) Anyway, no change in timings probably as expected.
How did you install omp? scoop? pwsh?
done. no idea where I picked up the old version since I only started using posh about a week ago :-) Anyway, no change in timings probably as expected.
How did you install omp? scoop? pwsh?
Nope. just downloaded with webrequest from powershell. I have a fairly strong recollection of using 'releases/latest/download/...', but I could be wrong.
I just realized I also need to revert back to exec.Command("command").Output()
rather than the override to avoid calling process.Wait()
. Ill try to do that and compile with the fork. See if that makes a difference.
This build is with everything originally, but compiled with a go fork. posh-windows-amd64-experimental.exe.zip
PS H:\github\tcsh\win32> C:\bin\posh-windows-amd64-experimental.exe --debug --config C:\Users\amol\.poshthemes\amol.omp.json
Here are the timings of segments in your prompt:
text(true) - 0 ms - █%c03█
git(true) - 173 ms - █ unicode ≡█
text(true) - 0 ms - █%T█
exit(true) - 0 ms - █
PS H:\github\tcsh\win32> measure-command {C:\bin\posh-windows-amd64-experimental.exe --debug --config C:\Users\amol\.poshthemes\amol.omp.json}
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 225
Ticks : 2251493
TotalDays : 2.60589467592593E-06
TotalHours : 6.25414722222222E-05
TotalMinutes : 0.00375248833333333
TotalSeconds : 0.2251493
TotalMilliseconds : 225.1493
Seems about the same as the last version.
Yup. That's what I have as well. That does imply we can shave off a couple of MS but not a lot. Command execution is simply slower on Windows. That said, below 300ms to render a prompt is still smooth.
Spinning up processes is simply more expensive on Windows... I heard this is why .NET Framework introduced App Domains ages ago... Most of the benefits of process isolation, without expensive bit of being a separate process.
@JanDeDobbeleer have you considered what I said about parallelizing git commands ? I don't see the harm in executing all the git commands in parallel and then parsing the output based on conditional "git status" states as usual.
@amoldeshpande It's not so easy. A couple of commands can be executed in // but others depend on the context.
At least we can give it a try.
@amoldeshpande I'm going to give that a try next week. See where that brings us.
@TylerLeonhardt thanks for that context, I'm off Windows for a while now so there's a lot I'm missing.
Would it also be an idea, where appropriate, to replace an Cmd execution, with a small go function? I would expect this to be faster on both Windows & Linux.
For example, I think the check 'is in git folder' could be replaced with a simple check if there is a .Git folder in the current dir or any parent dir. Might be possible to replace some other cmd executions with some go code.
I think the az module could be changed as well to just read two config files!
As royvou mentions, another approach is removing as many external command calls as possible. Same principle as using the go-git
package, but with a simple manual implementation.
For example, the first git call is in the segment enable check, and only checks if the current working directory is inside a working tree. Replicating the same behavior using system calls through the os
package should be easy; just check each folder between working directory and root for a .git
subfolder. As far as I can tell, this is how the go-git
package handles their PlainOpen
function with DetectDotGit
specified.
I got a very basic proof-of-concept working here if anyone else wants to try it out/test it: https://github.com/shedric1/oh-my-posh3/tree/git-treewalk
Some pretty promising results:
...\posh-windows-amd64-git-treewalk.exe --config C:\Misc\Resources\customtheme.omp.json --debug
Here are the timings of segments in your prompt:
path(true) - 0 ms - C:\Misc█
git(false) - 20 ms -
exit(false) - 0 ms -
For reference, here are my results of running the faster-commands build without any changes:
...\posh-windows-amd64-faster-commands.exe --config C:\Misc\Resources\customtheme.omp.json --debug
Here are the timings of segments in your prompt:
path(true) - 0 ms - C:\Misc█
git(false) - 66 ms -
exit(false) - 0 ms -
The same ~40-60ms savings are present for both repo folders and non-repo folders alike. Might be too small to warrant the potential edge-cases, but the fewer external command executions the better. Comparing against the experimental build, it's a smaller ~20-40ms savings, but still (slightly) faster consistently.
Not sure if it will cause performance issues on other OSes where external commands aren't so slow, so any Linux/macOS comparisons would be appreciated!
EDIT: looking closer, the process of walking parent dirs for a git repo by definition would also return the repo root directory. The second external git call only returns this path, so we could kill two external function calls with one stone here fairly easily.
This sounds great! 2x ~40ms would be a huge bonus. And would get us closer to under the 100ms.
I also did a little expiriment with go git, and as Jan already mentioned the git status equivalent, is slow with big repositories with a lot of excluded files.
Parrelize the other few git commands (or check if some could be merged) would be a huge bonus.
For the Linux check I did (on my own branch) it didnt really matter as it was always 4-6ms.
These 3 seemed like the simplest candidates for native Go refactoring in the git segment:
{ROOT_REPO}/.git/logs/refs/stash
(if it exists)I pushed a new proof-of-concept branch trying these changes here: https://github.com/shedric1/oh-my-posh3/tree/native-git
Using all three gave the following results on my machine inside a git repo directory:
...\posh-windows-amd64-native-git.exe --config C:\Misc\Resources\customtheme.omp.json --debug
path(true) - 0 ms - C:\Misc\oh-my-posh3█
git(true) - 71 ms - :faster-commands ≡ ?2 2
exit(false) - 0 ms -
For reference, here's the experimental build from above:
...\posh-windows-amd64-experimental.exe --config C:\Misc\Resources\customtheme.omp.json --debug
path(true) - 0 ms - C:\Misc\oh-my-posh3█
git(true) - 208 ms - :faster-commands ≡ ?2 2
exit(false) - 0 ms -
Still needs polishing, but in general it's a big performance improvement.
Prerequisites
CONTRIBUTING
guideDescription
Slowness in rendering prompt. It's noticeably slow in Windows compared to WSL2, even in the same Terminal.
Environment
Steps to Reproduce
I was trying to figure out why composing the prompt is so slow on windows. I unfortunately don't know much go, but I muddled around with it and found a couple of things.
Firstly, running and parsing git commands seems quite slow.
As you can see here, running the raw git command takes less than half the time it takes to run a posh command where my only config is a git segment (the default one from your theme file)
There is no git repo in that directory (I was trying to get the fastest execution for the git segment, but interestingly the overall execution time of posh does not change even within a git repo)
I tried to do some profiling in go, and came up with this for execution:
I'm always running
where the theme only contains a only git prompt segment exactly like the jandedobbeleer profile
In a repo with git, this is the profile
So, it seems that running multiple git commands slows things down even more , as you'd expect.
With my less-than-zero knowledge of go, I can only theorize that
Love the tool, thanks for the great work !