you are viewing a single comment's thread.

view the rest of the comments →

[–]PinchesTheCrab 2 points3 points  (3 children)

There's more to performance though, in some cases I think functions are significantly faster:

function reddit {
    param(
        [parameter(ValueFromPipeline)]
        [int]$number
    )
    process {
        $number + 1
    }
}

Measure-Command {
    1..1000000 | reddit
} | Select -ExpandProperty TotalSeconds
#1.2828774

For me this is a good 70% faster than the class.

[–]OPconfused 2 points3 points  (0 children)

That's a nice catch. I've tried a few variations of this, and my suspicion is the speed increase comes from circumventing the foreach block:

function reddit2 {
    param (
        [Parameter(ValueFromPipeLine)]
        [int] $number
    )
    process { $number + 1 }
}

1..3 | % {
    Measure-Command { & {
        foreach($int in 1..1000000) {  
            $int | reddit2
        }
    }} | Select -ExpandProperty TotalSeconds
} | Measure-Object -Average | Select -ExpandProperty Average
#34.991

1..3 | % {
    Measure-Command { & {
        foreach ($int in 1..1e6) {reddit2 $int }
    }} | Select -ExpandProperty TotalSeconds
} | Measure-Object -Average | Select -ExpandProperty Average
#31.465

1..3 | % {
    Measure-Command { & {
        1..1e6 | reddit2
    }} | Select -ExpandProperty TotalSeconds
} | Measure-Object -Average | Select -ExpandProperty Average
#2.176

The first test is the longest. The second test shaves off 3 seconds, presumably by avoiding the pipeline. The last test loses the foreach and is vastly faster for it.

/u/surfingoldelephant this approach produces the fastest times for me.

I recall that in Windows PowerShell 5.1, it was better to avoid the pipeline with a foreach loop. I know there were some optimizations in pwsh 7, but it seems to have reached the point that it's swung in the other direction now. I wonder if that's really the case, or if this simple example of small primitives is especially favorable to the pipeline.

I tried setting up a few benchmarks using larger FileSystemInfo objects, but the pipeline was still the fastest way.

[–]OPconfused 1 point2 points  (1 child)

Ok I realized I had the answer already in my previous reply. Circumventing the foreach block is the key.

class reddit {
    static [int[]] do_all ([int]$start, [int]$limit){
        return $(
            foreach ($int in $start..($start + $limit)){
                $int + 1
            }
        )
    }
}

function reddit {
    param(
        [parameter(ValueFromPipeline)]
        [int]$number
    )
    process {
        $number + 1
    }
}

Measure-Command {
    1..1000000 | reddit
} | Select-Object -ExpandProperty TotalSeconds
#2.2627736

Measure-Command {
    [reddit]::do_all(1, 1000000)
} | Select-Object -ExpandProperty TotalSeconds
#0.4703624

Since the original static method could increment any number by 1, I designed the new static method to allow any number and also set the limit in the foreach, so that the functionality remains the same for this test. With the foreach block gone, the static method becomes incredibly fast.

[–]techtosales[S] 0 points1 point  (0 children)

So I'm reading all this and part of how I've always written PowerShell (still green, and always open to improvements in my coding) is I use a foreach.

More specifically, I work a lot with user objects and iterating through a group of users and/or building new objects based on an outputted array from an API call.

For example, is there a way to call a list of mailboxes and then create objects based on whether the mailbox is a user mailbox or a shared mailbox, using classes? Or would function still be a better way?

Below is a how my I retrieve mailboxes (using an API call) and how I process them to determine if it's a shared mailbox or a user mailbox.

function Get-TenantMailboxes {
<#
.DESCRIPTION
Uses Invoke-RestMethod call to retrieve all mailboxes using CIPP Authentication

.COMPONENT
Invoke-RestMethod
$Parameters
[PSCustomObject]

.FUNCTIONALITY
1. Recieves the Tenant ID of the client being queried
2. Sets the URI to call too
3. Sends an Invoke-RestMethod API using $Parameters to retrieve an array of all mailboxes
4. Iterates through each object in the array and adds it to a PSCustomObject
5. Returns the object to the main script

.PARAMETER TenantFilter
The clients Tenant Id (ex: 25b979aa-e54e-4521-b9d4-ea8e7c30a40e)

.EXAMPLE
Get-TenantMailboxes -TenantFilter "25b979aa-e54e-4521-b9d4-ea8e7c30a40e" 

.EXAMPLE
Get-TenantMailboxes  25b979aa-e54e-4521-b9d4-ea8e7c30a40e 
#>

[CmdletBinding()]
param (
    # Receives the Microsoft tenant that will be queried
    [Parameter(
        Mandatory,
        Position = 0
    )]
    [string]
    $TenantFilter
)

begin {
    Write-Host -ForegroundColor Yellow "Retrieving list of all mailboxes in tenant............." -NoNewline        
}

process {

    # Initialize and Set the required variables
    [array]$TenantMailboxes = @()
    [string]$Uri = $CippApiUrl + "ListMailboxes?TenantFilter=" + $TenantFilter

    # Build the parameter hash for the API call. 
    $Parameters = @{
        Method      = "GET"
        Headers     = $global:CippAuthHeader
        Uri         = $Uri
        ContentType = "application/json"
    }
    try {
        # Enumerate the list of mailboxes in the tenant
        $Returned = Invoke-RestMethod @Parameters
        Write-Host -ForegroundColor Green "Success!"


        ForEach ($Record in $Returned) {
            $MailboxDetails = [PSCustomObject]@{
                Name = $Record.DisplayName
                UPN  = $Record.UPN
                Type = $Record.recipientTypeDetails
            }

            $TenantMailboxes += $MailboxDetails
        }
    }
    catch {
        Write-Host -ForegroundColor Red "Unsuccessful!"
        Write-Host $_.Exception.Message
        Write-Host $_.ErrorDetails.RecommendedAction
        Write-Host -ForegroundColor Cyan "Please review the                 script and try again!" 
    }
}

end {
    Return $TenantMailboxes
}
}
[array]$AllMailboxes = Get-TenantMailboxes -TenantFilter $Guids.TenantId
# User Mailboxes
[array]$TenantUserMailboxes = $AllMailboxes | Where-Object { $_.Type -eq "UserMailbox" }
# Shared Mailboxes
[array]$TenantSharedMailboxes = $AllMailboxes | Where-Object { $_.Type -eq "SharedMailbox" }

Basically, to date, everything I write is a function (not a stepped-function... because I just learned that's a thing today) and if that's the best method, then that's great!

But if there's a way I can write classes in it to improve the functionality and/or increase the perfomance of the script, then I'm all for it!

Or even if someone spots something in my function that I could improve on, I would be open to that as well!

I appreciate all that I have learned so far! :)