PowerShell 脚本效率

3
我尽可能地使用PowerShell来完成快速简便的脚本任务;在我的工作中,我经常使用它来进行数据解析、日志文件筛选或创建CSV\文本文件。
但有一件事情我不太理解,为什么执行某些数据\IO任务时可能效率非常低。我想这可能与它处理流水线或者我还没有理解的某些东西有关。
如果您采用以下逻辑生成ABC123 ID,将其编译成PowerShell并执行,那么它将在1分钟内完成:
$source = @'
    public static System.Collections.Generic.List<String> GetIds()
    {
        System.Collections.Generic.List<String> retValue = new System.Collections.Generic.List<String>();
        for (int left = 97; left < 123; left++)
        {
            for (int middle = 97; middle < 123; middle++)
            {
                for (int right = 97; right < 123; right++)
                {
                    for (int i = 1; i < 1000; i++)
                    {
                        String tmp = String.Format("{0}{1}{2}000", (char)left, (char)middle, (char)right);
                        retValue.Add(String.Format("{0}{1}", tmp.Substring(0, tmp.Length - i.ToString().Length), i));
                    }
                }
            }
        }
        return retValue;
    }
'@
$util = Add-Type -Name "Utils" -MemberDefinition $source -PassThru -Language CSharp

$start = get-date
$ret = $util::GetIds()
Write-Host ("Time: {0} minutes" -f ((get-date)-$start).TotalMinutes)

现在采用相同的逻辑,通过 PowerShell 运行而不编译为程序集,则需要数小时才能完成。

$start = Get-Date
$retValue = @()
for ($left = 97; $left -lt 123; $left++)
{ 
    for ($middle = 97; $middle -lt 123; $middle++)
    { 
        for ($right = 97; $right -lt 123; $right++)
        { 
            for ($i = 1; $i -lt 1000; $i++)
            { 
                $tmp = ("{0}{1}{2}000" -f [char]$left, [char]$middle, [char]$right)
                $retValue += ("{0}{1}" -f $tmp.Substring(0, $tmp.Length - $i.ToString().Length), $i)
            }
        }
    }
}
Write-Host ("Time: {0} minutes" -f ((get-date)-$start).TotalMinutes)

为什么会这样呢?我使用了过多的类型转换或低效的操作导致了性能变慢吗?

1个回答

4

你在这里正在拖慢你的性能:

$retValue += ("{0}{1}" -f $tmp.Substring(0, $tmp.Length - $i.ToString().Length), $i)

数组添加是一种非常“昂贵”的操作。你所做的基本上是每次创建一个全新的数组,由原始数组加上新元素组成。

编辑:这种数组添加不仅效率低下,而且完全没有必要。你只需要将这些值输出到管道中,并将结果赋值回变量即可。

$start = Get-Date
$retValue =
for ($left = 97; $left -lt 123; $left++)
{ 
    for ($middle = 97; $middle -lt 123; $middle++)
    { 
        for ($right = 97; $right -lt 123; $right++)
        { 
            for ($i = 1; $i -lt 1000; $i++)
            { 
                $tmp = ("{0}{1}{2}000" -f [char]$left, [char]$middle, [char]$right)
                "{0}{1}" -f $tmp.Substring(0, $tmp.Length - $i.ToString().Length), $i
            }
        }
    }
}
Write-Host ("Time: {0} minutes" -f ((get-date)-$start).TotalMinutes)
Time: 1.866812045 minutes

非常好!现在很有意义。我假设 System.Collections.Generic.List<String> 不是与 @() 相同的类型。System.Collections.Generic.List<String>.Add 方法是否执行数组扩展?再次感谢! - The Unique Paul Smith
通用列表的 .add() 方法比数组加法更高效。在有效添加元素方面,arralist 类型似乎是最快的,但通常只需要在管道已经用于其他对象的情况下进行偶然的累积时才需要这样做。 - mjolinor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接