Il dot-sourcing è più lento della semplice lettura del contenuto del file?


13

Ho scritto un modulo PowerShell che estrae le definizioni delle funzioni da diversi file di origine (ovvero un file .ps1 per funzione). Questo ci consente (come gruppo) di lavorare su diverse funzioni in parallelo. Il modulo (file .psm1) ottiene l'elenco dei file .ps1 disponibili ...

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

... quindi scorre l'elenco e inserisce la definizione di ciascuna funzione tramite dot-sourcing:

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

Problema: abbiamo notato che la velocità con cui questo viene completato può variare molto, da 10 a 180 secondi per circa 50 file sorgente, a seconda della macchina su cui testiamo. Non possiamo spiegare l'ampia variazione del tempo impiegato e riteniamo di aver controllato per variabili come il tipo di macchina, il sistema operativo, l'account utente, le autorizzazioni di amministratore, il profilo PS, la versione PS, ecc. Il tempo impiegato può variare sullo stesso host per lo stesso utente da un giorno all'altro.

Ci siamo chiesti se questo fosse un problema con l'accesso al disco e testato quanto velocemente potevamo semplicemente leggere dal disco. Si scopre che correre Get-Contentsu tutti quei file è stato molto veloce, di cui abbiamo approfittato in una soluzione alternativa al problema:

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

Perché l'aggiunta di queste funzioni tramite dot-sourcing è molto più lenta della lettura e dell'esecuzione del contenuto del file?

Risposte:


17

Preparare la scienza

Innanzitutto, alcuni script per aiutarci a testare questo. Questo genera 2000 file di script, ognuno con una singola piccola funzione:

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

Dovrebbe essere sufficiente per non importare troppo il normale sovraccarico di avvio. Puoi aggiungere altro se vuoi. Questo li carica tutti usando dot-sourcing:

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

Questo li carica tutti leggendo prima il loro contenuto:

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

Ora dobbiamo fare alcune ispezioni serie su come funziona PowerShell. Mi piace JetBrains dotPeek per un decompilatore. Se hai mai provato a incorporare PowerShell in un'applicazione .NET , scoprirai che è l'assembly che include la maggior parte delle cose rilevanti System.Management.Automation. Decompila quello in un progetto e un PDB.

Per vedere dove viene trascorso tutto questo tempo misterioso, useremo un profiler. Mi piace quello integrato in Visual Studio. È molto facile da usare . Aggiungi la cartella contenente il PDB nelle posizioni dei simboli . Ora, possiamo eseguire un'esecuzione di profilatura di un'istanza di PowerShell che esegue solo uno degli script di test. (Impostare i parametri della riga di comando da utilizzare -Filecon il percorso completo del primo script da provare. Impostare la posizione di avvio sulla cartella contenente tutti gli script minuscoli.) Una volta fatto quello, aprire le Proprietà sulla powershell.exevoce in Target e modificare gli argomenti per usare l'altro script. Quindi fare clic con il pulsante destro del mouse sull'elemento più in alto in Performance Explorer e selezionare Avvia profilatura. Il profiler viene eseguito nuovamente utilizzando l'altro script. Ora possiamo confrontare. Assicurati di fare clic su "Mostra tutto il codice" se ti viene data l'opzione; per me, che appare in un'area Notifiche nella vista Riepilogo del Rapporto di profilazione di esempio.

I risultati arrivano

Sul mio computer, la Get-Contentversione ha impiegato 9 secondi per passare attraverso i file di script 2000. Le funzioni importanti sul "Hot Path" erano:

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

Questo ha molto senso: dobbiamo aspettare per Get-Contentleggere il contenuto dal disco e dobbiamo aspettare per Invoke-Expressionfare uso di tali contenuti.

Nella versione dot-source, la mia macchina impiegava poco più di 15 secondi a lavorare su quei file. Questa volta, le funzioni su Hot Path erano metodi nativi:

WinVerifyTrust
CodeAuthzFullyQualifyFilename

Il secondo sembra non essere documentato, ma WinVerifyTrust"esegue un'azione di verifica della fiducia su un oggetto specificato". È più vago che puoi ottenere, ma in altre parole, quella funzione verifica l'autenticità di una determinata risorsa utilizzando un determinato provider. Si noti che non ho abilitato alcuna roba di sicurezza sofisticata per PowerShell e la mia politica di esecuzione degli script è Unrestricted.

Cosa significa

In breve, stai aspettando che ogni file venga verificato in qualche modo, probabilmente controllato per una firma, anche se questo non è necessario quando non limiti gli script che possono essere eseguiti. Quando tu gce poi iexi contenuti, è come se avessi digitato le funzioni sulla console, quindi non c'è risorsa da verificare.


2
Ben, grazie per questa superba risposta. Mi ha colpito il fatto che tu sia arrivato al punto di decompilare, il che è un passo oltre tutto ciò che ho provato. Vedrò se è possibile seguire il metodo di prova su una delle macchine in cui questo problema è più acuto. Questo potrebbe richiedere molto tempo, quindi non trattenere il respiro!
Charlie Joynt,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.