0

Microsoft technet建议[Math]::Floor([int]$a / [int]$b)进行整数除法。我相信[int][Math]::Floor($a / $b)由于少了一次强制转换操作,它的可读性和性能都更高。我已经证明这两种方法是等效的。但是,我无法获得一致的结果。我的方法包括将这两种方法重复 10,000 次并使用Measure-Command cmdlet 测量结果。但是,不能构建一个测试重复执行比另一个测试更好的测试。我的代码如下:

Write-Host
$loopLength = 10000

$runtime = Measure-Command {
    1..$loopLength | ForEach-Object {
        Foreach ($divisor in 2,3,5,7) {
            [Math]::Floor([int]$_ / [int]$divisor) > $null
        }
    }
}

"Double Cast: $($runtime.TotalMilliSeconds)"

$runtime = Measure-Command {
    1..$loopLength | ForEach-Object {
        Foreach ($divisor in 2,3,5,7) {
            [int][Math]::Floor($_ / $divisor) > $null           
        }
    }
}
"Single Cast: $($runtime.TotalMilliSeconds)"

如何修改我的代码,以便获得一致的结果,证明一种方法比另一种更好。

4

3 回答 3

1

对我来说,这种性能优化并不是很重要。PowerShell 本身比编译语言慢得多,所以如果你真的需要性能,请使用编译语言或使用Add-Type.

除此之外——如果你测试性能,你需要最少的其他代码来改变结果。Foreach-Object本身增加了自己的复杂性。这就是为什么我建议改用foreach语句的原因。

令人惊讶的是,我机器上的结果有时是相反的..

[76]: $loopLength = 100000
[77]:
[77]: $runtime = Measure-Command {
>>     foreach($i in 1..$loopLength) {
>>         Foreach ($divisor in 2,3,5,7) {
>>             [Math]::Floor([int]$i / [int]$divisor) > $null
>>         }
>>     }
>> }
>>
[78]: "Double Cast: $($runtime.TotalMilliSeconds)"
Double Cast: 16294.3328
[79]:
[79]: $runtime = Measure-Command {
>>     foreach($i in 1..$loopLength) {
>>         Foreach ($divisor in 2,3,5,7) {
>>             [int][Math]::Floor($i / $divisor) > $null
>>         }
>>     }
>> }
>> "Single Cast: $($runtime.TotalMilliSeconds)"
>>
Single Cast: 15924.3836
于 2012-01-16T07:38:09.137 回答
1

ForEach通过使用良好的老式For循环,我还能够将示例中的时间缩短大约 7 倍;尽管最快和最慢之间的差异仍然只有 20 毫秒左右。

但是,您还应该注意非整数输入的两个转换位置的细微操作差异:

  • [Math]::Floor([int]17.2 / [int]1.1) = 17
  • [int][Math]::Floor(17.2 / 1.1) = 15

平均 For-Loop 代码:

$Spins       = 100
$loopLength  = 10000
$Denominator = 2,3,5,7

$runtime = Measure-Command {
   for($s=0;$s -lt $Spins;$s++){
    for($Numerator=1;$Numerator -le $loopLength; $Numerator++){
        For($i=0;$i -lt 4;$i++) {
            $null=[Math]::Floor([int]$Numerator / [int]$Denominator[$i])
        }
    }
   }
}

"Double Cast: $($runtime.TotalMilliSeconds/$Spins)"

$runtime = Measure-Command {
   for($s=0;$s -lt $Spins;$s++){
    for($Numerator=1;$Numerator -le $loopLength; $Numerator++){
        For($i=0;$i -lt 4;$i++) {
            $null=[int][Math]::Floor($Numerator / $Denominator[$i])
        }
    }
   }
}
"Single Cast: $($runtime.TotalMilliSeconds/$Spins)"

add-type -TypeDefinition @'
public class intOps{
   public static ulong div(ulong a, ulong b){
      return a/b;
   }
}
'@
$runtime = Measure-Command {
   for($s=0;$s -lt $Spins;$s++){
    for($Numerator=1;$Numerator -le $loopLength; $Numerator++){
        For($i=0;$i -lt 4;$i++) {
            $null=[intOps]::div($Numerator,$Denominator[$i])
        }
    }
   }
}
"C# Cast: $($runtime.TotalMilliSeconds/$Spins)"
于 2020-09-17T00:26:47.420 回答
0

TechNet 上的示例有点傻,因为数字已经是 type 了System.Int32。看看这个例子:

PS C:\Users\andy> [math]::floor( 100 / 26 ).GetType().Fullname
System.Double
PS C:\Users\andy> (100).GetType().FullName
System.Int32
PS C:\Users\andy> [int].FullName
System.Int32

所以完全没有必要把[int]Floor 方法参数放在前面,因为它们已经是 type 了System.Int32

此外,您也不希望将返回的值强制System.Double转换为 an Int32,因为返回值可能大于 anInt32可以容纳的值。例如:

PS C:\Users\andy> [int][math]::floor( ([int]::MaxValue + 1) / 1 )
Cannot convert value "2147483648" to type "System.Int32". Error: "Value was either too large or too small for an Int32."

至于性能,速度上的差异可以忽略不计。无论您是否愿意,PowerShell 引擎都会在幕后执行大量类型调整和强制转换……它是这样设计的,因此系统管理员不必过多担心 int、double、小数等问题……一个数字是一个数字对吗?;-) 例如:

[Math]::Floor("123")
# This outputs 123 as System.Double.

这甚至不会在 C# 中编译。PowerShell 运行时执行必要的转换以满足 Floor 方法签名。

另一个例子:

"2" / "1"
# This outputs 2 as System.Int32.

字符串无法进行除法,但 PowerShell 引擎会在后台进行转换,以便您完成这项工作。

以下是我机器的性能结果:

function Get-SingleCastTime {
    $runtime = Measure-Command {
        1..10000 | ForEach-Object {
            Foreach ($divisor in 2,3,5,7) {
                [int][Math]::Floor($_ / $divisor) > $null          
            }
        }
    }
    "Single Cast: $($runtime.TotalMilliSeconds)"
}

function Get-DoubleCastTime {
    $runtime = Measure-Command {
        1..10000 | ForEach-Object {
            Foreach ($divisor in 2,3,5,7) {
                [Math]::Floor([int]$_ / [int]$divisor) > $null
            }
        }
    }

    "Double Cast: $($runtime.TotalMilliSeconds)"
}

Get-SingleCastTime
#Single Cast: 614.6537

Get-DoubleCastTime
#Double Cast: 545.2668

Get-DoubleCastTime
#Double Cast: 514.2103

Get-SingleCastTime
#Single Cast: 526.9188
于 2012-01-15T04:31:41.633 回答