Skip to content

Commit d299710

Browse files
authored
Merge pull request #23 from Rovey/stock-five-minutes
feat: the clock profiles measure stock for five minutes too
2 parents 2afc8fe + 3dfa280 commit d299710

7 files changed

Lines changed: 103 additions & 63 deletions

File tree

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,15 @@ The undervolt is saved in place of an overclock, not next to it: on the card a c
165165
166166
```mermaid
167167
flowchart LR
168-
A[Baseline<br/>30 s at stock] --> B[Power limit]
168+
S[Stock<br/>300 s, to compare with] --> A[Baseline<br/>30 s at stock]
169+
A --> B[Power limit]
169170
B --> C[Memory offset<br/>bandwidth peak, confirmed,<br/>safety margin]
170171
C --> D[Core offset<br/>single steps, confirmed,<br/>safety margin]
171172
D --> G[Soak<br/>300 s]
172173
G --> H[Save profile]
173174
```
174175
176+
- **Every run starts with the card at stock for five minutes,** as long as the result is tested at the end, with the fans left to the NVIDIA driver. That is the "before" of the result: a card is not heated through after 30 s, so a shorter run would make every result look warmer and louder than stock.
175177
- **Stress test.** A DirectX 11 compute load in which the GPU checks every value it computes against a known answer. Each probe ends in a verdict: `STABLE`, `WRONG RESULT`, `DEVICE LOST`, `STALLED` (the values were right but the card computed almost nothing), `TOO HOT` or `NO TELEMETRY`.
176178
- **Memory comes first and stops at the bandwidth peak,** not at the first error. GDDR6 and GDDR6X retry failed transfers, so an overclocked memory bus loses speed long before it returns a wrong result. The search raises the offset 50 MHz at a time, measures bandwidth at each step and keeps the lowest offset within 1 % of the best. Where bandwidth cannot be measured, it climbs until a probe fails instead.
177179
- **The core search climbs from stock.** With the chosen memory offset applied, it raises the core offset 15 MHz at a time until a probe fails, and keeps the last value that passed. How far it may go comes from the range the driver reports for your card, so a card that can hold more is not stopped at a fixed limit.

‎docs/development.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ The applied offset is always at least one step below the confirmed edge. Profile
9696
| The fans are tuned by measuring, last | `tune_fan`: under the load of the finished tune the fans are set by hand, the temperature is given time to settle (within 1 C over three looks 15 s apart, three minutes at most), and the speed goes down 5 % at a time until the card settles above the target, 5 C under the profile's limit. The lowest speed that held is one point of the saved curve; full speed sits at the limit. It runs after the clocks because the heat to carry away is the tune's. The final five minutes then run on that curve: a tune that only held with louder fans does not get the quiet ones, and keeps the profile's own curve. |
9797
| One click is all of it | A run from the window that ends saved switches apply-at-logon on itself (`UiState::keep_at_logon`, a ticked box on the Optimize page while it is still off): the promise is that the user presses one button and is done, and a second switch to find afterwards breaks it. It is a box and not a silent default because it copies the app to Program Files and registers a logon task. The command line does not do it: `gao --boot on` stays its own command. |
9898
| The run draws the curve | During a run from the window the telemetry tick reads the curve back once a second and the run screen draws it over the last read at stock: the search's own writes, not a picture of the plan. It is a read from the window's own driver connection, like the applied-state read that was there already. |
99-
| The undervolt run measures stock for as long as it soaks | Five minutes each (`kUvBaselineS = kUvSoakS`). A card is not heated through after 30 s: on the reference card the short stock run read 59 to 61 C and 185 W where five minutes gave 67 C at 72 % fan and 196 W, so a result compared with it looked no cooler than stock. The run takes four and a half minutes longer for it. It also puts the clock to keep and the built-in curve on a warm card, as the result will run. The long measurements (stock, confirm, soak) are announced through `UndervoltIo::measuring`, and the run screen counts them down. The result table names the length of both runs and shows the fan speed each ended on next to its peak temperature. The optimize run of the clock profiles still measures stock for 30 s (its score is the reference of the power search); its table says so under it. |
99+
| The undervolt run measures stock for as long as it soaks | Five minutes each (`kUvBaselineS = kUvSoakS`). A card is not heated through after 30 s: on the reference card the short stock run read 59 to 61 C and 185 W where five minutes gave 67 C at 72 % fan and 196 W, so a result compared with it looked no cooler than stock. The run takes four and a half minutes longer for it. It also puts the clock to keep and the built-in curve on a warm card, as the result will run. The long measurements (stock, confirm, soak) are announced through `UndervoltIo::measuring`, and the run screen counts them down. The result table names the length of both runs and shows the fan speed each ended on next to its peak temperature. The optimize run of the clock profiles measures stock the same way before its search (`measure_stock`, the fans left to the driver) and shows that as the "before"; the search's own 30 s baseline stays, as the reference of the power search and of the stall and health thresholds. The all-in-one run hands its own stock measurement to the overclock step, so nothing is measured twice. |
100100
| Fans through NVML, stop zone via the driver | NVIDIA's legacy NVAPI fan API is gone on RTX 20-series and newer; NVML's `nvmlDeviceSetFanSpeed_v2` is public and verified by reading the target back. Below the stop threshold the driver owns the fans, so no failure of this app can leave them stopped. |
101101
| Tray app plus logon task | Driver settings are volatile: a reboot or driver reset clears them. The logon task starts the tray app, whose watchdog keeps the tune applied; three crashing logons in a row switch it off. |
102102
| An update is one click, and never more than that | The app asks GitHub for the latest release (`core/update`) and shows one line in the sidebar; nothing is downloaded until it is clicked. The zip must come from this project's releases over HTTPS and match the SHA-256 the GitHub API publishes for it, and both executables in it must carry the version the release claims. The order of these steps and every refusal are `install_release` in `core/update`, which gets its steps as callbacks and is tested with a fake source; a release without a usable SHA-256 is refused there too, not only by the parser, because an empty expectation would equal the hash of a file that cannot be read. Elevated, the download is unpacked under `%ProgramData%\GpuAutoOptimizer\update`, which only administrators can write, so nothing can be swapped between the check and the copy. The executables are replaced where they run from (a running one is renamed aside) and the app starts again with `--resume`, which keeps a tune that is applied under the watchdog. The trust this rests on is the GitHub account: the executables are not code-signed. |

‎docs/hardware-checks.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ root; adjust the path for a Debug build.
109109
| 79 | The page scrolls with the mouse on any card | Start `GpuAutoOptimizer.exe` in a window that is too low for the whole dashboard (its default size is). Turn the mouse wheel with the cursor on the **All in one** card, on the **Undervolt** card, on one of the four profile cards and on the tuning card | no | the page scrolls each time, as it does with the cursor between the cards | Pass for the two cards that failed (2026-10-08, reference RTX 4070, build of 6883a39 with the fix, version 0.4.0, started by a script that turned the wheel and took pictures). Before the fix the owner found that the page did not scroll with the mouse on the All in one card or on the Undervolt card: those two are as tall as their text, such a panel can be a pixel short of its contents, and it then kept the wheel for itself. Every card now passes the wheel on. With the cursor on the All in one card, two notches down: the card's top edge went from y 522 to y 355. With the cursor on the Undervolt card, one notch up: to y 439; one notch down again: back to y 355. The other cards were not tried with the script (they scrolled before). |
110110
| 80 | A caught fault in an NVML call: no hang, the app restarts itself | With a local test build with a temporary patch that is never committed: `--inject-nvml-fault=S` gives one `nvmlDeviceGetTemperature` call a bad result pointer S seconds after the app started, so the call faults inside `nvml.dll` after NVML has counted it in. Not elevated, no other copy running. (a) `GpuAutoOptimizer.exe --inject-nvml-fault=8`, window open; (b) `GpuAutoOptimizer.exe --tray --inject-nvml-fault=6`; (c) `GpuAutoOptimizer.exe --resume --after-fault --hidden --inject-nvml-fault=3`. Watch the processes and their command lines | no | (a) within 2 s of the fault a new copy runs with `--resume --after-fault`, the first copy has exited with code 0 and the new one responds, window open; (b) the same with `--hidden` added and no window; (c) the copy goes on running and responding on the library it no longer shuts down, and makes way for a new one only 60 s after it started | Pass (2026-10-10, reference RTX 4070, driver 617.42, test build of c7f18c9). What it is for: on 2026-10-09 the window froze for good during an All in one run (v0.4.0 build; the NVIDIA installer had replaced driver 617.14 by 617.42 ninety seconds before the run, with the app running, and the process still had the 617.14 `nvml.dll` loaded). The driver reset at memory +2550 MHz, as it does at that edge; the card ended at stock. A dump of the frozen process showed the window's thread in `nvmlShutdown`, waiting for NVML's count of calls in flight to reach 0 while it held the lock `nvmlInit` takes, the run's thread spinning on that lock in `nvmlInit`, the count at 1 and no thread inside any NVML call: a call had been thrown out by an access violation that our guard caught. Reproduced on its own on 617.42 with a 40-line program: after a guarded `nvmlDeviceGetTemperature` with a bad result pointer, `nvmlShutdown` does not return (waited 5 s) and neither does an `nvmlInit` after it; without the fault both return at once. With the fix: (a) fault at 8 s, new copy `--resume --after-fault` at 8.3 s, first copy gone at 8.7 s with exit code 0, new copy responding with its window; (b) fault at 6 s, new copy `--resume --after-fault --hidden` at 6.4 s, no window; (c) fault at 3 s, still running and responding at 20 s, new copy at 60.7 s. Not covered here, see check 81: anything elevated (the `boot.log` line, a tune kept applied across the restart, a fault during a run). |
111111
| 81 | A caught fault in an NVML call, elevated: the tune stays applied, a run ends properly | With the test build of check 80, elevated, no other copy running, a saved tune applied. (a) `GpuAutoOptimizer.exe --tray --inject-nvml-fault=20`, wait 60 s, `gao --status`, read the end of `boot.log`; (b) `GpuAutoOptimizer.exe --inject-nvml-fault=120`, start a run at once and leave the window open until the run has ended, then close the window | yes | (a) `boot.log` has `a call into the NVIDIA driver library faulted; restarting the app to load the library afresh`; the tray icon is back within seconds and says `tune kept applied`; `applied:` equals the saved tune. (b) the run goes on after the fault at two minutes and ends at its result or at stock; its result stays on the screen; the app restarts only when the window is closed, and its icon is back in the tray | Not run yet. |
112+
| 82 | The clock profiles measure stock for five minutes | Elevated, nothing else on the card: start this build's `GpuAutoOptimizer.exe`, choose **Best of my GPU** and run it to the end | yes | the run screen first counts down **Measuring the card at stock** from five minutes, with the fan tile on `(driver)`; the search starts after it with its own 30 s baseline; the result table says **Before (stock, 5 min)**, has no line about a stock run that was too short, and its stock temperature and fan speed are those of a card heated through (on the reference card about 67 C and 72 %, as in checks 76 and 77) | Not run yet. |
112113

113114
## Notes
114115

0 commit comments

Comments
 (0)