-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Use fast set location at startup #10401
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
Conversation
What about the Azure Cloud Shell, which starts by default in the |
I guess it is loaded after initial startup and also it is remoting scenario. |
|
@bergmeister I'm pretty sure CloudShell uses the PowerShell profile to create the Azure drive, logon, and Set-Location so all that is after pwsh startup anyways. |
|
Perhaps this code can still be improved, but it also works for Linux where I can’t debug :-( |
|
@TravisEz13 and @adityapatwardhan Given you two have experiences with FileSystemProvider related code, could you please review this PR when you have time? Thanks! |
| { | ||
| // See if the path is a relative or absolute | ||
| // path. | ||
| if (Globber.IsAbsolutePath(path, out string driveName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel this could be optimized too but I don't know how this works on Unix and I afraid to make breaking change.
Additional thought is that the branch works only on Unix and at startup we possibly already initialized current drive and no need to repeat the step.
TravisEz13
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@daxian-dbw I only have experience with the location globber.
| PathInfo current = null; | ||
| if (PublicSessionState.InvokeCommand.LocationChangedAction != null) | ||
| { | ||
| // It is under a condition to avoid the unneeded allocation - CurrentLocation always allocates. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand your comment. The two parts of the sentence seem to conflict. How about this:
| // It is under a condition to avoid the unneeded allocation - CurrentLocation always allocates. | |
| // Allocate a new location using the CurrentLocation property |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want protect the condition from removing because CurrentLocation always allocates and we should avoid the allocation if we don't use CurrentLocation in code below. Your proposal doesn't reflect this.
What about "CurrentLocation always allocates and we should avoid the allocation if we don't use CurrentLocation later."?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can understand that much better. Please use that.
src/System.Management.Automation/engine/SessionStateLocationAPIs.cs
Outdated
Show resolved
Hide resolved
…Is.cs Co-Authored-By: Travis Plunk <[email protected]>
This reverts commit c264379.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm generally concerned about the risk of changing the provider code for perf purpose, especially when the gain is insignificant, as the code can be exercised in so many different ways, it's hard for us to enumerate all the scenarios that might be affected.
I think it's worth for the committee to take this PR as case for discussion, to think about what the guidelines/balance should be for performance changes and sensitive code.
| } | ||
| } | ||
|
|
||
| string currentPath = path.Substring(CurrentDrive.Root.Length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see there would be problem to this line. Say CurrentDrive is the Temp drive, and the path passed is Temp:\blah, then this Substring call will throw exception.
PS:121> $temp.Root
C:\Users\AUser\AppData\Local\Temp\
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If current drive is Temp:/ we always call IsAbsolutePath() and always set current drive based on the path. I checked this for shorter code path that is ResetRunspaceState(). Other code paths call previously Bind(), there is a call SetSessionStateDrive(), then again SetSessionStateDrive() and we fall in IsAbsolutePath() too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the following repro:
cd temp:\
mkdir test
$t =$ExecutionContext.SessionState.GetType()
$p = $t.GetProperties("nonpublic, instance")
$p = $p[0]
$v = $p.GetValue($ExecutionContext.SessionState)
$t = $v.GetType()
## Get the existing 'SetLocation(string)' method
$m1 = $t.GetMethod("SetLocation", [System.Reflection.BindingFlags]"nonpublic, instance", $null, [type[]]@([string]), $null)
## Success. Location is set to 'Temp:\test'
$m1.Invoke($v, @("Temp:\test"))
## Get the new 'SetLocationFileSystemFast' method and invoke it.
$m2 = $t.GetMethod("SetLocationFileSystemFast", [System.Reflection.BindingFlags]"nonpublic, instance")
## Fail with 'ArgumentOutOfRangeException'
$m2.Invoke($v, @("Temp:\test"))
> Exception calling "Invoke" with "2" argument(s): "startIndex cannot be larger than length of string. (Parameter 'startIndex')"I know you have examined the current existing call sites of this method and know they are safe, but the fast-code-path method is internal, and it could be used by someone in other places in future, without knowing the prerequisites or assumptions for this method to work. This is not the right pattern to follow, it will results code hard to maintain.
|
@PowerShell/powershell-committee reviewed this one, and unfortunately we think we're going to have to reject it.
|
It is not true. We always call the code with
My intention was and remains to go through all startup code and remove delays where possible. I would not be surprised if in the end we would get a 20-30 percent increase in performance. (Is startup scenario important?) Is this enough to continue the work? |
Again, no other entry points exists today doesn't mean it won't in future, because the new API is internal. This is not a good pattern to follow.
This was already done by #10416, wasn't it? And that PR has been accepted. |
|
I rebased to get #10416 in the branch only now. :-) I see we set a location only once at startup. There is still a comment about AppContainer and I don't still understand how address this after removing globbing code. It seems we need to check that Directory.GetCurrentDirectory() exists? Thank you for great comments and help! |
My concerns are not just it being internal, but in general that it's risky to change the provider code. We cannot enumerate all possible scenarios that exercise the code path. If I can quickly find one potential problem just by looking at the code (which I'm not familiar with), then that's a signal that there may be more issues with the changes that are unknown yet. My personal principles on performance work are:
|
|
@daxian-dbw Thanks! Sorry if I noisy. In the PR we do not do behavioral changes in the PR. There are only two scenarios where we set a folder location (that only the PR care). Last notice is that single scenario where set location is important is startup one because we want get right prompt. Remaining is not important because we use OS current path for initialization and it is not predictable and reliable source in multi runspace environment which means that nobody should lean on the value and set needed location explicitly in current runspace. Now мy only concern is with AppContainer scenario, which is mentioned in the comments. I do not understand how this can happen and how debug it. It looks like Directory.GetCurrentDirectory() returns nonexistent value in AppContainer. |
|
@joeyaiello @daxian-dbw measure-Command {
for( $i=0; $i -lt 100; $i++) {
[runspace] $rs1 = [runspacefactory]::CreateRunspace()
[runspace] $rs2 = [runspacefactory]::CreateRunspace()
[runspace] $rs3 = [runspacefactory]::CreateRunspace()
[runspace] $rs4 = [runspacefactory]::CreateRunspace()
[runspace] $rs5 = [runspacefactory]::CreateRunspace()
[runspace] $rs6 = [runspacefactory]::CreateRunspace()
[runspace] $rs7 = [runspacefactory]::CreateRunspace()
[runspace] $rs8 = [runspacefactory]::CreateRunspace()
[runspace] $rs9 = [runspacefactory]::CreateRunspace()
[runspace] $rs10 = [runspacefactory]::CreateRunspace()
$rs1.Open()
$rs2.Open()
$rs3.Open()
$rs4.Open()
$rs5.Open()
$rs6.Open()
$rs7.Open()
$rs8.Open()
$rs9.Open()
$rs10.Open()
$rs1.Dispose()
$rs2.Dispose()
$rs3.Dispose()
$rs4.Dispose()
$rs5.Dispose()
$rs6.Dispose()
$rs7.Dispose()
$rs8.Dispose()
$rs9.Dispose()
$rs10.Dispose()
}
}Before the change: After the change: That is 41.5% for the samples (~8 ms per 1 runspace). |
|
Interesting. I will take a close look this week. Currently working on driving the preview.4 release. |
@iSazonov I don't see new commits here. Are there new commits in your branch that are not shown here? |
|
@daxian-dbw Closed PR doesn't show new commits. :-) |
|
@daxian-dbw @SteveL-MSFT I'd want to bring back the change. I still believe it is useful for startup and related scenarios. |
|
@PoshChan Please remind me in 24 hours |
|
@daxian-dbw, this is the reminder you requested 24 hours ago |

PR Summary
At startup we create initial runspace and set default location to current file system location (path).
For that we use generic SetLocation() which designed to work with all providers and do many extra checks/operations.
In the startup scenario we know that the path is valid file system path so we can use more simple and more fast set location.
On my system I get win ~10ms or ~1.62% and ~8Mb or ~3%.

The win comes from removing some extra file operations:
PR Context
Related #6443
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.