Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # PSBenchFrame 1.1
- # by MaxK 2026.05.30
- param(
- # Execution mode: "Sequential" or "Interlaced"
- [string]$ExecutionMode = "Interlaced",
- [int]$TotalIterations = 10000
- )
- # ==============================================================================
- # Configuration
- # ==============================================================================
- $Global:PauseProbabilityPercent = 0.25 # Pause probability(eg. 0.25%)
- # Pause type: "Random" or "KeyPress"
- $Global:PauseType = "Random"
- # Pause length - random between min and max in ms
- $Global:PauseMinMilliseconds = 10
- $Global:PauseMaxMilliseconds = 500
- # Probability of switching to the next function to be tested in %
- $Global:SwitchProbabilityPercent = 6.0
- $Global:LogFile = "PSBenchFrame.log"
- # ==============================================================================
- # Functions to be tested (and their init-helpers)
- # ==============================================================================
- # A helper init function for Get-XYZ invoked before benchmark
- function Init-XYZ {
- $Global:XYZ_Salt = [Guid]::NewGuid().ToString()
- $Global:XYZ_Base = "SystemSession_SecureKey_2026_"
- $Global:XYZ_Counter = 0
- }
- function Init-XYZ-Fat {
- $Global:XYZ_Fat_Counter = 0
- }
- function Init-XYZ-Turbo {
- $Global:XYZ_Turbo_Counter = 0
- $Global:sha = [System.Security.Cryptography.SHA256]::Create()
- }
- function Dispose-XYZ-Turbo {
- $Global:XYZ_Turbo_Counter = 0
- $Global:sha = [System.Security.Cryptography.SHA256]::Create()
- }
- # The function to be tested itself
- function Get-XYZ {
- $Global:XYZ_Counter++
- $rawString = "$Global:XYZ_Base$Global:XYZ_Salt$Global:XYZ_Counter"
- $bytes = [System.Text.Encoding]::UTF8.GetBytes($rawString)
- $sha = [System.Security.Cryptography.SHA256]::Create()
- $hash = $sha.ComputeHash($bytes)
- $sha.Dispose()
- $token = [System.Convert]::ToBase64String($hash)
- return $token
- }
- function Get-XYZ-Turbo {
- $Global:XYZ_Turbo_Counter++
- $rawString = "$Global:XYZ_Base$Global:XYZ_Salt$Global:XYZ_Turbo_Counter"
- $bytes = [System.Text.Encoding]::UTF8.GetBytes($rawString)
- $hash = $Global:sha.ComputeHash($bytes)
- $token = [System.Convert]::ToBase64String($hash)
- return $token
- }
- function Get-XYZ-Fat {
- $Global:XYZ_Fat_Counter++
- $rawString = "$Global:XYZ_Base$Global:XYZ_Salt$Global:XYZ_Fat_Counter"
- $bytes = [System.Text.Encoding]::UTF8.GetBytes($rawString)
- $sha = [System.Security.Cryptography.SHA256]::Create()
- for ([int]$i = 0; $i -lt 50; $i++) {
- $hash = $Global:sha.ComputeHash($bytes)
- }
- $sha.Dispose()
- $token = [System.Convert]::ToBase64String($hash)
- return $token
- }
- function Try-To-Melt-Single-Core {
- [double]$x = 1.0000001
- for ([int]$i = 0; $i -lt 100000; $i++) {
- $x = $x * $x
- $x = $x / 0.98999999
- if ($x -gt 1e100) { $x = 1.0000001 }
- }
- }
- # ==============================================================================
- # Test suite
- # ==============================================================================
- $Global:TestEntries = @(
- #1st function to be tested
- [PSCustomObject]@{
- Name = "Get-XYZ"
- Code = { $null = Get-XYZ } # the function above
- },
- #2nd function to be tested
- [PSCustomObject]@{
- Name = "Get-XYZ-Fat"
- Code = { $null = Get-XYZ-Fat } # some alternative to the function above
- },
- #3rd function to be tested
- [PSCustomObject]@{
- Name = "Try-To-Melt-Single-Core"
- Code = { $null = Try-To-Melt-Single-Core }
- },
- #4th function to be tested
- [PSCustomObject]@{
- Name = "Get-XYZ-Turbo"
- Code = { $null = Get-XYZ-Turbo } # some alternative to the function above
- }
- #5th function to be tested
- #[PSCustomObject]@{
- # Name = "Get-XYZ-Turbo-Ultra"
- # Code = { $null = Get-XYZ-Turbo-Ultra } # some other alternative to the function above
- #}
- # and so on - you can add here further functions to be tested...
- )
- # ==============================================================================
- # Benchmark engine helper functions
- # ==============================================================================
- # Progress bar
- function Show-DismProgressBar {
- param (
- [int]$Current,
- [int]$Total,
- [string]$Status = ""
- )
- $percent = [math]::Round(($Current / $Total) * 100)
- $barLength = 20
- $filledLength = [math]::Round(($percent / 100) * $barLength)
- $filled = "+" * $filledLength
- $empty = "-" * ($barLength - $filledLength)
- $clearBuffer = " " * 20
- $output = "`rProgress: [$filled$empty] $percent% | Status: $Status$clearBuffer"
- [System.Console]::Write($output)
- }
- # Random pause during test - not added to the execution time of course
- function Invoke-StochasticPause {
- param ([int]$PauseIndex)
- $roll = Get-Random -Minimum 1 -Maximum 10001
- if ($roll -le ($Global:PauseProbabilityPercent * 100)) {
- if ($Global:PauseType -eq "KeyPress") {
- Show-DismProgressBar -Current $PauseIndex -Total $TotalIterations -Status "[PAUSE] Press any key..."
- $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
- }
- elseif ($Global:PauseType -eq "Random") {
- $totalSleepMs = Get-Random -Minimum $Global:PauseMinMilliseconds -Maximum ($Global:PauseMaxMilliseconds + 1)
- for ($remaining = $totalSleepMs; $remaining -gt 0; $remaining -= 100) {
- Show-DismProgressBar -Current $PauseIndex -Total $TotalIterations -Status "[PAUSE] Resuming in ${remaining}ms..."
- Start-Sleep -Milliseconds 100
- }
- }
- return $true
- }
- Show-DismProgressBar -Current $i -Total $TotalIterations -Status "Active: [$($Global:activeTest.Name)]"
- return $false
- }
- # ==============================================================================
- # Main benchmark engine
- # ==============================================================================
- function Start-BenchmarkSuite {
- Write-Host "=== ENGINE START: Mode = $ExecutionMode ===" -ForegroundColor Cyan
- [System.GC]::Collect()
- [System.GC]::WaitForPendingFinalizers()
- $results = @()
- # Sequential mode
- if ($ExecutionMode -eq "Sequential") {
- foreach ($test in $Global:TestEntries) {
- Write-Host "Running: $($test.Name)... " -NoNewline -ForegroundColor Gray
- $Global:activeTest = $test
- # One StopWatch instance for the whole loop = no-GC, zero-overhead
- $sw = [System.Diagnostics.Stopwatch]::StartNew()
- $pauseTicks = 0
- for ($i = 1; $i -le $TotalIterations; $i++) {
- $pauseSw = [System.Diagnostics.Stopwatch]::StartNew()
- $hadPause = Invoke-StochasticPause -PauseIndex $i
- $pauseSw.Stop()
- if ($hadPause) { $pauseTicks += $pauseSw.ElapsedTicks }
- # direct call = no thousands of Measure-Command in the loop
- & $test.Code
- }
- $sw.Stop()
- $netTicks = $sw.ElapsedTicks - $pauseTicks
- $netTimeSpan = [TimeSpan]::FromTicks($netTicks)
- $results+=([PSCustomObject]@{
- TestName = $test.Name
- TotalIterations = $TotalIterations
- TotalTimeSeconds = [math]::Round($netTimeSpan.TotalSeconds, 4)
- AvgTimeMsPerIter = [math]::Round(($netTimeSpan.TotalMilliseconds / $TotalIterations), 4)
- })
- Show-DismProgressBar -Current 100 -Total 100 -Status "Finished $TotalIterations iterations of [$($Global:activeTest.Name)]"
- Write-Host ""
- }
- }
- # Interlaced mode
- elseif ($ExecutionMode -eq "Interlaced") {
- $testCount = $Global:TestEntries.Count
- $metrics = [System.Collections.Generic.Dictionary[int, PSCustomObject]]::new()
- for ($t = 0; $t -lt $testCount; $t++) {
- $metrics[$t] = [PSCustomObject]@{
- Stopwatch = [System.Diagnostics.Stopwatch]::new()
- Executions = 0
- }
- }
- $currentTestIndex = 0
- Show-DismProgressBar -Current 0 -Total $TotalIterations -Status "Starting Interlaced..."
- for ($i = 1; $i -le $TotalIterations; $i++) {
- # Stochastic pause
- if (Invoke-StochasticPause -PauseIndex $i) { }
- $Global:activeTest = $Global:TestEntries[$currentTestIndex]
- $activeMetric = $metrics[$currentTestIndex]
- # Precise measurement of time
- # Start
- $activeMetric.Stopwatch.Start()
- & $Global:activeTest.Code # Run currently selected function
- # Stop
- $activeMetric.Stopwatch.Stop()
- # ---------------------------------
- $activeMetric.Executions++
- $switchRoll = Get-Random -Minimum 1 -Maximum 10001
- if ($switchRoll -le ($Global:SwitchProbabilityPercent * 100)) {
- $currentTestIndex = ($currentTestIndex + 1) % $testCount
- }
- # Progress bar
- if ($i % 10 -eq 0 -or $i -eq $TotalIterations) {
- Show-DismProgressBar -Current $i -Total $TotalIterations -Status "Active: [$($Global:activeTest.Name)]"
- }
- }
- Show-DismProgressBar -Current 100 -Total 100 -Status "Finished $TotalIterations iterations"
- Write-Host ""
- # Results
- for ($t = 0; $t -lt $testCount; $t++) {
- $execs = $metrics[$t].Executions
- $sw = $metrics[$t].Stopwatch
- $avg = 0
- if ($execs -gt 0) {
- $avg = [math]::Round(($sw.Elapsed.TotalMilliseconds / $execs), 4)
- }
- $results += [PSCustomObject]@{
- TestName = $Global:TestEntries[$t].Name
- TotalIterations = $execs
- TotalTimeSeconds = [math]::Round($sw.Elapsed.TotalSeconds, 4)
- AvgTimeMsPerIter = $avg
- }
- }
- }
- # ==============================================================================
- # Output
- # ==============================================================================
- Write-Host "`n================ FINAL TEST RESULTS ================" -ForegroundColor Green
- $timestamp = [DateTime]::Now.ToString("yyyy-MM-dd HH:mm:ss")
- $logHeader = "`n[BENCHMARK RUN: $timestamp]`n"
- $(
- $logHeader
- $results | Format-Table -AutoSize | Out-String
- ) | Tee-Object -FilePath $Global:LogFile -Append
- }
- function Init-Functions {
- Init-XYZ
- Init-XYZ-Turbo
- Init-XYZ-Fat
- # Place here all other init functions required by test functions
- }
- function Dispose-Functions {
- Dispose-XYZ-Turbo
- }
- Init-Functions
- Start-BenchmarkSuite
- Dispose-Functions
Comments
-
Comment was deleted
-
- Update 1.1 - less waste for GC, much more precise time measurement
-
Comment was deleted
-
- How to Use
- 1. Copy/Import functions you want to test. Add Init-functions if required.
- 2. Populate the Suite: Go to the $Global:TestEntries array and add your blocks. Define a user-friendly Name and wrap your target commands inside the Code = { ... } script block.
- 3. Configure the Chaos Engine:
- - Adjust $Global:SwitchProbabilityPercent to set how often the engine switches tasks in Interlaced mode. Higher values mean shorter, frantic bursts of alternating tasks.
- - Adjust $Global:PauseProbabilityPercent to simulate system hiccups or user idling. You can use values like 0.15 for fine granularity. Or 0 if you don't want any pauses.
- 4. Run the script in a standard PowerShell console window (powershell.exe or pwsh). Avoid VS Code's integrated terminal for best UI rendering accuracy.
- Parameters:
- - ExecutionMode: either "Sequential" if you want to baseline individual commands back-to-back or "Interlaced" if you want to simulate real-world environment context-switching stress.
- - TotalIterations: in "Sequential" mode it defines how mant times each function will be executed and measured. In "Interlaced" mode it's a total number of function calls. Each function will be called and measured more or less as frequent as others. So for 5 functions and 1000 iterations each should be called around 200 times.
- Hope this helps anyone looking to pull highly accurate metrics out of their automated scripts.
Add Comment
Please, Sign In to add comment