Skip to content

Instantly share code, notes, and snippets.

@qqii
Created August 19, 2023 17:18
Show Gist options
  • Save qqii/7c24d3f825193885688d0f8838fa3a0a to your computer and use it in GitHub Desktop.
Save qqii/7c24d3f825193885688d0f8838fa3a0a to your computer and use it in GitHub Desktop.
Disk torture test by repeatedly running the default CrystalDiskMark benchmark
# Repeatedly runs the default CrystalDiskMark benchmark:
#
# Random Data, 1GiB
# Test Count 5
# Interval 5s, Measure 5s, Read then Write
# SEQ1M Q8T1
# SEQ1M Q1T1
# RND4K Q32T1
# RND4K Q1T1
#
# CrystalDiskMark is a wrapper DiskSpd, this script calls DiskSpd directly.
# Instructions:
# 1. Download DiskSpd (https://github.com/microsoft/diskspd/releases)
# 2. Extract DiskSpd/amd64/diskspd.exe into the same folder as this script
# 3. Run this script in an elevated PowerShell session. The first argument
# should be an non-existent file path in the drive you want to test.
#
# Leave the script running for as long as you want to test the drive. Press
# CTRL+C to stop the script.
# TODO:
# - Automatically download and extract DiskSpd
# - Automatically run as admin
# - Parse DiskSpd output and save to CSV
# - Parse DiskSpd and display results in a GUI
Param(
[Parameter(Mandatory = $true)]
[string]$TestFilePath,
[Parameter()]
[ValidateRange(1, [int]::MaxValue)]
[int]$N = [int]::MaxValue, # How many times to run the torture test
[Parameter()]
[ValidateRange(1, [int]::MaxValue)]
[int]$TestCount = 5, # How many times each test is run in a row
[Parameter()]
[ValidateRange(1, [int]::MaxValue)]
[int]$TestDataSize = 1GB,
[Parameter()]
[TimeSpan]$TestDuration = (New-TimeSpan -Seconds 5),
[Parameter()]
[TimeSpan]$IntervalDelay = (New-TimeSpan -Seconds 5)
)
# Check administartor rights
$IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $IsAdmin) {
Write-Error "This script must be run as an administrator."
return
}
# Check DiskSpd exists
$DiskSpdPath = Get-Command -Name "diskspd.exe" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source
if (-not $DiskSpdPath) {
$ScriptFolder = Split-Path -Parent $MyInvocation.MyCommand.Path
$DiskSpdPath = Join-Path $ScriptFolder "diskspd.exe"
if (-not (Test-Path $DiskSpdPath -PathType Leaf)) {
Write-Error "diskspd.exe not found in system path or script folder."
return
}
}
# Check if file exists
if (Test-Path $TestFilePath -PathType Leaf) {
Write-Error "File already exists at $TestFilePath."
$DeleteFile = Read-Host "Type 'delete this file' to delete the file and continue, or any other key to exit"
if ($DeleteFile -eq "delete this file") {
Write-Output "Deleting..."
Remove-Item -Force $TestFilePath
}
else {
return
}
}
# Generate random file
Write-Output "Generating File..."
New-Item -Force -ItemType File -Path $TestFilePath > $null
$RandomData = New-Object byte[] $TestDataSize
(New-Object Random).NextBytes($RandomData)
[IO.File]::WriteAllBytes($TestFilePath, $RandomData)
if (-not (Test-Path $TestFilePath -PathType Leaf)) {
Write-Error "Failed to create file at $TestFilePath"
return
}
$FileSize = (Get-Item $TestFilePath).Length
if ($FileSize -ne $TestDataSize) {
Write-Error "Failed to fill file with random data."
return
}
function Start-SleepWithProgress {
param (
[int]$DurationSeconds,
[int]$ParentId = 1,
[int]$Id = 1
)
for ($i = 1; $i -le $DurationSeconds; $i++) {
$Status = "$($i)s / $($DurationSeconds)s"
$Pct = $i / $DurationSeconds * 100
$Rem = $DurationSeconds - $i
Write-Progress -ParentId $ParentId -Id $Id -Activity "Sleeping for $($DurationSeconds)s" -Status $Status -PercentComplete $Pct -SecondsRemaining $Rem
Start-Sleep -Seconds 1
}
Write-Progress -ParentId $ParentId -Id $Id -Activity "Sleeping for $($DurationSeconds)s" -Completed
}
function Invoke-DiskSpdTest {
param (
[int]$Id,
[string]$TestName,
[string]$TestArgs
)
$SplitArgs = $TestArgs -split " "
for ($j = 1; $j -le $TestCount; $j++) {
$TestStatus = "$j / $TestCount"
$TestPct = $j / $TestCount * 100
$Rem = $TestDuration.TotalSeconds * ($TestCount - $j)
# There's a bug preventing this from
Write-Progress -ParentId 1 -Id $Id -Activity $TestName -Status $TestStatus -PercentComplete $TestPct -SecondsRemaining $Rem
# -Z use random data of size
# -W0 no warmup
# -S disable software caching
# -ag group affinity
# -d duration in seconds
& $DiskSpdPath "-Z$($TestDataSize)" @SplitArgs -W0 -S -ag "-d$($TestDuration.TotalSeconds)" $TestFilePath > $null
}
Write-Progress -ParentId 1 -Id $Id -Activity $TestName -Status "Done" -PercentComplete 100
Start-SleepWithProgress -DurationSeconds $IntervalDelay.TotalSeconds -ParentId $Id -Id 10
}
Write-Host "Running tests..."
for ($i = 1; $i -le $N; $i++) {
$Status = "$i / $N"
$Pct = $i / $N * 100
Write-Progress -Id 1 -Activity "Torture Test" -Status $Status -PercentComplete $Pct
Invoke-DiskSpdTest -Id 2 -TestName "Read: SEQ1M Q8T1" -TestArgs "-b1M -o8 -t1 -w0"
Invoke-DiskSpdTest -Id 3 -TestName "Read: SEQ1M Q1T1" -TestArgs "-b1M -o1 -t1 -w0"
Invoke-DiskSpdTest -Id 4 -TestName "Read: RND4K Q32T1" -TestArgs "-b4K -o32 -t1 -w0 -r"
Invoke-DiskSpdTest -Id 5 -TestName "Read: RND4K Q1T1" -TestArgs "-b4K -o1 -t1 -w0 -r"
Invoke-DiskSpdTest -Id 6 -TestName "Write: SEQ1M Q8T1" -TestArgs "-b1M -o8 -t1 -w100"
Invoke-DiskSpdTest -Id 7 -TestName "Write: SEQ1M Q1T1" -TestArgs "-b1M -o1 -t1 -w100"
Invoke-DiskSpdTest -Id 8 -TestName "Write: RND4K Q32T1" -TestArgs "-b4K -o32 -t1 -w100 -r"
Invoke-DiskSpdTest -Id 9 -TestName "Write: RND4K Q1T1" -TestArgs "-b4K -o1 -t1 -w100 -r"
}
Write-Host "Done! Deleting file..."
Remove-Item -Force $TestFilePath
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment