Last active
February 3, 2023 10:52
-
-
Save rickardvh/4e7317f9b8052489c458efe6cf034672 to your computer and use it in GitHub Desktop.
Benchmark scripts for nopCommerce cache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace System.Diagnostics | |
using namespace System.Threading | |
# This module contains two commandlets, Measure-Startup and Measure-Loadtest. | |
# Example invocations: | |
# > import-module .\measure.psm1 | |
# > measure-startup -nopcompath C:\src\nopCommerce -warmup computers, electronics, cart | |
# > measure-loadtest -nopcompath C:\src\nopCommerce -requests 100 -throttlelimit 5 | |
function Measure-Startup { | |
[CmdletBinding()] | |
param ( | |
[string]$nopcomPath, # the root nopCommerce directory, e.g. C:\src\nopCommerce | |
[switch]$build = $false, | |
[string]$output = "profiling.csv", | |
[string[]]$warmup = @() # the index page is always included | |
) | |
$loc = (Get-Location).Path | |
$file = "${loc}\${output}" | |
try { | |
if ($build) { | |
& dotnet build "${nopcomPath}\src\Presentation\Nop.Web" -c Release | |
} | |
$stopwatch = [Stopwatch]::new() | |
$stopwatch.Start() | |
$proc = StartNopCommerce $nopcomPath | |
$job = Start-ThreadJob -Name "Profile" -ScriptBlock ${Function:Profile} -ArgumentList $proc.Id, $file | |
$hostname = WaitForStart $proc | |
foreach ($path in @("") + $warmup) { | |
$url = "${hostname}/${path}" | |
Write-Host "Making request to ${url}..." | |
$t = Measure-Command { Invoke-WebRequest -URI $url } | |
Write-Host "Request finished in ${t}." | |
} | |
$stopwatch.Stop() | |
Stop-Job $job | |
$total = $stopwatch.ElapsedMilliseconds / 1000 | |
"${total},0,0" | Out-File $file -Append -Encoding utf8 | |
Write-Host "Startup finished in ${total} seconds." | |
} | |
finally { | |
if (Test-Path variable:job) { | |
Stop-Job $job | |
} | |
if (Test-Path variable:proc) { | |
Stop-Process -Id $proc.Id | |
} | |
} | |
} | |
function Measure-Loadtest { | |
[CmdletBinding()] | |
param ( | |
[string]$nopcomPath, # the root nopCommerce directory, e.g. C:\src\nopCommerce | |
[switch]$build = $false, | |
[switch]$coldStart = $false, | |
[int]$requests = 200, | |
[int]$throttleLimit = 10, | |
[string]$outputPrefix = "load" | |
) | |
$loc = (Get-Location).Path | |
$file = "${loc}\${outputPrefix}.csv" | |
try { | |
if ($build) { | |
& dotnet build "${nopcomPath}\src\Presentation\Nop.Web" -c Release | |
} | |
$proc = StartNopCommerce $nopcomPath | |
$hostname = WaitForStart $proc | |
if (!$coldStart) { | |
Write-Host "Warming up..." | |
$t = Measure-Command { Invoke-WebRequest -URI $hostname } | |
Write-Host "Warmup finished in ${t}." | |
} | |
$stopwatch = [Stopwatch]::new() | |
$job = Start-ThreadJob -Name "Profile" -ScriptBlock ${Function:Profile} -ArgumentList $proc.Id, $file | |
$stopwatch.Start() | |
$responseTimes = 1..$requests | ForEach-Object -Parallel { | |
$t = Measure-Command { Invoke-WebRequest -URI $using:hostname } | |
[Math]::Round($t.TotalMilliseconds) | |
} -ThrottleLimit $throttleLimit | |
$stopwatch.Stop() | |
Stop-Job $job | |
$total = $stopwatch.ElapsedMilliseconds / 1000 | |
"${total},0,0" | Out-File $file -Append -Encoding utf8 | |
$responseTimes | Out-File "$loc\${outputPrefix}_responsetimes.csv" -Encoding utf8 | |
Write-Host "Processed ${requests} requests in ${total} seconds." | |
} | |
finally { | |
if (Test-Path variable:job) { | |
Stop-Job $job | |
} | |
if (Test-Path variable:proc) { | |
Stop-Process -Id $proc.Id | |
} | |
} | |
} | |
function Profile { | |
param ( | |
[string]$ProcId, | |
[string]$FilePath, | |
# processIdString and processorTimeString are localised; | |
# use Get-Counter -ListSet to see the localized names if you are running Windows in a language other than English | |
[string]$processIDString = "ID Process", | |
[string]$processorTimeString = "Processor Time" | |
) | |
$cpu_cores = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors | |
$start_time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() | |
$proc_path = ((Get-Counter "\Process(*)\${processIDString}").CounterSamples | Where-Object { $_.RawValue -eq $ProcId }).Path | |
"time,memory,cpu", "0,0,0" | Out-File $FilePath -Encoding utf8 | |
while ($true) { | |
$proc = Get-Process -Id $ProcId | |
$memory = [Math]::Round($proc.WorkingSet64 / 1mb, 2) | |
$time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - $start_time | |
$time = $time / 1000 | |
$cpu = (Get-Counter ($proc_path -replace "\\${processIDString}$", "\% ${processorTimeString}")).CounterSamples.CookedValue | |
$cpu = [Math]::Round($cpu / $cpu_cores, 2) | |
"${time},${memory},${cpu}" | Out-File $FilePath -Append -Encoding utf8 | |
} | |
} | |
function StartNopCommerce { | |
param ( | |
[string]$nopcomPath | |
) | |
$workingDirectory = "${nopcomPath}\src\Presentation\Nop.Web" | |
$procInfo = New-Object System.Diagnostics.ProcessStartInfo | |
$procInfo.WorkingDirectory = $workingDirectory | |
$procInfo.FileName = "${workingDirectory}\bin\release\net7.0\Nop.Web.exe" | |
$procInfo.RedirectStandardOutput = $true | |
$proc = New-Object System.Diagnostics.Process | |
$proc.StartInfo = $procInfo | |
Write-Host "Starting nopCommerce..." | |
$proc.Start() | |
return $proc | |
} | |
function WaitForStart { | |
param ( | |
$proc, | |
[int]$timeout = 30 | |
) | |
$start_time = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds() | |
while ($true) { | |
$time = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds() - $start_time | |
if ($time -ge $timeout) { | |
throw "nopCommerce startup timed out" | |
} | |
Start-Sleep -Milliseconds 500 | |
$line = $proc.StandardOutput.ReadLine() | |
if ($line -match "Now listening on: (.*)") { | |
$url = $Matches[1] | |
Write-Host "nopCommerce started on ${url}" | |
return $url | |
} | |
elseif ($line -match "Unhandled exception.*") { | |
throw $line | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment