最近我回答了关于性能的另一个问题。利用PowerShell成功地找到了一种解决方案,并利用PowerShell内核验证了该方案的良好速度.
但是,在PowerShell 5.1中运行相同的脚本(S)会显示出完全不同的结果,而absolutely在性能改进方面更糟糕。此外,原始脚本运行速度超过4×慢于ISE。
因此,我的问题是:<#>where为如此巨大的差异提供了一个理由,在PowerShell脚本中我应该避免什么(或者相反,可能显式地加载.NET库或类似的东西)?
PowerShell-ISE:性能改进cca 10:1 (好):
.\cr\122635wrapper.ps1 -maxLoop 8;$Host.Name;$PSVersionTablemaxRange=7,lowCountThreshold=1,operators=“+-*/ permutations=4096 formatString=1{0}2{1}3{2}4{3}6{5}7”。7 269 756 19 574412详细: maxRange=7,lowCountThreshold=1,Operators=“+-*/ permutations=4096 formatString=”1{5}2{4}3{2}4{1}6{0}7“回答7284 839 1,911312 Windows PowerShell ISE主机名值.503 PSEdition桌面PSCompatibleVersions {1.0,BuildVersion 10.0.17763.503 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1
PowerShell核心:性能改进28:1 (优秀):
pwsh -noprofile -command .\cr\122635wrapper.ps1 -maxLoop 8;$Host.Name;$PSVersionTablemaxRange=7,lowCountThreshold=1,operators=“+-*/ permutations=4096 formatString=1{0}2{1}3{2}4{3}6{5}7”。7 269 756 22 0310531详细: maxRange=7,lowCountThreshold=1,Operators=“+-*/ permutations=4096 formatString=”1{5}2{4}3{2}4{1}6{0}7“回答7 284 839 0 7800336 ConsoleHost名称值6.2.0OS MicrosoftWindows10.0.17763平台Win32NT PSCompatibleVersions {1.0,2.0,3.0,4.0…} PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 WSManStackVersion 3.0
PowerShell 5.1:性能改进cca 4:3 (very可怜):
powershell -noprofile -command .\cr\122635wrapper.ps1 -maxLoop 8;$Host.Name;$PSVersionTablemaxRange=7,lowCountThreshold=1,operators=“+-*/ permutations=4096 formatString=1{0}2{1}3{2}4{3}6{5}7”。7 269 756 87,1714765详细: maxRange=7,lowCountThreshold=1,operators="+-*/“permutations=4096 formatString=”1{5}2{4}3{3}4{2}6{0}7“答案为7 284 839 64,888286 ConsoleHost名称值- PSVersion 5.1.17763.503 PSEdition Desktop PSCompatibleVersions {1.0,BuildVersion 10.0.17763.503 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1
包括代码片段从链接到关于性能的另一个问题:
122635responer.ps1脚本:
# [CmdletBinding(PositionalBinding=$false)] # slows script execution cca 50%
param ( # Variables
[parameter()] # [ValidateRange(3,20)] # ???
[int]$maxRange = 9,
[parameter()]
[int]$lowCountThreshold = 5,
[parameter()]
[ValidateNotNullOrEmpty()]
[string]$opString = '+-*/' # Mathematical Operators as string
)
Begin {
Set-StrictMode -Version Latest
# cast $operators variable as an array of characters
$operators = [char[]]$opString
$opsCount = $operators.Count
# Define the number range for calculations. 13 would make for the largest values 13!. Cap the script as 13
$maxRangeMinus1 = $maxRange - 1
# Build an array for extending
$maxOpsArray = 1..$maxRange
for ( $i=0; $i -lt $maxRange; $i++ ) {
$maxOpsArray[$maxRangeMinus1 -$i] = ,$operators[0] * $i
}
# Build the format string that will be used for invoking.
# Will look like 1{2}2{1}3{0}4. Acting as place holders for mathematic operators
[string]$formatString = -join (1..($maxRangeMinus1) |
ForEach-Object{"$_{$([int]$maxRangeMinus1 - $_)}"}) + $maxRange # reverse order
# ForEach-Object{"$_{$([int]$_ - 1)}"}) + $maxRange # $range[-1] # ascending order
# ascending order would require `[array]::Reverse($newOperatorArr)` below in the process loop
if ( $maxRange -gt 11 ) {
# force decimal computing in following `$DataTable.Compute( $mathString, '')`
$formatString = $formatString.Replace('{','.0{') + '.0'
}
# Determine the number of possible permutations of those operators inbetween the number set.
[int64]$permutations = [System.Math]::Pow($opsCount, $maxRangeMinus1)
# Narrow down $alphanumerics array size to necessary count
$alphanumerics = $([char[]]'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
)[0..($opsCount -1)]
Write-Verbose -Verbose -Message `
("maxRange=$maxRange, lowCountThreshold=$lowCountThreshold, operators=""$(-join $operators)""" `
+ "`r`npermutations=$permutations formatString=""$formatString""")
$DataTable=[System.Data.DataTable]::new()
$Error.Clear() # for debugging purposes
}
Process {
# Cycle each permutation. Use `for` loop instead of `0..($permutations - 1) | ForEach-Object`
$( for ( $i=0; $i -lt $permutations; $i++ ) {
# Build an array of operators:
# ( based on the number converted to base `$opsCount` )
$Number = $i
$newOperatorArr = @( $( do {
$Remainder = $Number % $opsCount
# Get the associated character
$operators[$Remainder]
$Number = ($Number - $Remainder) / $opsCount
} while ($Number -gt 0) ))
# Extend array of operators to appropriate length if necessary
if ( $newOperatorArr.Count -lt $maxRangeMinus1 ) {
$newOperatorArr += $maxOpsArray[$newOperatorArr.Count]
}
### [array]::Reverse($newOperatorArr) # only if `$formatString` is in ascending order
$mathString = $formatString -f @( $newOperatorArr )
# evaluate math expression using the Compute method of the DataTable class
# rather than time consuming Invoke-Expression
$value122635 = $DataTable.Compute( $mathString, '')
# Effectively reduce the output size in advance: refuse "non-integers"
if ( $value122635 -eq [System.Math]::Floor($value122635) ) {
# Build an object that contains the result and the mathematical expression
[pscustomobject]@{
Expression = $mathString
Value = [System.Math]::Floor($value122635) # [int64]$value122635
}
}
# Since this take a while try and give the user some semblance of progress.
Write-Progress -Activity "Performing mathematical calculations" `
-Status "Please wait." -PercentComplete (100 * $i / $permutations) `
-CurrentOperation "$([math]::Round(100 * $i / $permutations))% Completed."
# Only give group results
} ) | Group-Object Value |
Where-Object{$_.Count -ge $lowCountThreshold} |
Sort-Object -property Count <# -Descending <##>, @{Expression = {[int]$_.Name} }122635wrapper.ps1 脚本:
param (
[parameter()]
[ValidateRange(8,13)]
[int]$maxLoop = 12
)
$y = (Measure-Command {$x = D:\PShell\CR\122635.ps1}).TotalSeconds
$z = ($x | Measure-Object -Property Count -Sum).Sum
'orig. {0,4} {1,9} {2,9} {3,16}' -f 7, $x.count, $z, $y
for ( $icnt=7; $icnt -lt $maxLoop; $icnt++ ) {
$y = (Measure-Command {
$x = D:\PShell\CR\122635answer.ps1 -maxRange $icnt -lowCountThreshold 1
}).TotalSeconds
$z = ($x | Measure-Object -Property Count -Sum).Sum
'answer {0,4} {1,9} {2,9} {3,16}' -f $icnt, $x.count, $z, $y
if ($icnt -eq 7) {''}
}The原始 122635.ps1脚本:
function ConvertTo-Base
{
[CmdletBinding()]
param (
[parameter(ValueFromPipeline=$true,Mandatory=$True, HelpMessage="Base10 Integer number to convert to another base")]
[int]$Number=1000,
[parameter(Mandatory=$True)]
[ValidateRange(2,20)]
[int]$Base
)
[char[]]$alphanumerics = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
do
{
# Determine the remainder
$Remainder = ($Number % $Base)
# Get the associated character and add it to the beginning of the string.
$newBaseValue = "$($alphanumerics[$Remainder])$newBaseValue"
# Adjust the number to remove the calculated portion
$Number = ($Number - $Remainder) / $Base
# Loop until we have processed the whole number
} while ($Number -gt 0)
return $newBaseValue
}
# Variables
$maxRange = 3 #13 would make for the largest values 13!. Cap the script as 13
$lowCountThreshold = 1 # Only show group results where the match exists more than five times.
# Mathematical Operators
[char[]]$operators = "+-*/"
# Define the number range for calculations. 13 would make for the largest values 13!. Cap the script as 13
$range = 1..$maxRange
# Build the format string that will be used for invoking. Will look like 1{0}2{1}3. Acting as place holders for mathematic operators
$formatString = -join (1..($range.count - 1) | ForEach-Object{"$_{$([int]$_ - 1)}"}) + $range[-1]
# Determine the number of possible permutations of those operators inbetween the number set.
$permutations = [System.Math]::Pow($operators.Count,$range.count - 1)
# Cycle each permutation
0..($permutations - 1) | ForEach-Object{
# Convert the number to a base equal to the element count in operators. Use those values to represent the index of the operators array.
$mathString = $formatString -f @([string[]][char[]]((ConvertTo-Base -Number $_ -Base $operators.Count).PadLeft($range.count - 1,"0")) | ForEach-Object{$operators[[int]$_]})
# Build an object that contains the result and the mathematical expression
[pscustomobject]@{
Expression = $mathString
Value = Invoke-Expression $mathString
}
# Since this take a while try and give the user some semblance of progress.
Write-Progress -Activity "Performing mathematical calculations" -Status "Please wait." -PercentComplete ($_ / $permutations * 100) -CurrentOperation "$([math]::Round($_ / $permutations * 100))% Completed."
# Filter for whole number and only give group results
} | Where-Object{$_.Value -is [int32]} | Group-Object Value | Where-Object{$_.Count -ge $lowCountThreshold} | Sort-Object Count -Descending发布于 2019-05-27 07:52:19
通常,为了找出代码的哪一部分是慢的,您可以在分析器中运行它。然而,不幸的是,PowerShell没有分析器,所以我们必须使用其他方法。找到慢的部分有多种方法。
您可以在您的代码中放置计时代码,以度量各个部件所需的时间。.NET 秒表类在这里可能会有所帮助。
另一种简单的方法就是简单地将代码中的一些切碎,直到它加速。我就是这么用你的密码的。
我尝试的第一件事是对Write-Progress进行评论。这似乎是一个可能的候选人,因为在ISE和控制台是不同的。当我这么做的时候,代码立刻加速了。看来这就是罪魁祸首。
你要做的就是少打电话。你可以这样做:
# Show progress bar. We don't call Write-Progress each time through the loop
# because that is slow.
if ($i % 1000 -eq 0)
{
Write-Progress -Activity "Performing mathematical calculations" `
-Status "Please wait." -PercentComplete (100 * $i / $permutations) `
-CurrentOperation "$([math]::Round(100 * $i / $permutations))% Completed."
}https://codereview.stackexchange.com/questions/221082
复制相似问题