Not all commands. ls
takes 20 seconds. dir
is nearly instant. This is just the simplest example, but the overall experience makes me want to stop using powershell on Linux. :-( Tab completion also takes around 20 seconds.
PS > measure-command { ls }
Days : 0
Hours : 0
Minutes : 0
Seconds : 20
Milliseconds : 25
Ticks : 200252179
TotalDays : 0.000231773355324074
TotalHours : 0.00556256052777778
TotalMinutes : 0.333753631666667
TotalSeconds : 20.0252179
TotalMilliseconds : 20025.2179
PS > measure-command { dir }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 3
Ticks : 33095
TotalDays : 3.83043981481481E-08
TotalHours : 9.19305555555556E-07
TotalMinutes : 5.51583333333333E-05
TotalSeconds : 0.0033095
TotalMilliseconds : 3.3095
ls takes a second or so.
ls takes 20 seconds.
> $PSVersionTable
Name Value
---- -----
PSVersion 6.1.0-preview.1
PSEdition Core
GitCommitId v6.1.0-preview.1
OS Linux 4.9.0-6-amd64 #1 SMP Debian 4.9.82-1+deb9u3 (2018-03-02)
Platform Unix
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
@SurferJeffAtGoogle Can you run Get-Command ls
and Get-Command dir
to see what commands are actually being executed? We don't install the "ls -> Get-ChildItem" alias on Linux by default so you should just be running the native /bin/ls
(or whatever's in your path). We do ship the "dir->Get-ChildItem" alias but you say that's OK which is confusing.
What's surprising is that calling get-command ls
also takes 20 seconds!
PS > get-command ls
CommandType Name Version Source
----------- ---- ------- ------
Application ls 0.0.0.0 /bin/ls
PS > get-command dir
CommandType Name Version Source
----------- ---- ------- ------
Alias dir -> Get-ChildItem
PS > measure-command { get-command ls }
Days : 0
Hours : 0
Minutes : 0
Seconds : 20
Milliseconds : 35
Ticks : 200359003
TotalDays : 0.000231896994212963
TotalHours : 0.00556552786111111
TotalMinutes : 0.333931671666667
TotalSeconds : 20.0359003
TotalMilliseconds : 20035.9003
But with a bash shell, the equivalent of get-command
takes hardly any time:
$ which ls
/bin/ls
$ time which ls
/bin/ls
real 0m0.003s
user 0m0.000s
sys 0m0.000s
I have experiencing the same issue sporadically in both Windows and Linux. Sometimes is fast and other time slow.
(?)
@SurferJeffAtGoogle What happens when you launch ls with a fully qualified path
/bin/ls
Also, can you try other executables like true
true
/bin/true
Finally, what does your path look like in PowerShell
$ENV:PATH
If it's mangled, then that would explain both slow launching and slow Get-Command
calls.
Launching with the full path runs quick:
PS > measure-command { ls }
Days : 0
Hours : 0
Minutes : 0
Seconds : 20
Milliseconds : 62
Ticks : 200625091
TotalDays : 0.000232204966435185
TotalHours : 0.00557291919444444
TotalMinutes : 0.334375151666667
TotalSeconds : 20.0625091
TotalMilliseconds : 20062.5091
PS > measure-command { /bin/ls }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 6
Ticks : 66497
TotalDays : 7.69641203703704E-08
TotalHours : 1.84713888888889E-06
TotalMinutes : 0.000110828333333333
TotalSeconds : 0.0066497
TotalMilliseconds : 6.6497
PS > measure-command { true }
Days : 0
Hours : 0
Minutes : 0
Seconds : 20
Milliseconds : 30
Ticks : 200306653
TotalDays : 0.000231836403935185
TotalHours : 0.00556407369444444
TotalMinutes : 0.333844421666667
TotalSeconds : 20.0306653
TotalMilliseconds : 20030.6653
PS > measure-command { /bin/true }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 15
Ticks : 155612
TotalDays : 1.80106481481481E-07
TotalHours : 4.32255555555556E-06
TotalMinutes : 0.000259353333333333
TotalSeconds : 0.0155612
TotalMilliseconds : 15.5612
Here's my path. It's identical in bash, and bash does not experience the same slowness.
PS /usr/local/google/home/rennie/tmp> ls
PS /usr/local/google/home/rennie/tmp> /bin/ls
PS /usr/local/google/home/rennie/tmp> $env:PATH
/opt/microsoft/powershell/6.1.0~preview.1:/usr/local/google/home/rennie/Downloads/google-cloud-sdk/bin:/usr/local/google/home/rennie/Downloads/phantomjs-1.9.8-linux-x86_64/bin:/usr/local/google/home/rennie/.composer/vendor/bin:/usr/local/google/home/rennie/bin:/usr/local/google/home/rennie/.phpenv/bin:/usr/local/google/home/rennie/Downloads/dotnet-2.0.5:/usr/local/google/home/rennie/Downloads/dotnet-1.1.2:/usr/local/google/home/rennie/Downloads/apache-maven-3.3.9/bin:/usr/local/google/home/rennie/Downloads/node-v4.2.1-linux-x64/bin:/usr/local/google/home/rennie/Downloads/google-cloud-sdk/bin:/usr/local/google/home/rennie/.composer/vendor/bin:/usr/local/google/home/rennie/bin:/usr/local/google/home/rennie/.phpenv/bin:/usr/local/google/home/rennie/Downloads/dotnet-2.0.5:/usr/local/google/home/rennie/Downloads/dotnet-1.1.2:/usr/local/google/home/rennie/Downloads/apache-maven-3.3.9/bin:/usr/local/google/home/rennie/Downloads/node-v4.2.1-linux-x64/bin:/usr/lib/google-golang/bin:/usr/local/buildtools/java/jdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PS /usr/local/google/home/rennie/tmp>
rennie@rennie:~/tmp$
rennie@rennie:~/tmp$ env | grep ^PATH
PATH=/usr/local/google/home/rennie/Downloads/google-cloud-sdk/bin:/usr/local/google/home/rennie/Downloads/phantomjs-1.9.8-linux-x86_64/bin:/usr/local/google/home/rennie/.composer/vendor/bin:/usr/local/google/home/rennie/bin:/usr/local/google/home/rennie/.phpenv/bin:/usr/local/google/home/rennie/Downloads/dotnet-2.0.5:/usr/local/google/home/rennie/Downloads/dotnet-1.1.2:/usr/local/google/home/rennie/Downloads/apache-maven-3.3.9/bin:/usr/local/google/home/rennie/Downloads/node-v4.2.1-linux-x64/bin:/usr/local/google/home/rennie/Downloads/google-cloud-sdk/bin:/usr/local/google/home/rennie/.composer/vendor/bin:/usr/local/google/home/rennie/bin:/usr/local/google/home/rennie/.phpenv/bin:/usr/local/google/home/rennie/Downloads/dotnet-2.0.5:/usr/local/google/home/rennie/Downloads/dotnet-1.1.2:/usr/local/google/home/rennie/Downloads/apache-maven-3.3.9/bin:/usr/local/google/home/rennie/Downloads/node-v4.2.1-linux-x64/bin:/usr/lib/google-golang/bin:/usr/local/buildtools/java/jdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
rennie@rennie:~/tmp$
Even when I reduce my path to almost nothing, the performance is similar:
PS /usr/local/google/home/rennie/tmp> $env:PATH
$env:PATH
/opt/microsoft/powershell/6.1.0~preview.1:/usr/bin
PS /usr/local/google/home/rennie/tmp> measure-command { ls }
ls : The term 'ls' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:19
+ measure-command { ls }
+ ~~
+ CategoryInfo : ObjectNotFound: (ls:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Days : 0
Hours : 0
Minutes : 0
Seconds : 40
Milliseconds : 122
Ticks : 401220615
TotalDays : 0.000464375711805556
TotalHours : 0.0111450170833333
TotalMinutes : 0.668701025
TotalSeconds : 40.1220615
TotalMilliseconds : 40122.0615
PS /usr/local/google/home/rennie/tmp> measure-command { /bin/ls }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 14
Ticks : 141501
TotalDays : 1.63774305555556E-07
TotalHours : 3.93058333333333E-06
TotalMinutes : 0.000235835
TotalSeconds : 0.0141501
TotalMilliseconds : 14.1501
PS /usr/local/google/home/rennie/tmp>
@SurferJeffAtGoogle Is this still an issue with 6.2?
I haven't tried 6.2 yet:
PS /usr/local/google/home/rennie> $PSVersionTable.PSVersion
Major Minor Patch PreReleaseLabel BuildLabel
6 1 3
PS /usr/local/google/home/rennie> measure-command { clear }
Days : 0
Hours : 0
Minutes : 0
Seconds : 14
Milliseconds : 249
Ticks : 142493659
TotalDays : 0.000164923216435185
TotalHours : 0.00395815719444444
TotalMinutes : 0.237489431666667
TotalSeconds : 14.2493659
TotalMilliseconds : 14249.3659
On Sat, Mar 23, 2019 at 12:06 PM Steve Lee notifications@github.com wrote:
@SurferJeffAtGoogle https://github.com/SurferJeffAtGoogle Is this still
an issue with 6.2?—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/PowerShell/PowerShell/issues/6577#issuecomment-475896182,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ALO54srnStQZaWiQRpKutZjzkwd1KVeLks5vZns3gaJpZM4TJN34
.
@SurferJeffAtGoogle I setup a new Debian 9 VM on my macBook Pro. Installed 6.1.3, and executed:
measure-command { clear }
That finished in 46ms
I suspect my corp environment has some quirks that don't exist elsewhere.
Is there a tool I can run to trace where PowerShell is spending its time?
On Mon, Mar 25, 2019 at 11:27 AM Steve Lee notifications@github.com wrote:
@SurferJeffAtGoogle https://github.com/SurferJeffAtGoogle I setup a new
Debian 9 VM on my macBook Pro. Installed 6.1.3, and executed:measure-command { clear }
That finished in 46ms
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/PowerShell/PowerShell/issues/6577#issuecomment-476322368,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ALO54iKThLfzku3XDHnNqrJg21avv2nWks5vaRUHgaJpZM4TJN34
.
Would you be able to clone my branch from https://github.com/PowerShell/PowerShell/pull/9230 which adds timestamps to the trace command and do this:
trace-command -Name * -Expression { true } -PSHost
And see where it's spending its time?
Thank you. I'll try that. But it could take a few days due to my other
commitments.
On Tue, Mar 26, 2019 at 11:22 AM Steve Lee notifications@github.com wrote:
Would you be able to clone my branch from #9230
https://github.com/PowerShell/PowerShell/pull/9230 which adds
timestamps to the trace command and do this:trace-command -Name * -Expression { true } -PSHost
And see where it's spending its time?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/PowerShell/PowerShell/issues/6577#issuecomment-476784404,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ALO54t30ivILSl7Yc95tMSSjBCra0SuRks5vamVNgaJpZM4TJN34
.
@SurferJeffAtGoogle Thanks! No rush, not much we can do for 6.2 right now anyways.
Fellow Googler here, with similar corp setup.
The problem seems to be that we have symlinks in /usr/bin
, pointing to files on slow filesystems. In zsh I can see ls --color=never /usr/bin
is instant but ls ---color=always /usr/bin
takes seconds (presumably resolving symlinks to print them in them red if invalid). I can confirm by looking at trace-command
output that the time is spent "looking for true in /usr/bin" and removing /usr/bin
from $env:PATH
fixes the issue.
Thank you @bendowski! That helps.
I see this as a pain point, but this seems to do with the filesystem perf and not something PowerShell can do without someone coming up with a proposal
For the record: other shells (bash, zsh) work fine in this setup. I can only guess they don't check the executable bit (or other file attributes) ahead of time for every file on the PATH, only when needed to actually run a binary.
@bendowski ok, that's something we could potentially do as an optimization!
We look files by paths from PATH environment variable and by executable extentions in https://github.com/PowerShell/PowerShell/blob/48db3de03ed427f10646f1fbb824a2b4eb958378/src/System.Management.Automation/engine/CommandPathSearch.cs#L408
We could try new API FileSystemEnumerable() in the method where we can use custom predicate to filter files in directory but it is another scenario (Suggestions, or FuzzyMatch).
Also we see possible perf issue in the algorithm. We use search pattern like "ping.exe." and then the same for all extensions - "ping.exe.ps1", "ping.exe.cpl" and so on, but it seems the first pattern overlaps the follow ones: if we do not find first then it makes no sense check rest.
@SteveL-MSFT If I remember correctly PATHEXT is only for Windows. So
there is no need to check extensions on Unix-s, right?
@SteveL-MSFT We have a perf regression on Windows. For Get-Command ping
__~3 ms__ on my system.
Test script:
for ($i=0; $i -lt 2000; $i++) {
Get-Command ping > $null
}
On Windows PowerShell 5.1 and up to PowerShell Core 6.1.0-Preview2 I get __~2.5 sec__.
Starting with PowerShell Core 6.1.0-Preview2 I get __~8.2 sec__.
I compare traces for PowerShell Core 6.1.0-Preview2 and PowerShell Core 6.1.0-Preview3
You can see that there other P/Invokes are used in Preview3.
I guess it was a change in .Net Core. If so I guess they know about this and accepted consciously.
Should we worry about this or can we ignore it?
Update: Maybe on Unix we have have the same root of the regression?
@iSazonov yes, PATHEXT is a Windows only thing. On non-Windows, the executable bit is what is interesting although we special case .ps1. The perf data is different from what this issue is discussing which is specific to traversing symlinks opening files at discovery time rather than at runtime.
@SteveL-MSFT Thanks! Clear about PATHEXT.
My trace is for discovery code. I think just the enumeration (Directory.EnumerateFiles()) is slow, not exec bit check.
Our exec bit check is _on last step_ for single file (after we enumerate all files in a directory and filter by name) so if we remove them it does not help.
@bendowski @SurferJeffAtGoogle Please download compiled artifact from #10815 and look if you see any perf change.
@iSazonov Can you open a corefx issue about Directory.EnumerateFiles()
? That perf regression is really bad and not expected.
@0xd4d PowerShell repo steps is not useful for Core team. This requires a C# repo. I didn't have time to check it out (on different .Net Core versions).
@iSazonov I opened a corefx issue about that problem https://github.com/dotnet/corefx/issues/41898
I'm sorry for the delay. I tried the build you linked as well as the latest preview (7.0.0-preview5) and they seem to behave the same. It's much better than before (with ls taking 20 seconds), but still some commands take longer than I would expect. This seems to be somewhat non-deterministic, see this example of running "true" 4 times in a row:
PS /> measure-command { true }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 28
Ticks : 289087
TotalDays : 3.34591435185185E-07
TotalHours : 8.03019444444444E-06
TotalMinutes : 0.000481811666666667
TotalSeconds : 0.0289087
TotalMilliseconds : 28.9087
PS /> measure-command { true }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 443
Ticks : 4435776
TotalDays : 5.134E-06
TotalHours : 0.000123216
TotalMinutes : 0.00739296
TotalSeconds : 0.4435776
TotalMilliseconds : 443.5776
PS /> measure-command { true }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 34
Ticks : 346932
TotalDays : 4.01541666666667E-07
TotalHours : 9.637E-06
TotalMinutes : 0.00057822
TotalSeconds : 0.0346932
TotalMilliseconds : 34.6932
PS /> measure-command { true }
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 422
Ticks : 4221326
TotalDays : 4.88579398148148E-06
TotalHours : 0.000117259055555556
TotalMinutes : 0.00703554333333333
TotalSeconds : 0.4221326
TotalMilliseconds : 422.1326
Doing the same with trace-command
confirms the additional ~400ms is spent in /usr/bin:
DEBUG: 2019-10-24 13:53:38.2773 CommandDiscovery Information: 0 : Looking for true.ps1 in /usr/sbin
DEBUG: 2019-10-24 13:53:38.2784 CommandSearch Information: 0 : WriteLine Current directory results are invalid
DEBUG: 2019-10-24 13:53:38.2785 CommandSearch Information: 0 : WriteLine Current patterns exhausted in current directory: /usr/sbin
DEBUG: 2019-10-24 13:53:38.2785 CommandDiscovery Information: 0 : Looking for true in /usr/bin
DEBUG: 2019-10-24 13:53:38.6796 CommandSearch Information: 0 : WriteLine Current directory results are invalid
DEBUG: 2019-10-24 13:53:38.6798 CommandDiscovery Information: 0 : Looking for true.ps1 in /usr/bin
DEBUG: 2019-10-24 13:53:38.6914 CommandSearch Information: 0 : WriteLine Current directory results are invalid
DEBUG: 2019-10-24 13:53:38.6914 CommandSearch Information: 0 : WriteLine Current patterns exhausted in current directory: /usr/bin
Would love to see if someone could reproduce this one again on .NET 5 with the latest version of Win10. I know a ton of work has been done around filesystem access and symlinks in both of those products.
Most helpful comment
@iSazonov I opened a corefx issue about that problem https://github.com/dotnet/corefx/issues/41898