JanDeDobbeleer / oh-my-posh

The most customisable and low-latency cross platform/shell prompt renderer
https://ohmyposh.dev
MIT License
16.79k stars 2.35k forks source link

Performance: Command execution slow on Windows #305

Closed amoldeshpande closed 3 years ago

amoldeshpande commented 3 years ago

Prerequisites

Description

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.

PS C:\Users\amol> Measure-Command {posh-windows-amd64.exe --shell zsh --config c:\users\amol\.poshthemes\amol.omp.json}

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 89
Ticks             : 895987
TotalDays         : 1.03702199074074E-06
TotalHours        : 2.48885277777778E-05
TotalMinutes      : 0.00149331166666667
TotalSeconds      : 0.0895987
TotalMilliseconds : 89.5987

PS C:\Users\amol> Measure-Command {c:\users\amol\appdata\local\Atlassian\SourceTree\git_local\bin\git.exe rev-parse --is-inside-work-tree }
fatal: Not a git repository (or any of the parent directories): .git

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 37
Ticks             : 376365
TotalDays         : 4.35607638888889E-07
TotalHours        : 1.04545833333333E-05
TotalMinutes      : 0.000627275
TotalSeconds      : 0.0376365
TotalMilliseconds : 37.6365

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

posh-windows-amd64.exe --shell zsh --config c:\users\amol\.poshthemes\amol.omp.json

where the theme only contains a only git prompt segment exactly like the jandedobbeleer profile

H:\github\oh-my-posh3\src>go tool pprof c:\Users\amol\foo
Type: cpu
Time: Jan 1, 2021 at 2:20pm (PST)
Duration: 201.11ms, Total samples = 60ms (29.83%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top10
Showing nodes accounting for 60ms, 100% of 60ms total
Showing top 10 nodes out of 30
      flat  flat%   sum%        cum   cum%
      50ms 83.33% 83.33%       50ms 83.33%  runtime.cgocall
      10ms 16.67%   100%       10ms 16.67%  os/exec.(*Cmd).Start
         0     0%   100%       30ms 50.00%  bufio.(*Reader).ReadLine
         0     0%   100%       30ms 50.00%  bufio.(*Reader).ReadSlice
         0     0%   100%       30ms 50.00%  bufio.(*Reader).fill
         0     0%   100%       30ms 50.00%  internal/poll.(*FD).Read
         0     0%   100%       60ms   100%  main.(*Segment).enabled (inline)
         0     0%   100%       60ms   100%  main.(*Segment).setStringValue
         0     0%   100%       60ms   100%  main.(*engine).setStringValues.func1
         0     0%   100%       10ms 16.67%  main.(*environment).hasCommand
(pprof) quit

In a repo with git, this is the profile

H:\github\oh-my-posh3\src>go tool pprof foo
Type: cpu
Time: Jan 1, 2021 at 2:08pm (PST)
Duration: 412.02ms, Total samples = 220ms (53.40%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top10
Showing nodes accounting for 220ms, 100% of 220ms total
Showing top 10 nodes out of 48
      flat  flat%   sum%        cum   cum%
     210ms 95.45% 95.45%      210ms 95.45%  runtime.cgocall
      10ms  4.55%   100%       10ms  4.55%  runtime.slicerunetostring
         0     0%   100%      150ms 68.18%  bufio.(*Reader).ReadLine
         0     0%   100%      150ms 68.18%  bufio.(*Reader).ReadSlice
         0     0%   100%      150ms 68.18%  bufio.(*Reader).fill
         0     0%   100%       30ms 13.64%  fmt.Fprintf
         0     0%   100%       30ms 13.64%  fmt.Printf
         0     0%   100%      150ms 68.18%  internal/poll.(*FD).Read
         0     0%   100%       30ms 13.64%  internal/poll.(*FD).Write
         0     0%   100%       30ms 13.64%  internal/poll.(*FD).writeConsole
(pprof)

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

  1. Perhaps parallelizing all the git commands will help responsiveness, instead of executing them sequentially. I saw a closed issue where you did this for segments, but I wasn't able to figure out if you tried just the git commands.
  2. Maybe there is an optimization to be made where instead of reading line by line, you read the entire buffer and then parse it line by line. This should help in situations where there is a lot of git output, I would think.

Love the tool, thanks for the great work !

amoldeshpande commented 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/

TylerLeonhardt commented 3 years ago

+1 to this. Here's a video of before and after (uploading as a zip because GitHub said I had to):

vid.zip

I don't expect it to be as fast as the before... but certainly not as slow as it is unfortunately.

amoldeshpande commented 3 years ago

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.

JanDeDobbeleer commented 3 years ago

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.

lnu commented 3 years ago

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.

JanDeDobbeleer commented 3 years ago

@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.

posh-windows-experimental-amd64.zip

royvou commented 3 years ago

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 :)

tradiff commented 3 years ago

Same error as royvou here image

JanDeDobbeleer commented 3 years ago

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 👍🏻

royvou commented 3 years ago

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 :(

lnu commented 3 years ago

Don’t forget that the segments processing is done in //. So the prompt is always as slow as the slowest segment(more or less).

JanDeDobbeleer commented 3 years ago

@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.

tradiff commented 3 years ago

1 is from main 2 is your experimental build image

JanDeDobbeleer commented 3 years ago

@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.

royvou commented 3 years ago

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 image

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.

amoldeshpande commented 3 years ago

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.

JanDeDobbeleer commented 3 years ago

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.

JanDeDobbeleer commented 3 years ago

Very, very fast in between everything. I could have made some mistakes here 😓 posh-windows-amd64-experimental.exe.zip

amoldeshpande commented 3 years ago

It's a LOT better ! Overall still a bit slower than Linux, but maybe parallelizing git commands will help with that ?

JanDeDobbeleer commented 3 years ago

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...

royvou commented 3 years ago

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?

amoldeshpande commented 3 years ago

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.

lnu commented 3 years ago

Now we have to wonder if the margin is enough to maintain an own fork of go?

JanDeDobbeleer commented 3 years ago

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.

lnu commented 3 years ago

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 //.

lnu commented 3 years ago

Am i missing something: image

amoldeshpande commented 3 years ago
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>
royvou commented 3 years ago

So far i can't say its any faster.

image

lnu commented 3 years ago
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

amoldeshpande commented 3 years ago

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.

amoldeshpande commented 3 years ago

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.

royvou commented 3 years ago

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).

amoldeshpande commented 3 years ago

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

amoldeshpande commented 3 years ago

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
JanDeDobbeleer commented 3 years ago

@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.

amoldeshpande commented 3 years ago

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.

lnu commented 3 years ago

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?

amoldeshpande commented 3 years ago

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.

JanDeDobbeleer commented 3 years ago

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.

JanDeDobbeleer commented 3 years ago

This build is with everything originally, but compiled with a go fork. posh-windows-amd64-experimental.exe.zip

amoldeshpande commented 3 years ago
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.

JanDeDobbeleer commented 3 years ago

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.

TylerLeonhardt commented 3 years ago

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.

amoldeshpande commented 3 years ago

@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.

lnu commented 3 years ago

@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.

JanDeDobbeleer commented 3 years ago

@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.

royvou commented 3 years ago

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!

shedric1 commented 3 years ago

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.

royvou commented 3 years ago

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.

shedric1 commented 3 years ago

These 3 seemed like the simplest candidates for native Go refactoring in the git segment:

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.