functions/performance/Measure-PSMDCommandEx.ps1

function Measure-PSMDCommandEx
{
    
    <#
        .SYNOPSIS
            Measures command performance with consecutive tests.
         
        .DESCRIPTION
            This function measures the performance of a scriptblock many consective times.
     
            Warning: Running a command repeatedly may not yield reliable information, since repeated executions may benefit from caching or other performance enhancing features, depending on the script content.
            This is best suited for measuring the performance of tasks that will later be run repeatedly as well.
            It also is useful for mitigating local performance fluctuations when comparing performances.
     
        PARAMETER ScriptBlock
            The scriptblock whose performance is to be measure.
     
        PARAMETER Iterations
            How many times should this performance test be repeated.
         
        .EXAMPLE
            PS C:\> Measure-PSMDCommandEx -ScriptBlock { dir \\Server\share } -Iterations 100
     
            This tries to use Get-ChildItem on a remote directory 100 consecutive times, then measures performance and reports common performance indicators (Average duration, Maximum, Minimum, Total)
         
        .NOTES
            Supported Interfaces:
            ------------------------
             
            Author: Friedrich Weinmann
            Company: die netzwerker Computernetze GmbH
            Created: 06.07.2016
            LastChanged: 06.11.2016
            Version: 1.1
         
        .LINK
            Link to Website.
    #>

    [CmdletBinding()]
    Param (
        
    )
    
    
    #region Dynamic Parameter
    <#
        Whoever might read this script will probably wonder: Why use dynamic parameters?
     
    There's a reason:
    Dynamic parameters are included in the $PSBoundParameters automatic variable, but are NOT bound as variables.
    Thus it becomes possible - using dynamic parameters - to provide easy to use parameter names without hiding super-scope variables with the same name.
    Since this is designed to test other pieces of code - some not necessarily isolated functions, but rather only slight pieces of code that use other variables - I tried to
    minimize the footprint of the function internals, so that it will provide as consistent user experience as I can manage.
    #>

    DynamicParam
    {
        # Create the dictionary
        $______RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        
        #region Prepare ScriptBlock Parameter
        # Create the collection of attributes
        $______AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        
        # Create and set the parameters' attributes
        $______ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $______ParameterAttribute.Position = 0
        $______ParameterAttribute.HelpMessage = "The scriptblock whose performance is to be measure."
        
        # Add the attributes to the attributes collection
        $______AttributeCollection.Add($______ParameterAttribute)
        
        # Create parameter and add it to dictionary
        $______RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter("ScriptBlock", [ScriptBlock], $______AttributeCollection)
        $______RuntimeParameterDictionary.Add("ScriptBlock", $______RuntimeParameter)
        #endregion Prepare ScriptBlock Parameter
        
        #region Prepare Iterations Parameter
        # Create the collection of attributes
        $______AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        
        # Create and set the parameters' attributes
        $______ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $______ParameterAttribute.Position = 1
        $______ParameterAttribute.HelpMessage = "How many times should this performance test be repeated."
        
        # Add the attributes to the attributes collection
        $______AttributeCollection.Add($______ParameterAttribute)
        
        # Create parameter and add it to dictionary
        $______RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter("Iterations", [Int], $______AttributeCollection)
        $______RuntimeParameter.Value = 1
        $______RuntimeParameterDictionary.Add("Iterations", $______RuntimeParameter)
        #endregion Prepare Iterations Parameter
        
        # Return dynamic parameter
        return $______RuntimeParameterDictionary
    }
    #endregion Dynamic Parameter
    
    Begin
    {
        $______Results = @()
        $______Int = 0
    }
    Process
    {
        while ($______Int -lt $PSBoundParameters["Iterations"])
        {
            $______Results += Measure-Command -Expression $PSBoundParameters["ScriptBlock"]
            $______Int++
        }
    }
    End
    {
        $______hash = @{ }
        $______hash["Results"] = $______Results
        $______hash["Max"] = $______Results | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum
        $______hash["Sum"] = $______Results | Select-Object -ExpandProperty Ticks | Measure-Object -Sum | Select-Object -ExpandProperty Sum | ForEach-Object { New-Object System.TimeSpan($_) }
        $______hash["Min"] = $______Results | Measure-Object -Minimum | Select-Object -ExpandProperty Minimum
        $______hash["Average"] = $______Results | Select-Object -ExpandProperty Ticks | Measure-Object -Average | Select-Object -ExpandProperty Average | ForEach-Object { New-Object System.TimeSpan($_) }
        
        New-Object System.Management.Automation.PSObject -Property $______hash
    }
}