release 0.3.14: merge ember-tune 59631f0 (the Measured wording for a baseline row; the helper's commands under its fixed folder)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
149a4bbec3
8 changed files with 154 additions and 11 deletions
|
|
@ -906,6 +906,21 @@ pub fn tuned_line(mhs: f64, watts: f64, eff: f64) -> String {
|
|||
format!("Tuned: {mhs:.1} MH/s at {watts:.0} W ({eff:.3} MH/W)")
|
||||
}
|
||||
|
||||
/// The row's line for a baseline (measure-only) result: the card was measured as it runs, nothing was set.
|
||||
/// (6 October 2026: PC 1's rows read "Tuned: 114.2 MH/s at 305 W" for a baseline, which is not a tune.)
|
||||
pub fn measured_line(mhs: f64, watts: f64, eff: f64) -> String {
|
||||
format!("Measured: {mhs:.1} MH/s at {watts:.0} W ({eff:.3} MH/W)")
|
||||
}
|
||||
|
||||
/// The row's line for a plan's result.
|
||||
pub fn result_line(kind: PlanKind, mhs: f64, watts: f64, eff: f64) -> String {
|
||||
if kind == PlanKind::Baseline {
|
||||
measured_line(mhs, watts, eff)
|
||||
} else {
|
||||
tuned_line(mhs, watts, eff)
|
||||
}
|
||||
}
|
||||
|
||||
/// Why a card cannot be tuned beyond measuring, or None when both knobs are available.
|
||||
pub fn control_reason(vendor: &str, limits: &Limits, device: &str, power_control: bool, amd_helper: bool) -> Option<String> {
|
||||
match vendor {
|
||||
|
|
@ -992,6 +1007,13 @@ mod tests {
|
|||
assert!(Run::applied(&step, Readback { limit_w: 90.0, acked: false }, 100.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_baseline_result_reads_measured_and_a_tune_reads_tuned() {
|
||||
assert_eq!(result_line(PlanKind::Baseline, 114.2, 305.0, 0.3744), "Measured: 114.2 MH/s at 305 W (0.374 MH/W)");
|
||||
assert_eq!(result_line(PlanKind::Full, 127.71, 226.8, 0.5632), "Tuned: 127.7 MH/s at 227 W (0.563 MH/W)");
|
||||
assert_eq!(result_line(PlanKind::Confirm, 127.71, 226.8, 0.5632), "Tuned: 127.7 MH/s at 227 W (0.563 MH/W)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limits_never_exceed_the_vendor_or_undercut_the_floor() {
|
||||
let l = l5090();
|
||||
|
|
|
|||
|
|
@ -1817,7 +1817,7 @@ impl Engine {
|
|||
// once, ever (src/powertask.rs): a registered task sets the caps with no prompt; the readback judges it
|
||||
if cfg!(windows) && self.power_task_registered() {
|
||||
let pairs: Vec<(String, u64)> = self.st().mining.cards.iter().filter(|c| c.vendor == "nvidia" && c.enabled && c.present() && c.power_default_w > 0.0 && !c.power_applied).map(|c| (c.device.clone(), requested_watts(c).round() as u64)).collect();
|
||||
let dir = self.sweep_dir();
|
||||
let dir = crate::powertask::helper_dir();
|
||||
let shared = self.shared.clone();
|
||||
self.shared.log("power cap: through the Igneum Power Helper task (no prompt)");
|
||||
std::thread::spawn(move || {
|
||||
|
|
@ -1936,8 +1936,9 @@ impl Engine {
|
|||
}
|
||||
if cfg!(windows) && self.power_task_registered() {
|
||||
// the kill switch: the task unregisters itself (elevated) and exits; nothing is left behind
|
||||
let dir = self.sweep_dir();
|
||||
let _ = std::fs::write(dir.join("cmd.txt"), "remove\n");
|
||||
let hdir = crate::powertask::helper_dir();
|
||||
let _ = std::fs::create_dir_all(&hdir);
|
||||
let _ = std::fs::write(hdir.join("cmd.txt"), "remove\n");
|
||||
match crate::powertask::start() {
|
||||
Ok(()) => self.shared.log("power control off: the Igneum Power Helper task removes itself"),
|
||||
Err(e) => self.shared.log(&format!("power control off: the task could not be started to remove itself ({e}); remove it in Task Scheduler")),
|
||||
|
|
@ -2142,6 +2143,16 @@ impl Engine {
|
|||
self.shared.runtime.app_dir.join("sweep")
|
||||
}
|
||||
|
||||
/// Where this engine's helper reads its commands: the task's own folder when the task is the helper, else this
|
||||
/// engine's sweep folder (the prompted helper script was handed that path on its command line).
|
||||
fn helper_cmd_dir(&self) -> PathBuf {
|
||||
if self.sweep_helper_is_task {
|
||||
crate::powertask::helper_dir()
|
||||
} else {
|
||||
self.sweep_dir()
|
||||
}
|
||||
}
|
||||
|
||||
/// The manifest's tuning object (<app data>/tuning.json, written by the updater from the signed manifest), or
|
||||
/// None: the Ember settings (kill switch, thresholds) and the fleet priors live under it.
|
||||
fn tuning_object(&self) -> Option<Value> {
|
||||
|
|
@ -2470,8 +2481,11 @@ impl Engine {
|
|||
format!("sh \"{}\" \"{}\" \"{}\" {} {}", script.display(), dir.display(), smi, c.device, restore)
|
||||
};
|
||||
if cfg!(windows) && self.power_task_registered() {
|
||||
// once, ever: the registered task is the helper; it reads the same command file, no prompt
|
||||
let _ = std::fs::write(dir.join("cmd.txt"), format!("{} dev {}\n", crate::platform::unix_now() % 1_000_000, c.device));
|
||||
// once, ever: the registered task is the helper; it reads ITS command file (powertask::helper_dir, never
|
||||
// under a scratch IGNEUM_APP_DATA), no prompt
|
||||
let hdir = crate::powertask::helper_dir();
|
||||
let _ = std::fs::create_dir_all(&hdir);
|
||||
let _ = std::fs::write(hdir.join("cmd.txt"), format!("{} dev {}\n", crate::platform::unix_now() % 1_000_000, c.device));
|
||||
crate::powertask::start()?;
|
||||
self.shared.log("tune helper: the Igneum Power Helper task (no prompt)");
|
||||
self.sweep_helper = true;
|
||||
|
|
@ -2491,7 +2505,7 @@ impl Engine {
|
|||
|
||||
fn sweep_helper_quit(&mut self) {
|
||||
if self.sweep_helper {
|
||||
let _ = std::fs::write(self.sweep_dir().join("cmd.txt"), "quit\n");
|
||||
let _ = std::fs::write(self.helper_cmd_dir().join("cmd.txt"), "quit\n");
|
||||
if self.sweep_helper_is_task {
|
||||
// the task exits on quit and reports nothing back; the next tune starts it again
|
||||
self.sweep_helper = false;
|
||||
|
|
@ -2553,7 +2567,7 @@ impl Engine {
|
|||
}
|
||||
let dev = device.to_string();
|
||||
let cmd = format!("{seq}0 dev {dev}\n{seq}1 pl {w}\n{seq}2 {}\n{seq}3 {}\n", if clock > 0 { format!("lgc {clock}") } else { "rgc".to_string() }, if mem > 0 { format!("lmc {mem}") } else { "rmc".to_string() });
|
||||
let _ = std::fs::write(self.sweep_dir().join("cmd.txt"), cmd);
|
||||
let _ = std::fs::write(self.helper_cmd_dir().join("cmd.txt"), cmd);
|
||||
// the helper polls twice a second and nvidia-smi answers within a second or two
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(Duration::from_secs(4));
|
||||
|
|
@ -2749,7 +2763,7 @@ impl Engine {
|
|||
c.tune_clock_mhz = row.point.clock_mhz;
|
||||
c.tune_mem_mhz = row.point.mem_mhz;
|
||||
c.tune_source = kind.name().into();
|
||||
c.tune_line = crate::ember::tuned_line(row.mhs, row.watts, row.eff);
|
||||
c.tune_line = crate::ember::result_line(kind, row.mhs, row.watts, row.eff);
|
||||
c.tune_curve = run.rows.iter().map(|r| r.json()).collect();
|
||||
let control = c.tune_control;
|
||||
if kind == crate::ember::PlanKind::Baseline {
|
||||
|
|
@ -2792,9 +2806,9 @@ impl Engine {
|
|||
let before = run.rows.first().filter(|_| kind == crate::ember::PlanKind::Full).cloned();
|
||||
let record = crate::ember::record_json(crate::platform::unix_now_f(), &crate::config::fingerprint8(&self.shared.runtime.machine_id), VERSION, crate::manifest::platform_name(), &name, &vendor, &driver, &class, kind, &run.rows, Some(&row), before.as_ref(), floor);
|
||||
self.sweep_say(&format!("TUNE {record}"));
|
||||
let line = crate::ember::tuned_line(row.mhs, row.watts, row.eff);
|
||||
let line = crate::ember::result_line(kind, row.mhs, row.watts, row.eff);
|
||||
match kind {
|
||||
crate::ember::PlanKind::Baseline => self.shared.event("info", &format!("{name}: measured as it runs: {line}{}", if control { String::new() } else { " (measure only; no control on this card)".into() })),
|
||||
crate::ember::PlanKind::Baseline => self.shared.event("info", &format!("{name}: {line}{}", if control { String::new() } else { " (measure only; no control on this card)".into() })),
|
||||
_ if pinned => self.shared.event("ok", &format!("{name}: best point {} ({line}); your setting stays pinned", point_words(&row.point))),
|
||||
_ => self.shared.event("ok", &format!("{name}: {line}, {} held", point_words(&row.point))),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ fn main() {
|
|||
if args.iter().any(|a| a == "--power-helper") {
|
||||
// the scheduled task's action (src/powertask.rs): elevated, runs only digit-argument nvidia-smi commands
|
||||
// from <app data>/app/sweep/cmd.txt, exits on quit, remove or 20 idle minutes
|
||||
let dir = powertask::sweep_dir(&platform::data_root().join("app"));
|
||||
let dir = powertask::helper_dir();
|
||||
std::process::exit(powertask::run_helper(&dir));
|
||||
}
|
||||
if args.iter().any(|a| a == "--launch") {
|
||||
|
|
|
|||
|
|
@ -103,6 +103,14 @@ pub fn data_root() -> PathBuf {
|
|||
if let Some(p) = std::env::var_os("IGNEUM_APP_DATA") {
|
||||
return PathBuf::from(p);
|
||||
}
|
||||
fixed_data_root()
|
||||
}
|
||||
|
||||
/// The platform's own root, with IGNEUM_APP_DATA ignored: where the Power Helper task (started by Windows with no
|
||||
/// environment of ours) reads its command file, so a measurement engine under a scratch root (IGNEUM_APP_DATA set)
|
||||
/// writes its task commands here and not under its own root. 6 October 2026: found while planning the unelevated
|
||||
/// climb through the task; the elevated runs never crossed it (they set the limits directly).
|
||||
pub fn fixed_data_root() -> PathBuf {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
home().join("Library").join("Application Support").join("Igneum")
|
||||
|
|
|
|||
|
|
@ -272,6 +272,12 @@ pub fn sweep_dir(app_dir: &Path) -> PathBuf {
|
|||
app_dir.join("sweep")
|
||||
}
|
||||
|
||||
/// The one folder the task's helper reads: `<fixed data root>/app/sweep` (never under IGNEUM_APP_DATA). Every
|
||||
/// engine that talks to the task writes its command file here; the `--power-helper` entry reads here.
|
||||
pub fn helper_dir() -> PathBuf {
|
||||
sweep_dir(&crate::platform::fixed_data_root().join("app"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -293,6 +299,9 @@ mod tests {
|
|||
assert_eq!(parse_line("12 reregister C:\\evil.exe"), None, "no path argument: the helper picks the install folder itself");
|
||||
assert_eq!(smi_args("0", &HelperCmd::Reregister), None);
|
||||
assert!(readback_command().contains("Actions[0].Execute"));
|
||||
// the helper's folder never follows IGNEUM_APP_DATA (the task has no environment of ours)
|
||||
assert!(helper_dir().ends_with(Path::new("app").join("sweep")));
|
||||
assert!(helper_dir().starts_with(crate::platform::fixed_data_root()));
|
||||
// nothing else: no shell, no path, no string argument, no oversized number
|
||||
for bad in ["7 pl 460; calc", "7 pl -460", "7 pl 4.60", "7 lgc 0,2472", "7 rm C:\\x", "x pl 460", "7 pl", "7 lgc 12345678", "7 dev ../1", "", "7 pl 460 extra"] {
|
||||
assert_eq!(parse_line(bad), None, "{bad:?}");
|
||||
|
|
|
|||
|
|
@ -2301,3 +2301,9 @@ acknowledged true" = the applied rule demanded watts from a card that reports of
|
|||
was read on every tick (amd_watts_source=engine_telemetry, 363 samples). The helper registered on the scratch copy
|
||||
(fixed 200362a: task_exe, the reregister verb). PC 1 mined through the installed app again by 16:44Z: 170.6 MH/s over
|
||||
the three cards, 0 faults.
|
||||
|
||||
Re-point and proof, 16:48 to 16:53Z: the Power Helper task re-pointed by the helper itself ("1 reregister ok: the
|
||||
task now runs ...Programs\Igneum Miner\igneum-app.exe --power-helper"), then the installed app's caps through the
|
||||
task with no prompt ("-pl 460: set to 460.00 W from 575.00 W", "-pl 160: set to 160.00 W from 100.00 W"). Task
|
||||
Running, Highest, user Admin. Cards: 5090 221 W at 1845 MHz, 4070 75.8 W at 1860 MHz, 9070 XT 202 W, all mining
|
||||
through the installed app. Ember closed 16:53Z.
|
||||
|
|
|
|||
|
|
@ -282,12 +282,28 @@ After the run (nvidia-smi, the engine gone, the installed miners not yet back):
|
|||
170.6 MH/s, 0 faults (console, 16:44Z). The 9070 XT's ADLX state read "gmax 0 plimit 0 factory 0" (before: factory
|
||||
1): zero offsets, the factory flag cleared by the engine's set of 0; the 3.0 coordinator's reset job puts it back.
|
||||
|
||||
### The re-point and the no-prompt proof (6 October 2026, 16:48 to 16:53Z)
|
||||
|
||||
`ember-repoint-pc1-1` (unelevated): task action before = the scratch copy; kit-7 (bd7fcf4) put in its place;
|
||||
`Start-ScheduledTask`; `1 reregister` written; helper.log at 16:48:42Z: "1 reregister ok: the task now runs
|
||||
C:\Users\Admin\AppData\Local\Programs\Igneum Miner\igneum-app.exe --power-helper"; action read back = the
|
||||
installed exe. Three seconds later the installed app's own caps went through the task with no prompt (helper.log:
|
||||
"nvidia-smi -i 0 -pl 460: Power limit for GPU 01:00.0 was set to 460.00 W from 575.00 W"; "-i 1 -pl 160: set to
|
||||
160.00 W from 100.00 W"). `ember-proof-pc1-2` (reads only): task state Running, run level Highest, user Admin;
|
||||
5090 221 W at 1845 MHz, 4070 75.8 W at 1860 MHz, 9070 XT 202 W; all three mining through the installed app. The
|
||||
Security log's process-start audit is off on PC 1, so a consent.exe count is unreadable; the helper's log is the
|
||||
record. Known in 0.3.13 on PC 1 until 0.3.14: the rows show the app's own earlier baseline (no /api/tune-progress
|
||||
to receive the run's result), and the 80% caps are the app's setting; the draw is the tuned one regardless because
|
||||
the driver holds the clock locks and the caps do not bind.
|
||||
|
||||
## 8a. Next-cut notes (for the 0.3.12 shipper)
|
||||
|
||||
For the 0.3.14 cut (6 October 2026, after run 6), on ember-tune:
|
||||
|
||||
| Commit | What | Where |
|
||||
|---|---|---|
|
||||
| (this cut) | every task command goes to the helper's fixed folder (`powertask::helper_dir` = `<platform data root>/app/sweep`, IGNEUM_APP_DATA ignored): a scratch-root measurement engine talking to the task wrote under its own root, where the task never reads (found 6 October 2026 planning the unelevated climb; the elevated runs set limits directly and never crossed it) | platform.rs `fixed_data_root`, powertask.rs, engine.rs `helper_cmd_dir`, main.rs + test |
|
||||
| (this cut) | a baseline result's row reads "Measured: ..." not "Tuned: ..." (`ember::result_line`); PC 1's 0.3.13 rows read "Tuned: 114.2 MH/s at 305 W" for a baseline | ember.rs, engine.rs + test |
|
||||
| bd7fcf4 | AMD: a power step with no limit readback is applied on the acknowledgement (run 6 aborted the 9070 XT's ladder at step 1 on "card reports 0 W") | ember.rs `Run::applied` + test |
|
||||
| 200362a | `task_exe` prefers the installed exe for every path that is not itself an install candidate; the helper's `reregister` verb; the clock ladder and floor to 45%; a chosen point on the floor says so; the fleet page's team rows | powertask.rs, ember.rs, engine.rs, site |
|
||||
| 5165de7, 9fba504, 5b0968f | Ember 2's Settings (goal, price, climb), £/day and the curve on the card; the playbook's AMD watts source and kit-7 preference; run 6 notes | ui, relay/playbooks, docs |
|
||||
|
|
|
|||
68
relay/playbooks/ember-installed-tune-pc1.ps1
Normal file
68
relay/playbooks/ember-installed-tune-pc1.ps1
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# Ember: the SHIPPED app tunes itself (6 October 2026). Unelevated, no kit, no second engine, no --stop-miners:
|
||||
# the installed app (0.3.14 or later: Ember Tune, the Power Helper task, /api/tune/goal, the climb) runs one tune
|
||||
# per card through the registered task (no prompt) while it mines. This script only asks and watches:
|
||||
# POST api/tune/goal (the climb on, balanced) and POST api/sweep/start per card, then reads api/state and the app's
|
||||
# own log for the TUNE rows. It never quits, pauses or resumes the installed app (the 5 October rule) and never
|
||||
# touches the cards itself. At the end the climb switch goes back to what it was.
|
||||
# Order: the AMD card's ladder first (the ADLX apply-on-ack fix, bd7fcf4), then each NVIDIA card's climb.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Say([string] $s) { Write-Output ('[' + (Get-Date -Format 'HH:mm:ss') + '] ' + $s) }
|
||||
$appDir = Join-Path $env:LOCALAPPDATA 'igneum\app'
|
||||
$u = Join-Path $appDir 'app.url'
|
||||
if (-not (Test-Path -LiteralPath $u)) { Write-Output 'RESULT TUNE error=no_app_url (installed app not running)'; exit 1 }
|
||||
$base = (Get-Content -LiteralPath $u -Raw).Trim()
|
||||
function State() { try { return ((Invoke-WebRequest -Uri ($base + 'api/state') -UseBasicParsing -TimeoutSec 5).Content | ConvertFrom-Json) } catch { return $null } }
|
||||
function Post([string] $path, [string] $json) { try { Invoke-WebRequest -Uri ($base + $path) -Method POST -Body $json -ContentType 'application/json' -UseBasicParsing -TimeoutSec 5 | Out-Null; return $true } catch { Say ($path + ': ' + $_.Exception.Message); return $false } }
|
||||
$s = State
|
||||
if (-not $s) { Write-Output 'RESULT TUNE error=state_unreadable'; exit 1 }
|
||||
$ver = [string]$s.version
|
||||
Write-Output ('RESULT TUNE installed app ' + $ver + ' power_control=' + $s.settings.power_control + ' tuning_off=' + $s.settings.tuning_off + ' climb_before=' + $s.settings.tune_climb + ' goal_before=' + $s.settings.tune_goal)
|
||||
$t = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue
|
||||
Write-Output ('RESULT TUNE task ' + $(if ($t) { $t.Actions[0].Execute + ' state=' + $t.State } else { 'NOT REGISTERED' }))
|
||||
if (-not $t) { Write-Output 'RESULT TUNE error=task_not_registered (the NVIDIA climbs would need a prompt; stopping)'; exit 1 }
|
||||
if (-not $s.settings.power_control) { Write-Output 'RESULT TUNE error=power_control_off'; exit 1 }
|
||||
$climbBefore = [bool]$s.settings.tune_climb
|
||||
$cards = @($s.mining.cards | Where-Object { $_.enabled -and ($_.vendor -eq 'nvidia' -or $_.vendor -eq 'amd') -and $_.name -notmatch 'Radeon\(TM\) Graphics' })
|
||||
Write-Output ('RESULT TUNE cards ' + (($cards | ForEach-Object { $_.key + ' (' + $_.name + ', ' + $_.vendor + ', sweep_state ' + $_.sweep_state + ', line "' + $_.tune_line + '")' }) -join ' | '))
|
||||
foreach ($c in $cards) { Write-Output ('RESULT TUNE before key=' + $c.key + ' power_w=' + $c.power_w + ' power_pct=' + $c.power_pct + ' clock_cap=' + $c.clock_cap_mhz + ' mhs=' + $c.mhs + ' line=' + $c.tune_line) }
|
||||
# the climb on (NVIDIA cards; an AMD card has no clock knob and runs its ladder regardless)
|
||||
if (-not (Post 'api/tune/goal' '{"goal":"balanced","climb":true}')) { Write-Output 'RESULT TUNE error=tune_goal_refused'; exit 1 }
|
||||
Start-Sleep -Seconds 2
|
||||
$order = @($cards | Where-Object { $_.vendor -eq 'amd' }) + @($cards | Where-Object { $_.vendor -eq 'nvidia' })
|
||||
$deadline = (Get-Date).AddMinutes(27)
|
||||
$logs = Join-Path $appDir 'logs'
|
||||
function LogMark() { $l = Get-ChildItem -Path $logs -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($l) { return @{ f = $l.FullName; n = (Get-Content -LiteralPath $l.FullName).Count } } else { return @{ f = ''; n = 0 } } }
|
||||
function LogSince($m) { if (-not $m.f) { return @() }; $all = @(Get-Content -LiteralPath $m.f -ErrorAction SilentlyContinue); if ($all.Count -le $m.n) { return @() }; return @($all[$m.n..($all.Count - 1)]) }
|
||||
$rows = 0
|
||||
foreach ($c in $order) {
|
||||
if ((Get-Date) -gt $deadline) { Say 'budget spent before ' + $c.key; break }
|
||||
$m = LogMark
|
||||
$body = '{"key":"' + ($c.key -replace '"', '\"') + '"}'
|
||||
if (-not (Post 'api/sweep/start' $body)) { Write-Output ('RESULT TUNE error=sweep_start_refused key=' + $c.key); continue }
|
||||
Say ('tune asked for ' + $c.key)
|
||||
$seen = 0; $done = $false; $started = Get-Date
|
||||
while (-not $done -and (Get-Date) -lt $deadline) {
|
||||
Start-Sleep -Seconds 10
|
||||
$new = @(LogSince $m)
|
||||
while ($seen -lt $new.Count) { $l = [string]$new[$seen]; $seen++; if ($l -match 'TUNE (card=|chosen|start|aborted|progress)') { if ($l -notmatch 'TUNE progress') { Write-Output ('RESULT ' + ($l -replace '^.*?(TUNE )', 'TUNE ')) }; if ($l -match 'TUNE card=') { $rows++ } } }
|
||||
$st = State
|
||||
if ($st) {
|
||||
$cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1
|
||||
if ($cc) {
|
||||
$running = ($cc.sweep_state -eq 'running')
|
||||
if (-not $running -and ((Get-Date) - $started).TotalSeconds -gt 180) { $done = $true }
|
||||
if ($running -and $cc.tune_steps -gt 0 -and ($seen % 6 -eq 0)) { Say ($c.key + ': step ' + $cc.tune_step + ' of ' + $cc.tune_steps + ', ' + $cc.sweep_note) }
|
||||
}
|
||||
}
|
||||
}
|
||||
$st = State
|
||||
if ($st) { $cc = $st.mining.cards | Where-Object { $_.key -eq $c.key } | Select-Object -First 1; if ($cc) { Write-Output ('RESULT TUNE after key=' + $c.key + ' power_w=' + $cc.power_w + ' power_pct=' + $cc.power_pct + ' clock_cap=' + $cc.clock_cap_mhz + ' line=' + $cc.tune_line + ' source=' + $cc.tune_source + ' note=' + $cc.sweep_note); if ($cc.tune_curve) { Write-Output ('RESULT TUNE curve key=' + $c.key + ' ' + (($cc.tune_curve | ForEach-Object { ($_.clock_mhz) + 'MHz/' + ($_.mem_mhz) + 'mem/' + ($_.power_pct) + '%:' + $_.mhs + 'MH/s ' + $_.watts + 'W ' + $_.eff + ' ' + $_.mark }) -join ' | ')) } } }
|
||||
}
|
||||
# the climb switch back to what it was
|
||||
Post 'api/tune/goal' ('{"climb":' + $(if ($climbBefore) { 'true' } else { 'false' }) + '}') | Out-Null
|
||||
Write-Output ('RESULT TUNE climb_restored=' + $climbBefore + ' rows=' + $rows)
|
||||
$smi = Join-Path $env:ProgramFiles 'NVIDIA Corporation\NVSMI\nvidia-smi.exe'
|
||||
if (-not (Test-Path $smi)) { $smi = Join-Path $env:SystemRoot 'System32\nvidia-smi.exe' }
|
||||
if (Test-Path $smi) { Write-Output ('RESULT TUNE nvidia_now ' + ((& $smi --query-gpu=index,name,power.draw,power.limit,clocks.gr,clocks.mem --format=csv,noheader 2>&1 | Out-String) -replace "`r?`n", ' | ')) }
|
||||
if ($rows -eq 0) { Write-Output 'RESULT TUNE error=no_rows'; exit 1 }
|
||||
exit 0
|
||||
Loading…
Reference in a new issue