Skip to content

Instantly share code, notes, and snippets.

@rickardvh
Last active February 3, 2023 10:52
Show Gist options
  • Save rickardvh/4e7317f9b8052489c458efe6cf034672 to your computer and use it in GitHub Desktop.
Save rickardvh/4e7317f9b8052489c458efe6cf034672 to your computer and use it in GitHub Desktop.
Benchmark scripts for nopCommerce cache
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