Skip to content

Commit 3112c23

Browse files
committed
Add laptop efficiency testing script
1 parent 2481b9d commit 3112c23

File tree

1 file changed

+310
-0
lines changed

1 file changed

+310
-0
lines changed

rust_perf_battery.ps1

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
<#
2+
.SYNOPSIS
3+
Continuously runs `cargo clean` followed by `cargo build` in a loop.
4+
Logs comprehensive performance and battery statistics per iteration.
5+
6+
.DESCRIPTION
7+
- Runs a self-test before starting. If self-test fails, prints error and exits.
8+
- On start, prints initial line with date/time, battery, mode, etc.
9+
- For each iteration (called a "run"):
10+
* Executes cargo clean and cargo build
11+
* Measures times and increments counters
12+
* Sleeps a configured interval
13+
* Tracks stats per power-mode combination:
14+
- Total mode time
15+
- Total build/clean times
16+
- Build count
17+
* Calculates ratios:
18+
- Builds per % battery drop
19+
- Build time per % battery drop
20+
- Wall time per % battery drop
21+
* Prints a multi-line summary for each iteration:
22+
1) Overall line (date/time, run #, mode, battery, total times)
23+
2) Indented line with sleep info and last build output
24+
3) One indented line per mode with detailed stats and ratios
25+
- On command failure, prints the error output and stops.
26+
27+
Time formats:
28+
- Display time: "yyyy-MM-dd HH:mm:sss" (with 's' appended at the end)
29+
- File names: "yyyy-MM-dd_HH-mm-ss"
30+
- Time spans: "HH:MM:SSs" for total times
31+
- Averages and ratios in seconds (with one decimal place if needed).
32+
33+
"Other" time = total script runtime - (build+clean+sleep).
34+
35+
.NOTES
36+
Requires PowerShell 5.1+ and `cargo` in PATH.
37+
Assumes the current directory has a Cargo project (Cargo.toml).
38+
39+
.EXAMPLE
40+
.\rust_perf_battery.ps1
41+
#>
42+
43+
param()
44+
45+
Set-StrictMode -Version Latest
46+
$ErrorActionPreference = 'Stop'
47+
48+
# Configuration
49+
$PauseBetweenRuns = 10 # seconds to sleep between each iteration
50+
51+
# Formats
52+
$logTimeFormat = "yyyy-MM-dd_HH-mm-ss" # for file names
53+
$displayTimeFormat = "yyyy-MM-dd HH:mm:ss" # for printed lines
54+
[DateTime]$GlobalScriptStart = Get-Date
55+
[string]$ScriptStartTime = (Get-Date -Format $logTimeFormat)
56+
[string]$FullLogPath = Join-Path (Get-Location) ("rust_perf_battery_full_$ScriptStartTime.log")
57+
[string]$SummaryLogPath = Join-Path (Get-Location) ("rust_perf_battery_summary_$ScriptStartTime.log")
58+
59+
# Track global stats
60+
$CompileCount = 0
61+
$StopLoop = $false
62+
[TimeSpan]$TotalBuildTime = [TimeSpan]::Zero
63+
[TimeSpan]$TotalCleanTime = [TimeSpan]::Zero
64+
[TimeSpan]$TotalSleepTime = [TimeSpan]::Zero
65+
66+
# Mode stats: Dictionary<string,PSObject>
67+
# Each value: { Mode, TotalModeTime, TotalBuildTime, TotalCleanTime, BuildCount }
68+
$ModeStats = [System.Collections.Generic.Dictionary[string, object]]::new()
69+
70+
function Ensure-ModeStats($mode) {
71+
if (-not $ModeStats.ContainsKey($mode)) {
72+
$ModeStats[$mode] = [PSCustomObject]@{
73+
Mode = $mode
74+
TotalModeTime = [TimeSpan]::Zero
75+
TotalBuildTime = [TimeSpan]::Zero
76+
TotalCleanTime = [TimeSpan]::Zero
77+
BuildCount = 0
78+
}
79+
}
80+
}
81+
82+
function Get-PowerSchemeName {
83+
$output = powercfg /getactivescheme 2>$null
84+
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($output)) {
85+
throw "Unable to retrieve power scheme name."
86+
}
87+
88+
if ($output -match '\((?<SchemeName>[^)]+)\)') {
89+
return ($Matches['SchemeName'].Trim())
90+
} else {
91+
throw "No scheme name found in powercfg output."
92+
}
93+
}
94+
95+
function Get-BatteryStatus {
96+
$batt = Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue
97+
if (-not $batt) {
98+
return [PSCustomObject]@{
99+
HasBattery = $false
100+
BatteryPercent = 100
101+
OnAC = $true
102+
BatteryStatus = "NoBattery"
103+
}
104+
}
105+
106+
$statusCode = [int]$batt.BatteryStatus
107+
$percent = [int]$batt.EstimatedChargeRemaining
108+
$onAC = $true
109+
if ($statusCode -eq 1 -or $statusCode -eq 4 -or $statusCode -eq 5) {
110+
$onAC = $false
111+
}
112+
113+
return [PSCustomObject]@{
114+
HasBattery = $true
115+
BatteryPercent = $percent
116+
OnAC = $onAC
117+
BatteryStatus = $statusCode
118+
}
119+
}
120+
121+
function Get-CurrentEnergyMode {
122+
$schemeName = Get-PowerSchemeName
123+
$battery = Get-BatteryStatus
124+
$powerSource = if ($battery.OnAC) { "ac" } else { "battery" }
125+
$modeName = ($schemeName -replace '\s+', '-').ToLower()
126+
return "$powerSource-$modeName"
127+
}
128+
129+
function Format-ShortTime($ts) {
130+
# Format as HH:MM:SSs
131+
"{0:00}:{1:00}:{2:00}s" -f [int]$ts.TotalHours, $ts.Minutes, $ts.Seconds
132+
}
133+
134+
function Self-Test {
135+
try {
136+
$mode = Get-CurrentEnergyMode
137+
# Just test we can add to mode stats without error
138+
Ensure-ModeStats($mode)
139+
$ModeStats[$mode].TotalModeTime += (New-TimeSpan -Seconds 5)
140+
} catch {
141+
Write-Host "Self-test failed: $($_.Exception.Message)"
142+
exit 1
143+
}
144+
}
145+
146+
function Run-CargoClean {
147+
$start = Get-Date
148+
$output = & cargo clean 2>&1
149+
$exit = $LASTEXITCODE
150+
$elapsed = (Get-Date) - $start
151+
return [PSCustomObject]@{
152+
Output = $output
153+
Success = ($exit -eq 0)
154+
Elapsed = $elapsed
155+
}
156+
}
157+
158+
function Run-CargoBuild {
159+
$start = Get-Date
160+
$output = & cargo build 2>&1
161+
$exit = $LASTEXITCODE
162+
$elapsed = (Get-Date) - $start
163+
return [PSCustomObject]@{
164+
Output = $output
165+
Success = ($exit -eq 0)
166+
Elapsed = $elapsed
167+
}
168+
}
169+
170+
function Write-Logs {
171+
param(
172+
[string]$FullOutput,
173+
[string]$SummaryLine
174+
)
175+
Add-Content -Path $FullLogPath -Value $FullOutput
176+
Add-Content -Path $FullLogPath -Value $SummaryLine
177+
Add-Content -Path $SummaryLogPath -Value $SummaryLine
178+
}
179+
180+
# Run self-test
181+
Self-Test
182+
183+
# Clear mode stats since we tested adding time
184+
$ModeStats.Clear()
185+
186+
# Now that functions are defined, we can read initial battery
187+
$InitialBatteryPercent = (Get-BatteryStatus).BatteryPercent
188+
189+
# Print initial lines
190+
$battery = Get-BatteryStatus
191+
$initMode = Get-CurrentEnergyMode
192+
$currentTimestamp = (Get-Date -Format $displayTimeFormat) + "s"
193+
194+
$initialLine = "$currentTimestamp battery=$($battery.BatteryPercent)% mode=$initMode"
195+
Write-Host $initialLine
196+
197+
$logLine = "Logs: $(Split-Path $FullLogPath -Leaf), $(Split-Path $SummaryLogPath -Leaf)"
198+
Write-Host $logLine
199+
200+
while (-not $StopLoop) {
201+
$iterationStart = Get-Date
202+
$CurrentEnergyMode = Get-CurrentEnergyMode
203+
204+
# Clean
205+
$cleanResult = Run-CargoClean
206+
if (-not $cleanResult.Success) {
207+
Write-Host "cargo clean failed, output:"
208+
$cleanResult.Output | Write-Host
209+
$StopLoop = $true
210+
break
211+
}
212+
213+
# Build
214+
$buildResult = Run-CargoBuild
215+
if (-not $buildResult.Success) {
216+
Write-Host "cargo build failed, output:"
217+
$buildResult.Output | Write-Host
218+
$StopLoop = $true
219+
break
220+
}
221+
222+
$iterationElapsed = (Get-Date) - $iterationStart
223+
224+
# Update global stats
225+
$TotalCleanTime += $cleanResult.Elapsed
226+
$TotalBuildTime += $buildResult.Elapsed
227+
228+
# Update mode stats
229+
Ensure-ModeStats($CurrentEnergyMode)
230+
$ModeStats[$CurrentEnergyMode].TotalModeTime += $iterationElapsed
231+
$ModeStats[$CurrentEnergyMode].TotalBuildTime += $buildResult.Elapsed
232+
$ModeStats[$CurrentEnergyMode].TotalCleanTime += $cleanResult.Elapsed
233+
$ModeStats[$CurrentEnergyMode].BuildCount += 1
234+
235+
$CompileCount++
236+
$batt = Get-BatteryStatus
237+
238+
# Extract last build line safely
239+
$buildLineObj = $buildResult.Output | Select-Object -Last 1
240+
$lastBuildLine = if ($buildLineObj) { [string]$buildLineObj } else { "" }
241+
$lastBuildLine = $lastBuildLine.Trim()
242+
243+
# Sleep between runs
244+
$sleepStart = Get-Date
245+
Start-Sleep -Seconds $PauseBetweenRuns
246+
$sleepElapsed = (Get-Date) - $sleepStart
247+
$TotalSleepTime += $sleepElapsed
248+
249+
# Compute metrics
250+
$totalElapsed = (Get-Date) - $GlobalScriptStart
251+
$otherTime = $totalElapsed - ($TotalBuildTime + $TotalCleanTime + $TotalSleepTime)
252+
253+
$totalBuildStr = Format-ShortTime $TotalBuildTime
254+
$totalCleanStr = Format-ShortTime $TotalCleanTime
255+
$totalSleepStr = Format-ShortTime $TotalSleepTime
256+
$otherTimeStr = Format-ShortTime $otherTime
257+
258+
$batteryDrop = $InitialBatteryPercent - $batt.BatteryPercent
259+
$batteryDropFloat = [double]$batteryDrop
260+
$currentTimestamp = (Get-Date -Format $displayTimeFormat) + "s"
261+
$runCountStr = "{0:000}" -f $CompileCount
262+
263+
# Overall line
264+
$overallLine = "$currentTimestamp, run $runCountStr, $CurrentEnergyMode, battery $($batt.BatteryPercent)%, total build $totalBuildStr, clean $totalCleanStr, sleep $totalSleepStr"
265+
Write-Host $overallLine
266+
267+
# Second line
268+
$secondLine = " Sleeping ${PauseBetweenRuns}s between builds. $lastBuildLine"
269+
Write-Host $secondLine
270+
271+
# Mode lines with ratios
272+
foreach ($modeKey in $ModeStats.Keys) {
273+
$m = $ModeStats[$modeKey]
274+
# avg build/clean time
275+
$avgBuild = "N/A"
276+
$avgClean = "N/A"
277+
if ($m.BuildCount -gt 0) {
278+
$avgBuildSec = $m.TotalBuildTime.TotalSeconds / $m.BuildCount
279+
$avgBuild = ("{0:F1}s" -f $avgBuildSec)
280+
$avgCleanSec = $m.TotalCleanTime.TotalSeconds / $m.BuildCount
281+
$avgClean = ("{0:F1}s" -f $avgCleanSec)
282+
}
283+
284+
$buildsPerDrop = "N/A"
285+
$buildTimePerDrop = "N/A"
286+
$wallPerDrop = "N/A"
287+
if ($batteryDropFloat -gt 0) {
288+
$buildsPerDropVal = $m.BuildCount / $batteryDropFloat
289+
$buildsPerDrop = ("{0:F2}" -f $buildsPerDropVal)
290+
291+
$buildTimePerDropVal = $m.TotalBuildTime.TotalSeconds / $batteryDropFloat
292+
$buildTimePerDrop = ("{0:F0}s" -f $buildTimePerDropVal)
293+
294+
$wallPerDropVal = $m.TotalModeTime.TotalSeconds / $batteryDropFloat
295+
$wallPerDrop = ("{0:F0}s" -f $wallPerDropVal)
296+
}
297+
298+
$modeLine = " On $($m.Mode) for $(Format-ShortTime $m.TotalModeTime), avg build $avgBuild, avg clean $avgClean, builds per % drop: $buildsPerDrop, build time per %: $buildTimePerDrop, wall per %: $wallPerDrop"
299+
Write-Host $modeLine
300+
}
301+
302+
$fullOutput = "=== Iteration $CompileCount ===`r`n" +
303+
"--- Cargo clean output ---`r`n" + ($cleanResult.Output -join "`r`n") + "`r`n" +
304+
"--- Cargo build output ---`r`n" + ($buildResult.Output -join "`r`n") + "`r`n"
305+
306+
# For logs, just write the overall line as summary
307+
Write-Logs -FullOutput $fullOutput -SummaryLine $overallLine
308+
}
309+
310+
Write-Host "Script ended."

0 commit comments

Comments
 (0)