Skip to content

All in one: overclock, undervolt on top, fan tune and a final test in one click - #21

Merged
Rovey merged 19 commits into
mainfrom
all-in-one
Oct 8, 2026
Merged

Rovey merged 19 commits into
mainfrom
all-in-one

Conversation

@Rovey

@Rovey Rovey commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Builds on #20 (the undervolt in the app) and contains its commits; merging this one merges both.

What this adds

  • All in one, the new default: one run that measures the card at stock, finds the overclock of Best of my GPU, puts an undervolt on top of it at a clock half-way back to stock, measures the quietest fan curve that holds the temperature, and tests the whole for five minutes. gao --optimize all on the command line. The result screen shows the card at stock next to the card now.
  • A profile can carry an overclock and an undervolt together. On the card the core offset and the curve are one table, so the two are one shape: the core offset below the anchor, the anchor at the target, the points above it lower.
  • The undervolt can be searched on top of a saved overclock (gao --undervolt --on-overclock), and at a clock of the caller's choosing.
  • A fan tune (core/fan_tune): the lowest fan speed, in steps of 5 %, at which the card settles at or under 5 C below the profile's limit, and a fan curve through that point.
  • One click is all of it: a run from the window that ends saved switches apply-at-logon on itself; a ticked box on the Optimize page declines it.
  • The fan tile says who has the fans during a run ((run), (tuning), (driver)), a final test that was disturbed by another program on the card is reported, and the README opens with what one click does and what it measured.

What it measured (reference RTX 4070, driver 617.14)

An overclock and an undervolt do not add up: an overclock already is an undervolt at every frequency. The undervolt on top of core +135 saved 5 W of 201 (check 74). They are two ends of one line, and All in one takes the middle.

Stock, 5 min All in one, 5 min (check 76) second run (check 77)
Score 5728 it/s 5820 it/s (+1.6 %) +0.9 %
Power 196 W 180 W (-8.2 %) -10.8 %
Fan speed 73 % 62 % 55 %
Temperature 67 C 71 C 71 C

Verification

  • cmake --workflow --preset ci: 348 test cases pass
  • Hardware checks 73 to 79 in docs/hardware-checks.md: a flat top over a core offset (73), the undervolt on top of the saved overclock (74), All in one from the command line (75) and from the window with nothing else on the card (76), the fan tile during a run (77), a saved run switching apply-at-logon on (78), the page scrolling with the mouse on a card (79)

Not verified

  • The owner's own walk through the window (the scroll bug of check 79 was found there)
  • Abort from the window during a run (the owner: it works)
  • Abort from the window during the fan tune
  • A real log-off and log-on with an All in one profile saved (the logon path was run by starting the tray app as the task does, with other profiles)
  • The label (tuning) itself during a fan tune (the run that showed the states still had the longer word, cut off), and a tile shrinking a value that does not fit
  • A run with "Keep the result applied at every logon" unticked
  • Any other card. The half-way clock and the 5 C fan margin are choices made on one RTX 4070.
  • One candidate in check 75 ran 2380 MHz instead of 2850 for 3 s and was counted as failed; not explained.

🤖 Generated with Claude Code

Rovey and others added 19 commits October 8, 2026 14:04
`gao --undervolt` saves what it found, like `gao --optimize` does: a profile
with the new preset `undervolt`, stock clocks and power, and the flat top
(the anchor's voltage, the clock, and the raise it was tested with). It
replaces a saved overclock, because a core offset and a flat top are one
table on the card. A profile whose undervolt cannot be read whole is no
profile. Applying it at logon and watching it follow in the next commits.

Hardware check 66 on the reference RTX 4070.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
apply_profile writes the profile's flat top on the curve point with the
saved voltage, after power and memory. It is refused, with the card at
stock, when the curve has no such point or when the point would have to be
raised further than the undervolt was tested with plus what the built-in
curve moves with temperature. A core offset next to an undervolt is refused
before any write. Every apply removes a flat top that is on the card first:
an offset write that asks for what the driver already reports changes
nothing. `gao --status` says whether the undervolt is applied, from the
curve: the offset read-out reports core +0 with a flat top on the card.

Hardware check 67 on the reference RTX 4070.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tray app's watchdog judges a profile with an undervolt by the curve (a
flat top at the saved voltage, stock, or something else), not by the core
offset, which reads +0 under a flat top. An undervolt that was reset is
re-applied; another program's offsets are left alone.

Two changes for every tune, overclock or undervolt:
- A driver reset that leaves the tune applied now counts like one that
  undid it. The tray app already sees driver resets (its display device is
  removed) and passes them on; a search's own resets are not counted.
- Giving up (a fourth reset within an hour) sets the card to stock instead
  of leaving whatever the driver kept, and the tune is no longer watched.

`gao --undervolt` no longer tells a running tray app to stand down: the
tuning lock holds it off during the run, as for `gao --optimize`, and
afterwards it finds the profile the run saved.

Hardware check 68 on the reference RTX 4070: re-applied three times, given
up at stock the fourth, a foreign offset left alone, and a forced driver
reset (which removes a flat top) survived and re-applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A fifth card, Undervolt, under the four clock profiles on the dashboard and
on the Optimize page. With it selected the button reads "Undervolt GPU" and
starts the undervolt search on the worker thread; the run screen lists its
probes, the result screen shows the clock, the voltage it now runs at next
to the card's own, and a before/after table. A run that ends saved leaves
the watchdog on, like an optimize run. The dashboard judges "applied now" by
the curve when an undervolt is saved, and shows it on a row of its own.

Hardware check 69 on the reference RTX 4070: the run started from the
window, saved, and was re-applied by the watchdog. The check also showed
that the saving shrinks once the card is warm (the built-in curve moves
under the written offsets) and that the result table compares a 30 s run
with a 300 s one; both are recorded there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ran and at what fan speed

During a run from the window the run screen draws the card's
voltage/frequency curve as the run writes it, read back once a second, over
the card's own curve, with a dot where the card runs now. An undervolt shows
as a flat top that starts one point further left with each candidate; an
overclock lifts the whole line. The result screen shows the curve as the run
left it.

The before/after table compared a 30 s run at stock with the 300 s soak
without saying so. It now names both durations in its headers, shows the fan
speed each run ended on next to its peak temperature (StabilityResult
end_fan_pct; the command line prints it too), and says that the short stock
run was not heated through, so that only score, clock and power compare
directly.

README and design notes: the Undervolt profile, the curve picture, the
watchdog's new rule, and what the hardware checks of this branch found: the
power saved depends on the temperature the card settles at (25, 22, 20 and
12 % in four runs), because the built-in curve moves under the written
offsets.

Hardware check 70 on the reference RTX 4070.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts long measurements down

The stock measurement of the undervolt run lasted 30 s and the soak of its
result 300 s. A card is not heated through after half a minute, so the
"before" temperature and fan speed read low and a result looked no cooler
than stock. The stock measurement now lasts as long as the soak. On the
reference RTX 4070 that is 67 C at 72 % fan and 196 W at stock against
61 C at 66 % fan and 166 W with the undervolt, where the short stock run
had read 59 to 61 C. The run takes four and a half minutes longer.

The search announces every measurement of 30 s or more (stock, confirm,
soak) and the run screen shows what is being measured, the time left and a
bar. The table's headers follow the real durations, so its note about a
short stock run no longer appears for an undervolt.

The optimize run of the clock profiles is unchanged: its 30 s stock score
is the reference of the power search.

Hardware check 71 on the reference RTX 4070.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ays on the anchor's voltage

A top written exactly flat does not stay flat: the card's built-in curve
moves with temperature, each point by its own step. On the reference
RTX 4070 the same offsets ran in two ways once the card was warm: on the
anchor's voltage one clock step lower (154 W), or on a higher voltage
holding the clock (163 to 168 W).

apply_flat_top now stores the points above the anchor 45 MHz below it
(kVfTailDropKhz), which makes the anchor the highest point whatever the
temperature does. The card reports for every point the most that it or any
point below it runs, so those points keep reading the anchor's frequency:
the top is written flat and read back first, as before, and then lowered in
one write that is checked by the offsets. A card that does not store them
is refused, at stock. `gao --curve-flatten <mV> <MHz> 0` still writes the
exactly flat top.

Hardware check 72 on the reference RTX 4070: never above 156 W with the
lowered points in five stress runs between 50 and 60 C, all stable; the
undervolt run's soak 155 W against 195 W at stock (20.5 % less) at 99.6 %
of the score, where the flat top had given 166 W (15.3 % less) at 99.8 %.
At the same clock the two shapes draw the same; the lowered points keep the
card from buying the last 15 MHz with 12 W.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A second result section next to the overclock one: 195 W at stock and
155 W with the undervolt (20.5 % less) at 99.6 % of the stress-test score,
59 C instead of 67 C and 64 % fan speed instead of 72 %, both sides five
minutes of load on a card that is heated through (hardware check 72). The
sentence in the Profiles section that carried these numbers now only says
what the lowered points above the anchor are for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tead of a radio button

The fan icon was a glyph that is a ring of dots: the icon fonts of Windows
have no fan. It is now drawn (a casing ring, a hub and three swept blades)
on the Fan speed tile and in the navigation.

The profile cards showed a radio button, an icon and the title in a row.
The radio button said what the card's border and colour already say, and
the icons sat unevenly next to the titles because the font's glyphs differ
in width and in where they sit in their cell. Each card now has its icon in
a square badge, centred by the glyph's own outline, with the title level
with it; the selected card's badge is filled.

README: the undervolt result section is titled "almost the same speed"
(the measurement is 99.6 % of the score), and the dashboard picture is new.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tile centred a block of 2.45 em, but the label and the value were laid
out with the style's own line spacing between them, so the block was taller
than that and the value hung at the bottom of the tile, away from the icon
and the label. They are now spaced as the block assumes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A core offset and the voltage/frequency curve are one table on the card, so
an undervolt on top of an overclock is one shape: the core offset on every
point below the anchor, the anchor raised further, the points above it
lower. apply_flat_top takes that base offset, levels the curve to it first
and keeps it on the points below the anchor; curve_state knows a flat top
over an offset from the bare offset and from stock.

apply_profile no longer refuses a profile with both. It checks the flat top
against the built-in curve before any clock is written (the raise counts
from there, offset included), sets power, core and memory, and writes the
flat top over the core offset.

`gao --curve-flatten <mV> <MHz> [<drop> [<core>]]` writes it by hand.

Hardware check 73 on the reference RTX 4070: core +135 alone ran 2895 MHz
at 199 W and 67 C; with a flat top over it at 1015 mV, 2880 MHz at 172 W
and 62 C, stable, and the driver still reports core +135.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
find_undervolt takes the overclock it runs on (power limit, core and memory
offsets): it sets it after every reset to stock, keeps the clock the card
runs with it, writes its flat tops over the core offset and counts every
raise over the built-in curve, offset included: for the journal, for the
margin and for the result. `gao --undervolt --on-overclock` runs it on the
saved tune and saves the undervolt into that profile.

Hardware check 74 on the reference RTX 4070: it works, and adds little.
Core +135 at 105 % ran 2906 MHz on 201 W; the undervolt on top of it,
2895 MHz on 196 W. With the margin of 70 % on the raise that held (225 MHz
over the built-in curve) 150 MHz is left, and the overclock is 135 of it:
an overclock already is an undervolt at every frequency. The two are ends
of one line, not gains that add up.

curve_state: a flat top whose anchor needed no raise of its own (the offset
curve already ran the target there; only the points above are stored lower)
is a flat top. The run above ended on one, and `gao --status` called it
"other offsets".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y measuring

tune_fan sets the fans by hand under a running load, waits for the
temperature to settle, and goes five percent lower until the card settles
above the target (the profile's limit minus 5 C) or the fans' minimum is
reached; a start that is too quiet goes up instead. The result is the
lowest speed that held and a fan curve through that point. A load that
stops computing right ends the tune, and every way out that is not a
success hands the fans back to the driver.

Core and unit tests only; nothing calls it yet. A probe result now carries
the temperature at its last sample.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and a final test in one run

`run_all_in_one`, `gao --optimize all` and the card "All in one" in the
window (the default now) run, under one tuning lock:

  1. five minutes at stock, to compare with;
  2. the overclock of Best of my GPU;
  3. an undervolt on top of it, at a clock half-way between what the card
     ran at stock and what it runs with the overclock;
  4. the fan tune: the quietest speed that holds the temperature target;
  5. five minutes with everything applied, fans on the tuned curve.

The result screen shows the card at stock next to the card now. A step that
fails keeps what the steps before it found; a stop request ends at stock.

The half-way clock is a choice. An overclock and an undervolt are two ends
of one line (hardware check 74), and the middle is where the result beats
stock on both counts. find_undervolt takes that clock from its caller and
then also accepts the overclock's curve cut off there, with no point raised
further, as a result: it is what is left when the margin allows no more.

A final test that scores more than 3 % under the same settings' soak a few
minutes earlier, at the same clock, is reported as disturbed: another
program was using the card. The Optimize page asks to close other programs.

Hardware check 75 on the reference RTX 4070: the run went through in 32
minutes and saved core +135, mem +1600, 2850 MHz at 1035 mV, fans 52 %.
Its clean numbers are the undervolt step's soak, 5725 it/s on 172 W against
5641 it/s on 194 W at stock (1.5 % more score, 11 % less power); the final
test itself was disturbed by an emulator on 11 % of the card and read 6 %
low. The run from the window is still to do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd as hardware check 76

A run from the window with nothing else on the card (no process above
0.5 % of its 3D engine), 34 minutes: core +135 MHz, memory +1500 MHz,
power limit 105 %, the overclock's curve cut off at 2850 MHz and 1035 mV,
fans 52 % at 70 C. Five minutes at stock against five minutes with it:
5728 and 5820 it/s (+1.6 %), 196 and 180 W (-8.2 %), fan speed 73 and
62 %, 67 and 71 C (the fan tune aims for 70 C; with the driver's own fan
curve the same settings ran at 63 C on 176 W).

README: a third result section for All in one with the result screen, the
table and a picture of the run, and why its gains are smaller than those of
the overclock and the undervolt alone: the two are ends of one line and
this is the middle. The dashboard picture shows the result applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The label next to the fan speed came from the app's own curve driver, which
is released for the length of a run: while the all-in-one run was setting
the fans by hand to measure them, the tile read "(driver)".

During a run the card now decides: a speed set by hand is the run's, shown
as "(run)", or as "(measuring)" while the run tunes the fans; a card whose
fans the run left to the NVIDIA driver, or handed back, reads "(driver)".
After the run the app's own driver speaks again. The decision is
`fan_owner` in core/fan_curve, with unit tests; the window knows the fan
tune by the start of what it announces (`is_fan_tune_measurement`).

Not seen on screen yet: the elevation prompt for the run was cancelled
twice. Hardware check 77 is added with an empty result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ever cuts its value off

Hardware check 77, an All in one run from the window: the tile read
"(driver)" at stock and in the undervolt step, "(run)" in the overclock
step and the final test, and during the fan tune "55 % (measur": the right
state and speed, with "(measuring)" cut off at the edge of the tile. The
word is now "(tuning)", as long as "(driver)", and a tile draws a value
that is wider than itself smaller. The new build was seen idle after the
run ("0 % (driver)"); the word itself in a fan tune was not.

That run is a second clean one for All in one: 5731 and 5781 it/s (+0.9 %),
195 and 174 W (-10.8 %), fans 72 and 55 %; the README mentions it next to
the first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on; the README says so first

A run from the window that ends saved now switches apply-at-logon on
itself, unless the box "Keep the result applied at every logon" on the
Optimize page (shown while it is still off, ticked) is unticked. The result
screen then says there is nothing left to do. Until now keeping the result
was a second switch to find after the run.

README: the first lines say what the app is for (one click tunes the card,
then it keeps it that way), a card with the measured numbers of that one
click sits above the screenshot, and "One click, then nothing" walks
through the button, the before/after and what keeps the result applied,
with what protects the user and the one time the app asks again (a driver
update). The result of All in one comes first; the overclock and the
undervolt follow as "all of the speed" and "all of the saving".

Hardware check 78 on the reference RTX 4070: with apply-at-logon off, a
Cool & efficient run from the window ended with it on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the cursor on the All in one card or on the Undervolt card the mouse
wheel did nothing: those two panels 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 instead of passing it to the page. No card scrolls itself, so every
card now passes the wheel on. Hardware check 79: seen on both cards.

The version is 0.4.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Rovey
Rovey marked this pull request as ready for review October 8, 2026 21:35
@Rovey
Rovey merged commit 1a303f6 into main Oct 8, 2026
2 checks passed
@Rovey
Rovey deleted the all-in-one branch October 8, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant