darallium's tech blog
home
blog
tech

qiita: @darallium
github: darallium
twitter: /darallium/
instagram: yu_kyu_n
email
Home tech lazypwsh-profiler
  • LazyPwsh Profiler

    1. 目次
    2. 結論
    3. 解説
    4. autoload
    5. lazyload

    PowerShellのプロファイル (Microsoft.PowerShell_profile.ps1) に多くの関数や設定を記述すると、単一のスクリプトに大量にコードを書くことになり、管理が複雑になる。 このために複数ファイルに機能を分割し、論理分割を行うことで管理を簡単にする方法があるが、PowerShellにおいて他ファイルスクリプトの実行は大きな処理であるため、起動が遅くなる問題が知られている この問題に対処するため、スクリプトを「即時読み込み」と「遅延読み込み」に分割する仕組みを導入した。

    これにより、30スクリプト程度に分割された状態で、591ms の起動時間を実現した。

    結論

    ~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1 に以下のスクリプトを記載する。

    $psdir="$ENV:HOMEDRIVE$ENV:HOMEPATH\.config\PowerShell"
    $autoloadPath = "$psdir\autoload"
    $lazyloadPath = "$psdir\lazyload"
    Write-Host ("Load PS Profiles from {0}" -f $autoloadPath) -ForegroundColor DarkCyan
    
    $measure = Measure-Command {
      Get-ChildItem $autoloadPath
      | where Extension -eq ".ps1"
      | where-object {$_.Name -notmatch "^~"}
      | ForEach-Object {
        $m = Measure-Command {
          . $_.FullName
        }
        Write-Host ("Done: $($_.FullName) in {0:N3} seconds." -f $m.TotalSeconds) -ForegroundColor DarkCyan
      }
    
      $m = Measure-Command {
        if (Test-Path $lazyloadPath) {
    
          Get-ChildItem -Path $lazyloadPath -Filter "*.ps1" | ForEach-Object {
            Write-Host "Lazy: '$lazyloadPath\$($_.Name)'" -ForegroundColor DarkCyan
            $command = $_.BaseName
            $orig = $_.FullName
    
            $hook= @"
    Write-Host "Lazy loading '$command' " -ForegroundColor Yellow
    
    Set-Item -Path "Function::global:$command" -Value ([scriptblock]::Create("& ``"$((Get-Command $command -CommandType Application -ErrorAction Stop).Source)``" @args")) -Force
    
    . "$orig"
    & "$command" @args
    "@
    
            New-Item -Path "Function::global:$command" -Value ([scriptblock]::Create($hook)) -Force | Out-Null
          }
        }
      }
      Write-Host ("Done: lazy in {0:N3} seconds." -f $m.TotalSeconds) -ForegroundColor DarkCyan
    }
    
    Write-Host ("Load completed in {0:N3} seconds." -f $measure.TotalSeconds) -ForegroundColor DarkGreen

    個人的なおすすめは、 ~/.config/PowerShell/Microsoft.PowerShell_profile.ps1 に保管することだ。 ~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1 へリンクを張ることで管理が楽になる。

    続けて、既存のコードや機能があれば分割する。 分割したコードは ~/.config/PowerShell/autoload/ および ~/.config/PowerShell/lazyload に ps1 スクリプトを配置する。

    この際、 lazyloadフォルダには「遅延展開する際の名前」と同一にする必要がある。例えば、 Python のパッケージマネージャ rye を管理する場合は rye.ps1として保管すると都合がよいだろう。

    解説

    このスクリプトは、2種類のディレクトリパス($autoloadPath と $lazyloadPath)を組み合わせて、スクリプトの読み込み方を制御している。

    autoload

    こちらは極めて単純な仕組みになっている。

    1. $autoloadPath で指定されたディレクトリ(~/.config/PowerShell/autoload)内のファイルを列挙する。
    2. .ps1 ファイルにフィルターをかけ、実行する。
    3. ファイル名が ~ から始まれば無効化とし、実行を取りやめる。

    プロンプト設定や頻繁に使うエイリアスなど、起動時に必須のスクリプトをここに配置することで、一切に登録なしに自動実行が可能となる。 PSReadLineのカスタムコマンドなどはここに該当するだろう。

    lazyload

    1. $lazyloadPath で指定されたディレクトリ(~/.config/PowerShell/lazyload)内のファイルを列挙する。
    2. これらのファイルを実行する代わりに、スクリプトのファイル名と同名のフックがグローバル空間に作成される。この処理はファイル読み込みより遥かに軽量である。
    3. ユーザーがそのコマンドを初めて実行すると、フック関数がトリガーされる。
    4. フック関数は、本来のスクリプトファイルを読み込んで自身を上書きする。その後、引数を引き継いで本来の処理を実行する。
    5. 2回目以降の実行ではフック関数は破棄され、既に本来の関数に置き換わっているため、直接その関数が実行される。

    これにより、使用頻度の低い関数の読み込みを、実際に必要になるまで遅延させることができる。

    ===============広告=================

    darallium

    Wed Sep 17 2025 12:28:29 GMT+0900 (Japan Standard Time)