Search code examples
powershellbatch-fileaverageexecution-time

How can I run a batch-file about 1000 times, afterwards I want the average execution time as a output. Is this possible?


I tried following powershell-command, but then 1000 windows opened and the powershell ISE crashed. Is there a way to run the batch-file 1000 times in the background? And is there a smarter way that leads to the average execution time?

That's the code I've tried:

cd C:\scripts
Measure-Command { 
    for($i = 0;$i -lt 1000;$i++){
      Start-Process -FilePath "C:\scripts\open.bat"
    }
}

Solution

  • Start-Process by default runs programs asynchronously, in a new console window.

    Since you want to run your batch file synchronously, in the same console window, invoke it directly (which, since the path is double-quoted - though it doesn't strictly have to be in this case - requires &, the call operator for syntactic reasons):

    Measure-Command { 
        foreach ($i in 1..1000){
          & "C:\scripts\open.bat"
        }
    }
    

    Note: Measure-Command discards the success output from the script block being run; if you do want to see it in the console, use the following variation, though note that it will slow down processing:

    Measure-Command { 
        & {
          foreach ($i in 1..1000){
            & "C:\scripts\open.bat"
          }
        } | Out-Host
    }
    

    This answer explains in more detail why Start-Process is typically the wrong tool for invoking console-based programs and scripts.


    Measure-Command is the right tool for performance measurement in PowerShell, but it's important to note that such measurements are far from an exact science, given PowerShell's dynamic nature, which involves many caches and on-demand compilation behind the scenes.

    Averaging multiple runs generally makes sense, especially when calling external programs; by contrast, if PowerShell code is executed repeatedly and the repeat count exceeds 16, on-demand compilation occurs and speeds up subsequent executions, which can skew the result.

    Time-Command is a friendly wrapper around Measure-Command, available from this MIT-licensed Gist[1]; it can be used to simplify your tests.

    # Download and define function `Time-Command` on demand (will prompt).
    # To be safe, inspect the source code at the specified URL first.
    if (-not (Get-Command -ea Ignore Time-Command)) {
      $gistUrl = 'https://gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27/raw/Time-Command.ps1'
      if ((Read-Host "`n====`n  OK to download and define benchmark function ``Time-Command`` from Gist ${gistUrl}?`n=====`n(y/n)?").Trim() -notin 'y', 'yes') { Write-Warning 'Aborted.'; exit 2 }
      Invoke-RestMethod $gistUrl | Invoke-Expression
      if (-not ${function:Time-Command}) { exit 2 }
    }
    
    Write-Verbose -Verbose 'Running benchmark...'
    # Omit -OutputToHost to run the commands quietly.
    Time-Command -Count 1000 -OutputToHost { & "C:\scripts\open.bat" }
    

    Note that while Time-Command is a convenient wrapper even for measuring a single command's performance, it also allows you to compare the performance of multiple commands, passed as separate script blocks ({ ... }).


    [1] Assuming you have looked at the linked Gist's source code to ensure that it is safe (which I can personally assure you of, but you should always check), you can install it directly as follows:
    irm https://gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27/raw/Time-Command.ps1 | iex