点源比仅仅读取文件内容慢吗?

点源比仅仅读取文件内容慢吗?

我编写了一个 PowerShell 模块,它从不同的源文件(即每个函数一个 .ps1 文件)中提取函数定义。这允许我们(作为一个团队)并行处理不同的函数。模块(.psm1 文件)获取可用 .ps1 文件的列表...

$Functions = Get-ChildItem -Path $FunctionPath *.ps1

...然后循环遍历列表并通过点源提取每个函数定义:

foreach($Function in $Functions) {
  . $Function.Fullname                                     # Can be slow
}

问题:我们注意到,完成此操作的速度可能相差很大,大约 50 个源文件需要 10 到 180 秒,具体取决于我们在哪台机器上进行测试。我们无法解释所用时间的巨大差异,并且相信我们已经控制了机器类型、操作系统、用户帐户、管理员权限、PS 配置文件、PS 版本等变量。在同一台主机上,同一用户每天所用的时间可能会有所不同。

我们确实怀疑这是否是磁盘访问的问题,并测试了从磁盘读取的速度。事实证明,运行Get-Content所有这些文件的速度非常快,我们利用这一点解决了这个问题:

foreach($Function in $Functions) {
  Invoke-Expression (Get-Content $Function.Fullname -Raw)  # Is quick
}

为什么通过点源添加这些功能比读取和执行文件内容慢得多?

答案1

建立科学

首先,一些脚本可以帮助我们测试这一点。这将生成 2000 个脚本文件,每个文件都有一个小函数:

1..2000 | % { "Function Test$_(`$someArg) { Return `$someArg * $_ }" > "test$_.ps1" }

这应该足以使正常的启动开销不会太过重要。您可以根据需要添加更多。这将使用点源加载它们:

dir test*.ps1 | % {. $_.FullName}

这将通过首先读取其内容来加载它们:

dir test*.ps1 | % {iex (gc $_.FullName -Raw)}

现在我们需要认真检查一下 PowerShell 的工作原理。我喜欢JetBrains dotPeek反编译器。如果你曾经尝试过在 .NET 应用程序中嵌入 PowerShell,你会发现包含大部分相关内容的程序集是System.Management.Automation。将其反编译为一个项目和一个PDB。

为了查看这些神秘的时间都花在了哪里,我们将使用分析器。我喜欢 Visual Studio 内置的分析器。它非常便于使用. 将包含 PDB 的文件夹添加到符号位置。现在,我们可以对只运行其中一个测试脚本的 PowerShell 实例进行性能分析运行。(设置要使用的命令行参数-File以及要尝试的第一个脚本的完整路径。将启动位置设置为包含所有小脚本的文件夹。)完成后,打开powershell.exe目标下条目的属性并更改参数以使用其他脚本。然后右键单击性能资源管理器中最上面的项目并选择开始剖析。分析器使用另一个脚本再次运行。现在我们可以进行比较了。如果有选项,请确保单击“显示所有代码”;对我来说,它会显示在示例分析报告的摘要视图中的通知区域中。

结果出来了

在我的计算机上,该Get-Content版本花了 9 秒钟浏览 2000 个脚本文件。“热路径”上的重要功能包括:

Microsoft.PowerShell.Commands.GetContentCommand.ProcessRecord
Microsoft.PowerShell.Commands.InvokeExpressionCommand.ProcessRecord

这很有意义:我们必须等待Get-Content从磁盘读取内容,并且我们必须等待Invoke-Expression才能使用这些内容。

在 dot-source 版本上,我的机器花了 15 多秒来处理这些文件。这次,Hot Path 上的函数是本机方法:

WinVerifyTrust
CodeAuthzFullyQualifyFilename

第二个似乎没有记录,但是WinVerifyTrust“对指定对象执行信任验证操作”。这几乎是最模糊的说法了,但换句话说,该函数使用给定的提供程序验证给定资源的真实性。请注意,我没有为 PowerShell 启用任何花哨的安全功能,我的脚本执行策略是Unrestricted

那意味着什么

简而言之,您正在等待每个文件以某种方式进行验证,可能还会检查签名,尽管当您不限制允许运行的脚本时,这不是必需的。当您gc查看iex内容时,就像您在控制台上输入了函数一样,因此没有资源需要验证。

相关内容