Repository navigation
Closed
Labels
Issue-BugIssue has been identified as a bug in the productIssue has been identified as a bug in the productResolution-FixedThe issue is fixed.The issue is fixed.WG-Engine-Performancecore PowerShell engine, interpreter, and runtime performancecore PowerShell engine, interpreter, and runtime performance
Description
Steps to reproduce
[Byte[]]$Value = [System.IO.File]::ReadAllBytes('C:\Some10MBfile')
# ~1000ms for 10MB array
# ~0.5ms for 1byte array
function MandType {
Param (
[parameter(Mandatory=$True)]
[Byte[]]$Value
)
}
# ~0.3ms for 10MB array
function TypeOnly {
Param (
[parameter()]
[Byte[]]$Value
)
}
# ~0.3 for 10MB array
function MandOnly {
Param (
[parameter(Mandatory=$True)]
$Value
)
}
(Measure-Command { MandType $Value}).TotalMilliseconds
(Measure-Command { MandType ([Byte[]]@(0))}).TotalMilliseconds
(Measure-Command { TypeOnly $Value}).TotalMilliseconds
(Measure-Command { MandOnly $Value}).TotalMilliseconds
Expected behavior
All versions of the above functions should behave identically and complete immediately, regardless of of parameter size as the functions perform no functions.
1.1
1.1
1.1
1.1
Actual behavior
When setting a parameter to Mandatory, specifying its Type, and passing a large array in, the function will pause for some amount of time before starting.
1000
1.1
1.1
1.1
Environment data
PS C:\> $PSVersionTable
Name Value
---- -----
PSVersion 6.0.0-beta.9
PSEdition Core
GitCommitId v6.0.0-beta.9
OS Microsoft Windows 10.0.16299
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
PS C:\> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.16299.19
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.16299.19
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1Metadata
Metadata
Assignees
Labels
Issue-BugIssue has been identified as a bug in the productIssue has been identified as a bug in the productResolution-FixedThe issue is fixed.The issue is fixed.WG-Engine-Performancecore PowerShell engine, interpreter, and runtime performancecore PowerShell engine, interpreter, and runtime performance
Activity
markekraus commentedon Nov 11, 2017
I can repro this in Core on Windows, macOS, and Linux. Very odd.
also, just to be thorough I tested with function below just to see if there was a difference between explicit or implicit
mandatory=$falseand did not see the same issue.daxian-dbw commentedon Nov 11, 2017
Mandatorycauses the parameter binder to check for NULL elements in an array argument (code here), which causes this perf issue.It also happens when the
[ValidateNotNull]attribute is applied to the parameter.The parameter binder shouldn't blindly check null elements. In the case of this repro, the element type of the argument array is value type, and thus cannot have null element.
iSazonov commentedon Nov 11, 2017
Dongbo Wang (@daxian-dbw) Do we use new label
buginstead ofIssue-Bug?iSazonov commentedon Nov 11, 2017
What should the attributes be applied to? Argument or its elements? Collection or its elements?
markekraus commentedon Nov 11, 2017
IMO,
[ValidateNotNull()]and[ValidateNotNullorEmpty()]would apply to the collection, not its elements... but... erm... maybe not all types. like[String[]]for example.. that one I could see users assuming it applies to elements. but for[Bytes[]]I would expect that to work on the collection, not the elements..iSazonov commentedon Nov 11, 2017
We could disable recursion here to set last parameter in false and in ValidateNullOrEmptyArgument add empty collection check.
lzybkr commentedon Nov 11, 2017
For
ValueFromPipelinearguments, it's important thatValidate...applies to members of the collection because the recommended pattern is to always accept a collection so that the following work similarly:It would be a little weird if
Validate...did not apply to members of a collection if the parameter were notValueFromPipeline.iSazonov commentedon Nov 11, 2017
I'd expect that we convert
Do-Something -InputObject $collectionto$collection | Do-SomethingifInputObjectis a scalar and that we applyValidate...to members in this case, otherwise ifInputObjectis a collection we applyValidate...to the collection.lzybkr commentedon Nov 11, 2017
Ilya (@iSazonov) - do you mean
InputObjectis typed as a collection, or that the value is a collection?I believe conversions are performed before validation - as this example demonstrates:
Because you git this error - showing clearly the conversion happened and validation failed on the empty strings:
So when using the usual pattern for pipeline parameters - use a strongly typed collection for
InputObject, it doesn't matter if you've passed a scalar or collection, the value will have been converted to a collection before validating.daxian-dbw commentedon Nov 12, 2017
I think we should skip checking elements for collections with a value-type element type. For collections with a value-type element type, the element cannot be
$null, so in this case, just check if the collection is empty would be sufficient.iSazonov commentedon Nov 14, 2017
Jason Shirk (@lzybkr)
My thoughts was about:
- if we convertfoo -InputObject $varto$var | foowe could use one code path (to work precisely similarly) and eliminate the internal cycle inProcessRecord()forInputObject`.iSazonov commentedon Nov 14, 2017
Jason Shirk (@lzybkr) Dongbo Wang (@daxian-dbw) We have an inconsistence with
ValueFromPipelinearguments.daxian-dbw commentedon Nov 14, 2017
The not-null-or-empty check of a mandatory parameter is based on the parameter type, while the check for
ValidateNotNullOrEmptyAttributeorValidateNotNullAttribute(and other validate attributes too) are based on the argument only (the attribute itself doesn't have the information about the type of the parameter/variable/property it applies to).iSazonov commentedon Nov 15, 2017
I believe we want "works similarly", don't we?