ドットソーシングは、単にファイルコンテンツを読み取るよりも遅いですか?


13

さまざまなソースファイル(関数ごとに1つの.ps1ファイル)から関数定義を取り込むPowerShellモジュールを作成しました。これにより、(チームとして)異なる機能を並行して作業できます。モジュール(.psm1ファイル)は、使用可能な.ps1ファイルのリストを取得します...

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

...次にリストをループし、ドットソースを介して各関数定義を取り込みます:

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

問題:これを完了する速度は、テストするマシンによって異なりますが、約50のソースファイルに対して10〜180秒と大幅に異なることがあります。所要時間の大幅な変動を説明することはできません。また、マシンの種類、OS、ユーザーアカウント、管理者権限、PSプロファイル、PSバージョンなどの変数を制御していると考えられます。ある日から次の日へのユーザー。

これがディスクアクセスの問題かどうか疑問に思い、ディスクから簡単に読み取ることができるかどうかをテストしました。Get-Contentこれらすべてのファイルでの実行は非常に高速であることが判明しました。これは、問題の回避策として利用しました。

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

ドットソースを介してこれらの関数を追加するのは、ファイルコンテンツを読み取って実行するよりもずっと遅いのはなぜですか?

回答:


17

科学のセットアップ

まず、これをテストするのに役立ついくつかのスクリプト。これにより、2000個のスクリプトファイルが生成されます。各スクリプトファイルには1つの小さな機能があります。

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

通常の起動時のオーバーヘッドがそれほど問題にならないようにするには、これで十分なはずです。必要に応じて追加できます。これにより、すべてがドットソーシングを使用してロードされます。

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

これは、最初にコンテンツを読み込むことですべてロードします:

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

次に、PowerShellがどのように機能するかを真剣に検査する必要があります。逆コンパイラにはJetBrains dotPeekが好きです。PowerShellを.NETアプリケーション埋め込むことを試みたことがあれば、関連するもののほとんどを含むアセンブリが.NETアプリケーションであることがわかりますSystem.Management.Automation。その1つをプロジェクトとPDBに逆コンパイルします。

この不思議な時間がすべて費やされている場所を確認するために、プロファイラーを使用します。Visual Studioに組み込まれているものが好きです。使い方はとても簡単です。PDBを含むフォルダーをシンボルの場所に追加します。これで、テストスクリプトの1つを実行するPowerShellインスタンスのプロファイリングを実行できます。(-File最初に試行するスクリプトの完全パスで使用するコマンドラインパラメーターを設定します。起動場所を、すべての小さなスクリプトを含むフォルダーに設定します。)それが完了したら、[ powershell.exeターゲット]の下のエントリのプロパティを開き、変更します他のスクリプトを使用するための引数。次に、Performance Explorerの一番上の項目を右クリックして、「プロファイリング開始」を選択します。プロファイラーは、他のスクリプトを使用して再度実行されます。これで比較できます。オプションが指定されている場合は、[すべてのコードを表示]をクリックしてください。私にとっては、サンプルプロファイリングレポートの概要ビューの通知領域に表示されます。

結果が出ます

私のマシンでは、Get-Contentバージョンが2000スクリプトファイルを処理するのに9秒かかりました。「ホットパス」の重要な機能は次のとおりです。

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

これは非常に理にかなっています。Get-Contentディスクからコンテンツを読み取るのを待たなければならずInvoke-Expression、それらのコンテンツを利用するのを待たなければなりません。

ドットソース版では、私のマシンはこれらのファイルを処理するのに15秒強かかりました。今回は、ホットパスの関数はネイティブメソッドでした。

WinVerifyTrust
CodeAuthzFullyQualifyFilename

そこにある2番目のものは文書化されていないように見えますが、WinVerifyTrust「指定されたオブジェクトに対して信頼検証アクションを実行します」。それはあなたが得ることができるほど曖昧ですが、言い換えれば、その機能は特定のプロバイダーを使用して特定のリソースの信頼性を検証します。PowerShellの高度なセキュリティ機能を有効にしていないことに注意してくださいUnrestricted。スクリプト実行ポリシーはです。

それが意味すること

要するに、実行を許可されているスクリプトを制限しない場合には必要ではないのですが、何らかの方法で各ファイルが何らかの方法で検証されるのを待っています。いつgcその後、iex内容、コンソールでの関数を入力したようにそれはですので、確認するために、何のリソースがありません。


2
ベン、この素晴らしい答えをありがとう。あなたが逆コンパイルまで行ったことに感銘を受けました。これは、私が試したことよりも一歩進んだものです。この問題が最も深刻なマシンの1つでテスト方法を実行できる方法があるかどうかを確認します。これには時間がかかる可能性があるため、息を止めないでください!
チャーリージョイント
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.