【问题标题】:PowerShell execution speed improvementPowerShell 执行速度提升
【发布时间】:2018-08-25 08:19:03
【问题描述】:

昨晚我玩弄了一些用 PowerShell 编写的排序算法。事先我必须生成一个随机整数数组,这样我就有了可以排序的东西。但是,生成数组的时间太长了,所以我改进了我的代码以尽可能快地运行。

我的最终代码如下所示:

$max = 1000000
$RndNumberArr = New-Object System.Collections.ArrayList
$ran = New-Object Random
for ($i = 0; $i -lt $max; $i++)
{ 
    [void]$RndNumberArr.Add($ran.Next(0,$max))    
}

使用 Measure-Command,我的平均执行时间约为 2.5 秒。

我注意到使用ArrayList 比使用固定大小的数组更快(这对我来说没有多大意义,有更快的方法吗?)

此外,使用 .NET 方法 random.Next() 而不是 CMDLet Get-Random 也可以大大提高速度(节省了大约 30 秒)。

使用 .NET 方法有什么缺点吗?

我也认为使用

[void]$RndNumberArr.Add($ran.Next(0,$max)) 

防止Array.Add输出比使用略快

$RndNumberArr.Add($ran.Next(0,$max)) > $null

我说的对吗?

CMDLet 有明显的时差是最慢的。

$RndNumberArr.Add($ran.Next(0,$max)) | Out-Null

我的计算机硬件(尤其是 CPU)是否会影响 PowerShell 的执行速度,或者速度是否在某个时候受到了限制?如果是这样,有没有办法增强分配的系统资源以提高速度?

非常感谢。

【问题讨论】:

  • 不,除了可读性和“最佳实践”之外,使用 .net 方法没有任何缺点。但是,当您像现在这样进行性能改进时,有时它们是获得改进的唯一方法。
  • 顺便说一句:注意this GitHub feature request 以添加对Get-Random cmdlet 创建多个值的支持。

标签: arrays performance powershell arraylist


【解决方案1】:

Maximilian Burszley's helpful answer 提供了最快的解决方案,原因有两个:

  • 它让 PowerShell 通过隐式收集来自 foreach 循环的所有输出来为您构造数组。

  • 它使用..range operator,结合foreach (1..$max) 而不是for 循环与算术;而具有范围语法的 foreach 循环会消耗更多内存 - 索引数组必须预先构造为一个整体 - 它明显快于算术 for 循环。

    • 作为一般规则,由 PowerShell 引擎本身执行的任何操作都比由用户代码执行的要好得多。

我注意到使用 ArrayList 比使用固定大小的 Array 更快

这是因为 .NET 数组是 固定大小 集合,因此您不能添加元素到数组中以迭代地构造它.

PowerShell 通过允许您执行 $array += ... 向您隐藏了这一事实,但在幕后所做的是每次都创建一个 new 数组,其中包含所有原始元素加上新元素。因此,在具有多次迭代的循环中,每次迭代都必须创建一个新数组,这会导致性能非常差

This GitHub issue 提出了一个潜在的未来增强功能,允许以 PowerShell 惯用的方式构建固有的可扩展列表。

另外,我认为使用 [void]$RndNumberArr.Add($ran.Next(0,$max)) 防止Array.Add 输出比使用稍快 $RndNumberArr.Add($ran.Next(0,$max)) > $null 我说的对吗?

是的,表达式的输出[void] 更快,但您应该考虑使用同样快速的$null = ... 来代替语法方便,因为= 有最低的运算符优先级,因此您不必担心将 RHS 包含在 (...) 中,这与 [void] 不同,因为 casts 具有高优先级。


使用 .NET 方法有什么缺点吗?

让我们先说明优势

能够调用几乎任何 .NET 类型的方法是 PowerShell 的一项出色的可扩展性功能 - 每当 PowerShell 不通过其 cmdlet/函数和运算符提供功能或不提供足够的性能时,.NET 类型的方法通常会。

也就是说, 缺点

  • 在语法上,方法语法(...) 围绕在参数列表周围,以,-分隔)可能会与 PowerShell 的参数模式(命令)语法混淆 (shell-like: no (...) 在参数周围,它们是 空格 - 分隔的); Set-StrictMode -Version 2 或更高版本可以帮助您检测无意中尝试使用方法语法调用 PowerShell cmdlet/函数。

  • 虽然很少发生,但由于 PowerShell 的后期绑定性质,在以后的 .NET 框架版本中引入的新方法重载可能会破坏现有代码:如果新的重载恰好导致现有代码绑定到它们,事情可能会中断。

  • .NET 类型方法的帮助未与 PowerShell 的帮助系统集成


我的计算机硬件(尤其是 CPU)是否会影响 PowerShell 的执行速度,或者速度是否在某个时候受到了限制?如果是这样,有没有办法增强分配的系统资源以提高速度?

据我所知,没有限制 CPU 使用,但我无法说明是否/如何使用多个 CPU/内核 - 如果有人知道,请告诉我们。

一般来说,如果适合给定的任务,并行化可能会加快速度,有几种方法可以做到这一点:

  • 后台作业Start-Job一起使用

  • 作为一种更快的替代方法,使用 PowerShell SDK 为每个进程创建多个运行空间,这是社区提供的 PoshRSJob module 以封装良好的 PowerShell 惯用形式提供的。

  • 在 Windows PowerShell 中,工作流提供并行执行。

  • 最后——再次,如果对手头的任务是可行的——您可以使用Invoke-Command将任务分散到多台计算机上并行执行。

【讨论】:

    【解决方案2】:

    如果您的目标是生成大量随机整数列表,我建议完全避免列表管理:

    $r = [Random]::new()
    $max = 1000000
    $list = foreach ($i in 1..$max)
    {
        $r.Next(0, $max)
    }
    

    与使用 for 循环相比,此代码的运行速度几乎是两倍(大约 1.4-1.5 秒,而 2.6 秒)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-21
      • 2019-06-17
      • 1970-01-01
      • 2011-05-14
      • 2013-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多