Ember run 6: the helper registered on the scratch copy, so task_exe prefers the installed exe for every path that is not itself an install candidate, and the helper gains a reregister verb (no path argument; re-points the task at the install folder it finds itself, logs the action read back); the clock ladder and floor go to 45% with the 1% rate tolerance as the guard; a chosen point on the floor says floor, not optimum; the 5090 row (84 W saved for 0.15% of rate) on the fleet page; the re-point and no-prompt proof playbook; run 6 notes, threat note, bench log
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
f20cc03b98
commit
12e045b64e
9 changed files with 227 additions and 23 deletions
|
|
@ -20,9 +20,11 @@ use std::time::{Duration, Instant};
|
||||||
/// The power steps, percent of the card's default limit, highest first (the same ladder as src/sweep.rs).
|
/// The power steps, percent of the card's default limit, highest first (the same ladder as src/sweep.rs).
|
||||||
pub const POWER_STEPS_PCT: [u32; 6] = [100, 90, 80, 70, 60, 50];
|
pub const POWER_STEPS_PCT: [u32; 6] = [100, 90, 80, 70, 60, 50];
|
||||||
/// The clock steps, percent of the card's maximum core clock, highest first; 100 = unlocked (the card's own boost).
|
/// The clock steps, percent of the card's maximum core clock, highest first; 100 = unlocked (the card's own boost).
|
||||||
pub const CLOCK_STEPS_PCT: [u32; 5] = [100, 90, 80, 70, 60];
|
/// 6 October 2026, run 6: the 5090's best MH/W sat on the 60% floor (1,854 MHz: 0.563 MH/W, the rate within 0.15%),
|
||||||
|
/// so the ladder and the floor go to 45% of the maximum; the 1% rate tolerance is the guard below that
|
||||||
|
pub const CLOCK_STEPS_PCT: [u32; 7] = [100, 90, 80, 70, 60, 50, 45];
|
||||||
/// A card's clock floor when the vendor reports none: this share of its maximum core clock.
|
/// A card's clock floor when the vendor reports none: this share of its maximum core clock.
|
||||||
pub const CLOCK_FLOOR_PCT: u32 = 60;
|
pub const CLOCK_FLOOR_PCT: u32 = 45;
|
||||||
/// A point may lose this much rate against the fastest point and still win on MH per watt (the manifest can change it).
|
/// A point may lose this much rate against the fastest point and still win on MH per watt (the manifest can change it).
|
||||||
pub const RATE_TOLERANCE_PCT: f64 = 1.0;
|
pub const RATE_TOLERANCE_PCT: f64 = 1.0;
|
||||||
/// A step whose hottest GPU reading reaches this is marked hot and cannot win (the engine aborts at 90).
|
/// A step whose hottest GPU reading reaches this is marked hot and cannot win (the engine aborts at 90).
|
||||||
|
|
@ -601,7 +603,7 @@ pub fn prior_of(tuning: Option<&serde_json::Value>, key: &str, min_samples: u32)
|
||||||
|
|
||||||
/// The fleet record: one `TUNE {json}` line. `machine` is a hash of the install id (never the id, never an address).
|
/// The fleet record: one `TUNE {json}` line. `machine` is a hash of the install id (never the id, never an address).
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn record_json(ts: f64, machine_hash: &str, app: &str, os: &str, card: &str, vendor: &str, driver: &str, class: &str, plan: PlanKind, rows: &[Row], chosen: Option<&Row>, before: Option<&Row>) -> serde_json::Value {
|
pub fn record_json(ts: f64, machine_hash: &str, app: &str, os: &str, card: &str, vendor: &str, driver: &str, class: &str, plan: PlanKind, rows: &[Row], chosen: Option<&Row>, before: Option<&Row>, floor: bool) -> serde_json::Value {
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"ts": ts.round(),
|
"ts": ts.round(),
|
||||||
"machine": machine_hash,
|
"machine": machine_hash,
|
||||||
|
|
@ -620,6 +622,8 @@ pub fn record_json(ts: f64, machine_hash: &str, app: &str, os: &str, card: &str,
|
||||||
"eff": chosen.map(|r| r4(r.eff)).unwrap_or(0.0),
|
"eff": chosen.map(|r| r4(r.eff)).unwrap_or(0.0),
|
||||||
"mhs": chosen.map(|r| r2(r.mhs)).unwrap_or(0.0),
|
"mhs": chosen.map(|r| r2(r.mhs)).unwrap_or(0.0),
|
||||||
"watts": chosen.map(|r| r1(r.watts)).unwrap_or(0.0),
|
"watts": chosen.map(|r| r1(r.watts)).unwrap_or(0.0),
|
||||||
|
"floor": floor,
|
||||||
|
"note": if floor { "floor, not optimum" } else { "" },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -928,7 +932,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn the_full_plan_is_the_power_ladder_then_the_clock_ladder_at_the_chosen_power() {
|
fn the_full_plan_is_the_power_ladder_then_the_clock_ladder_at_the_chosen_power() {
|
||||||
let plan = Plan::full(&l5090(), Point { clock_mhz: 0, power_pct: 80, mem_mhz: 0 }, 1.0);
|
let plan = Plan::full(&l5090(), Point { clock_mhz: 0, power_pct: 80, mem_mhz: 0 }, 1.0);
|
||||||
assert_eq!(plan.len(), 5 + 4, "five power steps (60% and 50% clamp to 400 W; one kept) and four clock steps");
|
assert_eq!(plan.len(), 5 + 6, "five power steps (60% and 50% clamp to 400 W; one kept) and six clock steps (90% down to 45%)");
|
||||||
let first = plan.next(&[]).unwrap();
|
let first = plan.next(&[]).unwrap();
|
||||||
assert_eq!((first.point, first.watts, first.kind), (Point { clock_mhz: 0, power_pct: 100, mem_mhz: 0 }, 575.0, Kind::Power));
|
assert_eq!((first.point, first.watts, first.kind), (Point { clock_mhz: 0, power_pct: 100, mem_mhz: 0 }, 575.0, Kind::Power));
|
||||||
// the power ladder: 575, 518, 460, 403, 400
|
// the power ladder: 575, 518, 460, 403, 400
|
||||||
|
|
@ -952,8 +956,14 @@ mod tests {
|
||||||
rows.push(row_at(s.point, 220.0, 123.5));
|
rows.push(row_at(s.point, 220.0, 123.5));
|
||||||
rows.push(row_at(plan.next(&rows).unwrap().point, 200.0, 118.0));
|
rows.push(row_at(plan.next(&rows).unwrap().point, 200.0, 118.0));
|
||||||
let s = plan.next(&rows).unwrap();
|
let s = plan.next(&rows).unwrap();
|
||||||
assert_eq!(s.point.clock_mhz, 1854, "60% of 3,090 is the floor");
|
assert_eq!(s.point.clock_mhz, 1854, "60% of 3,090");
|
||||||
rows.push(row_at(s.point, 180.0, 100.0));
|
rows.push(row_at(s.point, 180.0, 100.0));
|
||||||
|
let s = plan.next(&rows).unwrap();
|
||||||
|
assert_eq!(s.point.clock_mhz, 1545, "50%");
|
||||||
|
rows.push(row_at(s.point, 170.0, 90.0));
|
||||||
|
let s = plan.next(&rows).unwrap();
|
||||||
|
assert_eq!(s.point.clock_mhz, 1390, "45% of 3,090 is the floor (6 October 2026)");
|
||||||
|
rows.push(row_at(s.point, 160.0, 80.0));
|
||||||
assert_eq!(plan.next(&rows), None);
|
assert_eq!(plan.next(&rows), None);
|
||||||
// the choice: 2,472 MHz keeps 99.6% of the top rate at 220 W = 0.561 MH/W; 2,163 MHz (118 MH/s) is outside the 1% tolerance
|
// the choice: 2,472 MHz keeps 99.6% of the top rate at 220 W = 0.561 MH/W; 2,163 MHz (118 MH/s) is outside the 1% tolerance
|
||||||
let best = choose(&rows, 1.0).unwrap();
|
let best = choose(&rows, 1.0).unwrap();
|
||||||
|
|
@ -961,7 +971,7 @@ mod tests {
|
||||||
// a wider tolerance lets the 2,163 MHz step (0.590 MH/W, 4.8% slower) win
|
// a wider tolerance lets the 2,163 MHz step (0.590 MH/W, 4.8% slower) win
|
||||||
assert_eq!(choose(&rows, 5.0).unwrap().point.clock_mhz, 2163);
|
assert_eq!(choose(&rows, 5.0).unwrap().point.clock_mhz, 2163);
|
||||||
// no power limits, clocks only; no clocks, power only; nothing, empty
|
// no power limits, clocks only; no clocks, power only; nothing, empty
|
||||||
assert_eq!(Plan::full(&Limits { clock_max_mhz: 2000, ..Default::default() }, Point::default(), 1.0).len(), 4);
|
assert_eq!(Plan::full(&Limits { clock_max_mhz: 2000, ..Default::default() }, Point::default(), 1.0).len(), 6);
|
||||||
assert_eq!(Plan::full(&Limits { power_default_w: 300.0, ..Default::default() }, Point::default(), 1.0).len(), 6);
|
assert_eq!(Plan::full(&Limits { power_default_w: 300.0, ..Default::default() }, Point::default(), 1.0).len(), 6);
|
||||||
assert!(Plan::full(&Limits::default(), Point::default(), 1.0).is_empty());
|
assert!(Plan::full(&Limits::default(), Point::default(), 1.0).is_empty());
|
||||||
}
|
}
|
||||||
|
|
@ -969,11 +979,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn limits_never_exceed_the_vendor_or_undercut_the_floor() {
|
fn limits_never_exceed_the_vendor_or_undercut_the_floor() {
|
||||||
let l = l5090();
|
let l = l5090();
|
||||||
assert_eq!(l.clock_floor(), 1854);
|
assert_eq!(l.clock_floor(), 1390);
|
||||||
assert_eq!(l.clamp_clock(1000), 1854);
|
assert_eq!(l.clamp_clock(1000), 1390);
|
||||||
assert_eq!(l.clamp_clock(5000), 3090);
|
assert_eq!(l.clamp_clock(5000), 3090);
|
||||||
assert_eq!(l.clamp_clock(0), 0, "unlocked stays unlocked");
|
assert_eq!(l.clamp_clock(0), 0, "unlocked stays unlocked");
|
||||||
assert_eq!(Limits { clock_max_mhz: 3000, clock_min_mhz: 2100, ..Default::default() }.clamp_clock(1500), 2100, "the vendor's floor wins over the 60% rule");
|
assert_eq!(Limits { clock_max_mhz: 3000, clock_min_mhz: 2100, ..Default::default() }.clamp_clock(1500), 2100, "the vendor's floor wins over the 45% rule");
|
||||||
assert_eq!(l.watts_for(100), 575.0);
|
assert_eq!(l.watts_for(100), 575.0);
|
||||||
assert_eq!(l.watts_for(50), 400.0);
|
assert_eq!(l.watts_for(50), 400.0);
|
||||||
assert_eq!(Limits { power_default_w: 300.0, power_max_w: 250.0, ..Default::default() }.watts_for(100), 250.0);
|
assert_eq!(Limits { power_default_w: 300.0, power_max_w: 250.0, ..Default::default() }.watts_for(100), 250.0);
|
||||||
|
|
@ -1144,7 +1154,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn the_record_and_the_prior_round_trip_through_the_manifest_shape() {
|
fn the_record_and_the_prior_round_trip_through_the_manifest_shape() {
|
||||||
let chosen = row_at(Point { clock_mhz: 2472, power_pct: 100, mem_mhz: 0 }, 220.0, 123.5);
|
let chosen = row_at(Point { clock_mhz: 2472, power_pct: 100, mem_mhz: 0 }, 220.0, 123.5);
|
||||||
let rec = record_json(1_791_230_000.0, "8f3a2c1d", "0.3.10", "windows", "NVIDIA GeForce RTX 5090", "nvidia", "581.57", "l128w16", PlanKind::Full, &[chosen.clone()], Some(&chosen), None);
|
let rec = record_json(1_791_230_000.0, "8f3a2c1d", "0.3.10", "windows", "NVIDIA GeForce RTX 5090", "nvidia", "581.57", "l128w16", PlanKind::Full, &[chosen.clone()], Some(&chosen), None, false);
|
||||||
|
assert_eq!(rec["floor"], false);
|
||||||
assert_eq!(rec["card"], "NVIDIA_GeForce_RTX_5090");
|
assert_eq!(rec["card"], "NVIDIA_GeForce_RTX_5090");
|
||||||
assert_eq!(rec["key"], "NVIDIA_GeForce_RTX_5090|581|l128w16");
|
assert_eq!(rec["key"], "NVIDIA_GeForce_RTX_5090|581|l128w16");
|
||||||
assert_eq!(rec["driver_major"], "581");
|
assert_eq!(rec["driver_major"], "581");
|
||||||
|
|
|
||||||
|
|
@ -1845,7 +1845,7 @@ impl Engine {
|
||||||
let dir = self.sweep_dir();
|
let dir = self.sweep_dir();
|
||||||
let _ = std::fs::create_dir_all(&dir);
|
let _ = std::fs::create_dir_all(&dir);
|
||||||
let script = dir.join("register-power-task.ps1");
|
let script = dir.join("register-power-task.ps1");
|
||||||
let installed: Vec<PathBuf> = [std::env::var_os("LOCALAPPDATA").map(|l| PathBuf::from(l).join("Programs").join("Igneum Miner").join("igneum-app.exe")), std::env::var_os("ProgramFiles").map(|p| PathBuf::from(p).join("Igneum Miner").join("igneum-app.exe"))].into_iter().flatten().collect();
|
let installed: Vec<PathBuf> = crate::powertask::install_candidates();
|
||||||
match std::env::current_exe().map(|exe| std::fs::write(&script, [b"\xEF\xBB\xBF".as_slice(), crate::powertask::register_script(&crate::powertask::task_exe(&exe, &installed)).as_bytes()].concat())) {
|
match std::env::current_exe().map(|exe| std::fs::write(&script, [b"\xEF\xBB\xBF".as_slice(), crate::powertask::register_script(&crate::powertask::task_exe(&exe, &installed)).as_bytes()].concat())) {
|
||||||
Ok(Ok(())) => format!("{} & \"{}\" -NoProfile -ExecutionPolicy Bypass -File \"{}\"", cmds.join(" & "), crate::platform::tool("powershell").display(), script.display()),
|
Ok(Ok(())) => format!("{} & \"{}\" -NoProfile -ExecutionPolicy Bypass -File \"{}\"", cmds.join(" & "), crate::platform::tool("powershell").display(), script.display()),
|
||||||
_ => cmds.join(" & "),
|
_ => cmds.join(" & "),
|
||||||
|
|
@ -2351,7 +2351,7 @@ impl Engine {
|
||||||
let dir = self.sweep_dir();
|
let dir = self.sweep_dir();
|
||||||
let _ = std::fs::create_dir_all(&dir);
|
let _ = std::fs::create_dir_all(&dir);
|
||||||
let script = dir.join("register-power-task.ps1");
|
let script = dir.join("register-power-task.ps1");
|
||||||
let installed: Vec<PathBuf> = [std::env::var_os("LOCALAPPDATA").map(|l| PathBuf::from(l).join("Programs").join("Igneum Miner").join("igneum-app.exe")), std::env::var_os("ProgramFiles").map(|p| PathBuf::from(p).join("Igneum Miner").join("igneum-app.exe"))].into_iter().flatten().collect();
|
let installed: Vec<PathBuf> = crate::powertask::install_candidates();
|
||||||
if let Ok(exe) = std::env::current_exe() {
|
if let Ok(exe) = std::env::current_exe() {
|
||||||
let target = crate::powertask::task_exe(&exe, &installed);
|
let target = crate::powertask::task_exe(&exe, &installed);
|
||||||
if std::fs::write(&script, [b"\xEF\xBB\xBF".as_slice(), crate::powertask::register_script(&target).as_bytes()].concat()).is_ok() {
|
if std::fs::write(&script, [b"\xEF\xBB\xBF".as_slice(), crate::powertask::register_script(&target).as_bytes()].concat()).is_ok() {
|
||||||
|
|
@ -2785,9 +2785,12 @@ impl Engine {
|
||||||
};
|
};
|
||||||
let _ = key;
|
let _ = key;
|
||||||
self.shared.save_settings();
|
self.shared.save_settings();
|
||||||
self.sweep_say(&format!("TUNE chosen card={} clock={} cap={} mem={} limit={:.0} watts={:.1} mhs={:.2} eff={:.4} plan={}{}", run.label, row.point.clock_mhz, row.point.power_pct, row.point.mem_mhz, row.limit, row.watts, row.mhs, row.eff, kind.name(), if pinned { " pinned=1" } else { "" }));
|
// 6 October 2026, run 6: the 5090's chosen clock sat on the ladder's floor (1,854 MHz = the old 60%), so a
|
||||||
|
// chosen point at the floor says so: it is the lowest step measured, not the optimum (Ember 2's climb walks on)
|
||||||
|
let floor = row.point.clock_mhz > 0 && row.point.clock_mhz <= run.plan.limits.clock_floor();
|
||||||
|
self.sweep_say(&format!("TUNE chosen card={} clock={} cap={} mem={} limit={:.0} watts={:.1} mhs={:.2} eff={:.4} plan={}{}{}", run.label, row.point.clock_mhz, row.point.power_pct, row.point.mem_mhz, row.limit, row.watts, row.mhs, row.eff, kind.name(), if pinned { " pinned=1" } else { "" }, if floor { " floor=1 note=floor,_not_optimum" } else { "" }));
|
||||||
let before = run.rows.first().filter(|_| kind == crate::ember::PlanKind::Full).cloned();
|
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());
|
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}"));
|
self.sweep_say(&format!("TUNE {record}"));
|
||||||
let line = crate::ember::tuned_line(row.mhs, row.watts, row.eff);
|
let line = crate::ember::tuned_line(row.mhs, row.watts, row.eff);
|
||||||
match kind {
|
match kind {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ pub enum HelperCmd {
|
||||||
MemReset,
|
MemReset,
|
||||||
Quit,
|
Quit,
|
||||||
Remove,
|
Remove,
|
||||||
|
/// re-point the task at the installed exe (no path argument: the helper finds the install folder itself, so a
|
||||||
|
/// writer of cmd.txt can never choose what runs elevated); 6 October 2026, run 6 registered a scratch copy
|
||||||
|
Reregister,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses one line: `<seq> <verb> [<digits>]` (the 0.3.9 form `<seq> <watts>` reads as a power limit; `quit` and
|
/// Parses one line: `<seq> <verb> [<digits>]` (the 0.3.9 form `<seq> <watts>` reads as a power limit; `quit` and
|
||||||
|
|
@ -71,6 +74,7 @@ pub fn parse_line(line: &str) -> Option<(u64, HelperCmd)> {
|
||||||
[_, "lmc", m] if digits(m) => Some((seq, HelperCmd::MemClock(m.parse().ok()?))),
|
[_, "lmc", m] if digits(m) => Some((seq, HelperCmd::MemClock(m.parse().ok()?))),
|
||||||
[_, "rmc"] => Some((seq, HelperCmd::MemReset)),
|
[_, "rmc"] => Some((seq, HelperCmd::MemReset)),
|
||||||
[_, "dev", d] if digits(d) => Some((seq, HelperCmd::Dev(d.to_string()))),
|
[_, "dev", d] if digits(d) => Some((seq, HelperCmd::Dev(d.to_string()))),
|
||||||
|
[_, "reregister"] => Some((seq, HelperCmd::Reregister)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,16 +95,37 @@ pub fn smi_args(dev: &str, c: &HelperCmd) -> Option<Vec<String>> {
|
||||||
/// kit a job fetched: `<app data>\app\jobs\<id>\igneum-app-ember.exe`), in which case the installed app's own
|
/// kit a job fetched: `<app data>\app\jobs\<id>\igneum-app-ember.exe`), in which case the installed app's own
|
||||||
/// exe beside it is the durable target (6 October 2026: the first registration on PC 1 came from such a kit).
|
/// exe beside it is the durable target (6 October 2026: the first registration on PC 1 came from such a kit).
|
||||||
pub fn task_exe(running: &Path, install_candidates: &[PathBuf]) -> PathBuf {
|
pub fn task_exe(running: &Path, install_candidates: &[PathBuf]) -> PathBuf {
|
||||||
// either separator: the path comes from Windows, the tests run on the Mac too
|
// the running exe stands only when it is itself an install candidate (either separator, any case: the path
|
||||||
let under_jobs = running.to_string_lossy().split(['\\', '/']).any(|c| c.eq_ignore_ascii_case("jobs"));
|
// comes from Windows, the tests run on the Mac too); a kit under jobs\ or a measurement run's scratch copy
|
||||||
if under_jobs {
|
// (run 6, 6 October 2026: igneum-tune-<stamp>\bin\igneum-app.exe) never becomes the task's action when an
|
||||||
|
// installed exe exists
|
||||||
|
let norm = |p: &Path| p.to_string_lossy().replace('/', "\\").to_ascii_lowercase();
|
||||||
|
let me = norm(running);
|
||||||
|
if install_candidates.iter().any(|p| norm(p) == me) {
|
||||||
|
return running.to_path_buf();
|
||||||
|
}
|
||||||
if let Some(p) = install_candidates.iter().find(|p| p.is_file()) {
|
if let Some(p) = install_candidates.iter().find(|p| p.is_file()) {
|
||||||
return p.clone();
|
return p.clone();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
running.to_path_buf()
|
running.to_path_buf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Where the installed exe lives: the per-user install, then the old administrator install.
|
||||||
|
pub fn install_candidates() -> Vec<PathBuf> {
|
||||||
|
[
|
||||||
|
std::env::var_os("LOCALAPPDATA").map(|l| PathBuf::from(l).join("Programs").join("Igneum Miner").join("igneum-app.exe")),
|
||||||
|
std::env::var_os("ProgramFiles").map(|p| PathBuf::from(p).join("Igneum Miner").join("igneum-app.exe")),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The PowerShell that prints the task's action (what `reregister` is proven by).
|
||||||
|
pub fn readback_command() -> String {
|
||||||
|
format!("$t = Get-ScheduledTask -TaskName '{TASK_NAME}' -ErrorAction SilentlyContinue; if ($t) {{ Write-Output ($t.Actions[0].Execute + ' ' + $t.Actions[0].Arguments) }}; exit 0")
|
||||||
|
}
|
||||||
|
|
||||||
/// The PowerShell that registers the task (run inside the ONE elevated step, with the caps). `exe` is this
|
/// The PowerShell that registers the task (run inside the ONE elevated step, with the caps). `exe` is this
|
||||||
/// executable's path in the install folder. Principal: the signed-in user, interactive logon, highest run level; no
|
/// executable's path in the install folder. Principal: the signed-in user, interactive logon, highest run level; no
|
||||||
/// trigger; may start on battery; one hour limit per run; multiple starts are ignored while one runs.
|
/// trigger; may start on battery; one hour limit per run; multiple starts are ignored while one runs.
|
||||||
|
|
@ -195,6 +220,26 @@ pub fn run_helper(dir: &Path) -> i32 {
|
||||||
return if ok { 0 } else { 1 };
|
return if ok { 0 } else { 1 };
|
||||||
}
|
}
|
||||||
_ if seq <= last_seq => continue,
|
_ if seq <= last_seq => continue,
|
||||||
|
HelperCmd::Reregister => {
|
||||||
|
last_seq = seq;
|
||||||
|
idle = Instant::now();
|
||||||
|
let Some(target) = install_candidates().into_iter().find(|p| p.is_file()) else {
|
||||||
|
log(&format!("{seq} reregister: no installed exe found"));
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let script = dir.join("register-power-task.ps1");
|
||||||
|
let ok = std::fs::write(&script, [b"\xEF\xBB\xBF".as_slice(), register_script(&target).as_bytes()].concat()).is_ok() && {
|
||||||
|
let mut p = std::process::Command::new(crate::platform::tool("powershell"));
|
||||||
|
p.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", &script.display().to_string()]);
|
||||||
|
crate::platform::quiet(&mut p);
|
||||||
|
p.status().map(|s| s.success()).unwrap_or(false)
|
||||||
|
};
|
||||||
|
let mut q = std::process::Command::new(crate::platform::tool("powershell"));
|
||||||
|
q.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &readback_command()]);
|
||||||
|
crate::platform::quiet(&mut q);
|
||||||
|
let now = q.output().map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()).unwrap_or_default();
|
||||||
|
log(&format!("{seq} reregister {}: the task now runs {now}", if ok { "ok" } else { "failed" }));
|
||||||
|
}
|
||||||
HelperCmd::Dev(d) => {
|
HelperCmd::Dev(d) => {
|
||||||
last_seq = seq;
|
last_seq = seq;
|
||||||
idle = Instant::now();
|
idle = Instant::now();
|
||||||
|
|
@ -244,6 +289,10 @@ mod tests {
|
||||||
assert_eq!(parse_line("5 403"), Some((5, HelperCmd::PowerLimit(403))), "the 0.3.9 form");
|
assert_eq!(parse_line("5 403"), Some((5, HelperCmd::PowerLimit(403))), "the 0.3.9 form");
|
||||||
assert_eq!(parse_line("quit"), Some((0, HelperCmd::Quit)));
|
assert_eq!(parse_line("quit"), Some((0, HelperCmd::Quit)));
|
||||||
assert_eq!(parse_line("remove"), Some((0, HelperCmd::Remove)));
|
assert_eq!(parse_line("remove"), Some((0, HelperCmd::Remove)));
|
||||||
|
assert_eq!(parse_line("12 reregister"), Some((12, HelperCmd::Reregister)));
|
||||||
|
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"));
|
||||||
// nothing else: no shell, no path, no string argument, no oversized number
|
// 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"] {
|
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:?}");
|
assert_eq!(parse_line(bad), None, "{bad:?}");
|
||||||
|
|
@ -289,7 +338,11 @@ mod tests {
|
||||||
let here = std::env::current_exe().unwrap();
|
let here = std::env::current_exe().unwrap();
|
||||||
assert!(here.is_file());
|
assert!(here.is_file());
|
||||||
assert_eq!(task_exe(kit, &[here.clone()]), here);
|
assert_eq!(task_exe(kit, &[here.clone()]), here);
|
||||||
assert_eq!(task_exe(own, &[here]), own.to_path_buf());
|
// a measurement run's scratch copy (run 6, 6 October 2026) is not under jobs\ and still yields
|
||||||
|
let scratch = Path::new(r"C:\Users\Admin\AppData\Local\igneum-tune-20261006-170105\bin\igneum-app.exe");
|
||||||
|
assert_eq!(task_exe(scratch, &[here.clone()]), here);
|
||||||
|
// the installed exe itself stands (matched by path, any case or separator)
|
||||||
|
assert_eq!(task_exe(own, &[PathBuf::from(r"c:/users/admin/appdata/local/programs/igneum miner/IGNEUM-APP.EXE"), here]), own.to_path_buf());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -2244,3 +2244,13 @@ Run b (`segments-pc2-pv1b`, 07:20Z to 07:51Z) claimed nothing in 88 passes: the
|
||||||
| The rule as shipped (no switch): fresh refused while the previous segment is pending (known-failed), accepted after it is unproven | 22 passed | 166.2 s |
|
| The rule as shipped (no switch): fresh refused while the previous segment is pending (known-failed), accepted after it is unproven | 22 passed | 166.2 s |
|
||||||
| `--fresh-rule 0`: fresh accepted while the previous segment is pending, `freshAdmissible` true, still refused after a proven one, the second offer a duplicate ("segment already paid") | 23 passed | 139.9 s |
|
| `--fresh-rule 0`: fresh accepted while the previous segment is pending, `freshAdmissible` true, still refused after a proven one, the second offer a duplicate ("segment already paid") | 23 passed | 139.9 s |
|
||||||
|
|
||||||
|
|
||||||
|
## 6 October 2026, 16:01Z: Ember run 6 on PC 1 (ember-tune-pc1-6, 0.3.13 + kit-6 = 564bdea, elevated, one click)
|
||||||
|
|
||||||
|
The helper registered inside the run but on the scratch copy (fixed: task_exe, the `reregister` verb; see the plan's
|
||||||
|
run 6 notes). RTX 5090: chosen 1854 MHz at 100% = 127.71 MH/s at 226.8 W, 0.563 MH/W, against 127.9 at 311.0 W
|
||||||
|
(0.411) untuned: 84 W saved for 0.15% of rate. Every cap step 60 to 100% read 311 to 313 W (the cap never binds).
|
||||||
|
The clock ladder: 2781 MHz 298.8 W 0.428; 2472 MHz 262.0 W 0.488; 2163 MHz 239.9 W 0.533; 1854 MHz 226.8 W 0.563
|
||||||
|
(the floor, not the optimum: the next cut's ladder goes to 45%). RTX 4070: caps 100 to 60% all 106.0 W 28.71 MH/s
|
||||||
|
(0.271); 50% 99.1 W 28.70 (0.290); clock 2794 MHz at 50% 99.1 W (0.290); 2484 MHz 81.1 W 28.73 (0.354); further rows
|
||||||
|
and the 9070 XT ladder below once the run closes.
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,13 @@ scripts carry them), `relay/test/ember.test.mjs` (climb records aggregate into a
|
||||||
Owed: the measured comparison on PC 1 (one climb per card against run 5's ladder rows, no prompt through the
|
Owed: the measured comparison on PC 1 (one climb per card against run 5's ladder rows, no prompt through the
|
||||||
Power Helper), the temperature-band trigger, NVAPI/nvidia-settings for cards whose driver refuses `-lmc`.
|
Power Helper), the temperature-band trigger, NVAPI/nvidia-settings for cards whose driver refuses `-lmc`.
|
||||||
|
|
||||||
## 7a. One administrator approval, ever (0.3.13; the project lead, 6 October 2026, 11:50 UTC)
|
## 7a. One administrator approval, ever
|
||||||
|
|
||||||
|
Threat note, `reregister` (6 October 2026): the verb takes no path. The helper, already elevated, picks the installed
|
||||||
|
exe from the two install folders itself (`powertask::install_candidates`), so the most a writer of `cmd.txt` can do
|
||||||
|
is point the task back at the installed app. The exposure that remains is the one every per-user install with a
|
||||||
|
highest-run task has: the install folder is the user's own, so a process running as the user can replace the exe the
|
||||||
|
task runs. The installer's code signature and the OTA's hash check are the answer to that, not the task. (0.3.13; the project lead, 6 October 2026, 11:50 UTC)
|
||||||
|
|
||||||
What 0.3.12 does: Power control on raises one prompt and sets every cap in that step; every later cap (an app start, a
|
What 0.3.12 does: Power control on raises one prompt and sets every cap in that step; every later cap (an app start, a
|
||||||
reboot, a slider move) and every tune's helper is another elevated launch, so another prompt. Not "once, ever".
|
reboot, a slider move) and every tune's helper is another elevated launch, so another prompt. Not "once, ever".
|
||||||
|
|
@ -225,6 +231,35 @@ process, registry key or other binary is reachable through it. Tests: `powertask
|
||||||
non-digit or extra argument, the arguments reach nvidia-smi as a list, the registration is per-user, highest,
|
non-digit or extra argument, the arguments reach nvidia-smi as a list, the registration is per-user, highest,
|
||||||
trigger-less and quote-safe, a stale command file runs nothing).
|
trigger-less and quote-safe, a stale command file runs nothing).
|
||||||
|
|
||||||
|
### Run 6 (6 October 2026, 16:01Z, PC 1 on 0.3.13 with kit-6 = 564bdea, elevated, the project lead's one click)
|
||||||
|
|
||||||
|
The scheduled task registered inside the run ("helper registered=true"), but its action was the run's scratch copy
|
||||||
|
(`igneum-tune-20261006-170105\bin\igneum-app.exe`): `task_exe` preferred the installed exe only for a path under
|
||||||
|
`jobs\`. Fixed the same hour: the running exe stands only when it is itself an install candidate; and the helper
|
||||||
|
gained a `reregister` verb (no path argument, so a writer of cmd.txt can never choose what runs elevated: the helper
|
||||||
|
finds the install folder itself, re-registers, and logs the task's action read back). The scratch root stays until
|
||||||
|
that read-back shows the installed path.
|
||||||
|
|
||||||
|
RTX 5090 (driver 617.14, class l128w0, no prior), full plan, every row ok, 65 C at most:
|
||||||
|
|
||||||
|
| Step | Clock cap | Power | Draw | Rate | MH/W |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| 0 (before) | unlocked | 100% 575 W | 311.0 W | 127.90 | 0.411 |
|
||||||
|
| 1 to 4 | unlocked | 90, 80, 70, 60% | 313 W | 127.9 | 0.408 to 0.409 (the cap never binds) |
|
||||||
|
| 5 | 2781 MHz | 100% | 298.8 W | 127.89 | 0.428 |
|
||||||
|
| 6 | 2472 MHz | 100% | 262.0 W | 127.86 | 0.488 |
|
||||||
|
| 7 | 2163 MHz | 100% | 239.9 W | 127.82 | 0.533 |
|
||||||
|
| 8 (chosen) | 1854 MHz | 100% | 226.8 W | 127.71 | 0.563 |
|
||||||
|
|
||||||
|
The per-user number: 84 W saved for 0.15% of rate, 37% more hashes per watt. The chosen clock is the ladder's floor
|
||||||
|
(60% of 3,090), not the optimum: `CLOCK_STEPS_PCT` now ends 60, 50, 45 and `CLOCK_FLOOR_PCT` is 45, with the 1% rate
|
||||||
|
tolerance as the guard; a chosen point on the floor carries `floor=1 note=floor, not optimum` in the chosen line and
|
||||||
|
the record. Ember 2's climb starts from the chosen point.
|
||||||
|
|
||||||
|
RTX 4070 (same driver and class): caps 100 to 60% all 106 W at 28.71 MH/s (0.271); 50% (100 W) 99.1 W 28.70 (0.290);
|
||||||
|
clock 2794 at 50% 99.1 W (0.290); 2484 MHz 81.1 W 28.73 MH/s (0.354); the rest of the ladder and the 9070 XT rows in
|
||||||
|
the bench log entry for the run.
|
||||||
|
|
||||||
## 8a. Next-cut notes (for the 0.3.12 shipper)
|
## 8a. Next-cut notes (for the 0.3.12 shipper)
|
||||||
|
|
||||||
| Commit | What | Where |
|
| Commit | What | Where |
|
||||||
|
|
|
||||||
69
relay/playbooks/ember-helper-repoint-pc1.ps1
Normal file
69
relay/playbooks/ember-helper-repoint-pc1.ps1
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Ember: re-point the Igneum Power Helper at the installed exe and prove the no-prompt cap (6 October 2026)
|
||||||
|
# Unelevated. Run 6 registered the task on the run's scratch copy of the engine (igneum-tune-<stamp>\bin\igneum-app.exe).
|
||||||
|
# This script: (1) reads the task's action back; (2) puts the kit-7 engine (carries the `reregister` verb) in place of
|
||||||
|
# that scratch copy, which is the user's own file; (3) starts the task (no prompt: it is registered) and writes
|
||||||
|
# `<seq> reregister` to the installed app's sweep\cmd.txt; the elevated helper picks the install folder itself and
|
||||||
|
# re-registers; (4) reads the action back again and prints it; (5) the proof: asks the installed app to apply its caps
|
||||||
|
# through the task and prints the "power cap: through the Igneum Power Helper task (no prompt)" line from its log.
|
||||||
|
# Never quits, pauses or resumes the installed app (the 5 October rule). The scratch root is left in place.
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
function Say([string] $s) { Write-Output ('[' + (Get-Date -Format 'HH:mm:ss') + '] ' + $s) }
|
||||||
|
$appDir = Join-Path $env:LOCALAPPDATA 'igneum\app'
|
||||||
|
$sweep = Join-Path $appDir 'sweep'
|
||||||
|
$cmd = Join-Path $sweep 'cmd.txt'
|
||||||
|
$hlog = Join-Path $sweep 'helper.log'
|
||||||
|
$installed = Join-Path $env:LOCALAPPDATA 'Programs\Igneum Miner\igneum-app.exe'
|
||||||
|
$kit = Join-Path $appDir 'jobs\ember-kit-7\igneum-app-ember.exe'
|
||||||
|
function Action() { $t = Get-ScheduledTask -TaskName 'Igneum Power Helper' -ErrorAction SilentlyContinue; if ($t) { return ($t.Actions[0].Execute + ' ' + $t.Actions[0].Arguments) } else { return '' } }
|
||||||
|
$before = Action
|
||||||
|
Write-Output ('RESULT TUNE task_action_before=' + $(if ($before) { $before } else { 'none' }))
|
||||||
|
if (-not $before) { Write-Output 'RESULT TUNE error=task_not_registered'; exit 1 }
|
||||||
|
$exe = ($before -split ' --power-helper')[0].Trim()
|
||||||
|
if ($exe -like '*Programs\Igneum Miner\igneum-app.exe') { Say 'the task already runs the installed exe' }
|
||||||
|
else {
|
||||||
|
if (-not (Test-Path -LiteralPath $kit)) { Write-Output ('RESULT TUNE error=no_kit_7 at ' + $kit); exit 1 }
|
||||||
|
if (-not (Test-Path -LiteralPath $exe)) { Write-Output ('RESULT TUNE error=task_exe_missing ' + $exe); exit 1 }
|
||||||
|
# a helper still running from an earlier start would hold the exe: ask it to quit through its own file, then wait
|
||||||
|
Add-Content -LiteralPath $cmd -Value 'quit' -Encoding ascii
|
||||||
|
Start-Sleep -Seconds 6
|
||||||
|
Set-Content -LiteralPath $cmd -Value '' -Encoding ascii
|
||||||
|
$sha0 = (Get-FileHash -LiteralPath $kit -Algorithm SHA256).Hash.ToLower()
|
||||||
|
Copy-Item -LiteralPath $kit -Destination $exe -Force
|
||||||
|
$sha1 = (Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash.ToLower()
|
||||||
|
Write-Output ('RESULT TUNE task_exe_replaced=' + ($sha0 -eq $sha1) + ' sha256=' + $sha1 + ' path=' + $exe)
|
||||||
|
if ($sha0 -ne $sha1) { Write-Output 'RESULT TUNE error=copy_mismatch'; exit 1 }
|
||||||
|
$seq = 1
|
||||||
|
if (Test-Path -LiteralPath $cmd) { foreach ($l in (Get-Content -LiteralPath $cmd)) { if ($l -match '^(\d+) ') { $n = [int]$Matches[1]; if ($n -ge $seq) { $seq = $n + 1 } } } }
|
||||||
|
$mark = 0; if (Test-Path -LiteralPath $hlog) { $mark = (Get-Content -LiteralPath $hlog).Count }
|
||||||
|
Start-ScheduledTask -TaskName 'Igneum Power Helper'
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
|
Add-Content -LiteralPath $cmd -Value ($seq.ToString() + ' reregister') -Encoding ascii
|
||||||
|
$got = ''
|
||||||
|
for ($i = 0; $i -lt 30; $i++) {
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
if (Test-Path -LiteralPath $hlog) { $lines = @(Get-Content -LiteralPath $hlog); if ($lines.Count -gt $mark) { $new = $lines[$mark..($lines.Count - 1)] | Where-Object { $_ -match 'reregister' }; if ($new) { $got = [string]$new[-1]; break } } }
|
||||||
|
}
|
||||||
|
Write-Output ('RESULT TUNE helper_log=' + $(if ($got) { $got } else { 'no reregister line within 60 s' }))
|
||||||
|
}
|
||||||
|
$after = Action
|
||||||
|
Write-Output ('RESULT TUNE task_action_after=' + $(if ($after) { $after } else { 'none' }))
|
||||||
|
if ($after -notlike '*Programs\Igneum Miner\igneum-app.exe --power-helper*') { Write-Output 'RESULT TUNE error=task_not_on_installed_exe'; exit 1 }
|
||||||
|
# the proof: the installed app applies its caps through the task; its log says so without a prompt
|
||||||
|
$u = Join-Path $appDir 'app.url'
|
||||||
|
if (Test-Path -LiteralPath $u) {
|
||||||
|
$base = (Get-Content -LiteralPath $u -Raw).Trim()
|
||||||
|
$logs = Get-ChildItem -Path (Join-Path $appDir 'logs') -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||||
|
$mark2 = 0; if ($logs) { $mark2 = (Get-Content -LiteralPath $logs.FullName).Count }
|
||||||
|
try { Invoke-WebRequest -Uri ($base + 'api/power/control') -Method POST -Body '{"on":true}' -ContentType 'application/json' -UseBasicParsing -TimeoutSec 5 | Out-Null } catch { Say ('power/control: ' + $_.Exception.Message) }
|
||||||
|
$line = ''
|
||||||
|
for ($i = 0; $i -lt 20; $i++) {
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
|
$logs = Get-ChildItem -Path (Join-Path $appDir 'logs') -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||||
|
if ($logs) { $all = @(Get-Content -LiteralPath $logs.FullName); if ($all.Count -gt $mark2) { $hit = $all[$mark2..($all.Count - 1)] | Where-Object { $_ -match 'power cap' }; if ($hit) { $line = ($hit | ForEach-Object { [string]$_ }) -join ' | '; if ($line -match 'Power Helper task') { break } } } }
|
||||||
|
}
|
||||||
|
Write-Output ('RESULT TUNE cap_proof=' + $(if ($line) { $line } else { 'no power cap line within 60 s' }))
|
||||||
|
} else { Write-Output 'RESULT TUNE cap_proof=no app.url (installed app not running)' }
|
||||||
|
$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 --format=csv,noheader 2>&1 | Out-String) -replace "`r?`n", ' | ')) }
|
||||||
|
exit 0
|
||||||
|
|
@ -367,6 +367,8 @@ for (const [file, active] of PAGES) {
|
||||||
'<h2 id="priors">Fleet tuning priors</h2>',
|
'<h2 id="priors">Fleet tuning priors</h2>',
|
||||||
'<p>Ember Tune runs on every card the app mines with: the power limit and the core clock are stepped on the live program and the card keeps the point with the best MH per watt within 1% of its top rate. Every finished tune is reported back without anything that identifies the owner, and the fleet\'s median point per card model, driver major and program class comes back down inside the signed update manifest as the starting point for the next card of that model. A model needs ' + pj.min_samples + ' reports before its prior is used.</p>',
|
'<p>Ember Tune runs on every card the app mines with: the power limit and the core clock are stepped on the live program and the card keeps the point with the best MH per watt within 1% of its top rate. Every finished tune is reported back without anything that identifies the owner, and the fleet\'s median point per card model, driver major and program class comes back down inside the signed update manifest as the starting point for the next card of that model. A model needs ' + pj.min_samples + ' reports before its prior is used.</p>',
|
||||||
ptable,
|
ptable,
|
||||||
|
// the team's own measured rows (site/miner-priors.json "team"): the per-user sentence per card, with its job
|
||||||
|
...((pj.team || []).length ? ['<h3 id="team">Measured by the team</h3>', '<ul>' + pj.team.map(t => `<li><b>${esc(t.card)}</b> (driver ${esc(t.driver_major)}, class ${esc(t.class)}, ${esc(t.date)}, job <code>${esc(t.job)}</code>): ${esc(t.point)}. ${esc(t.sentence)}${t.note ? ' ' + esc(t.note) : ''}</li>`).join('') + '</ul>'] : []),
|
||||||
`<p>Rows: ${prows.length}${pj.generated ? ', generated ' + pj.generated : ''}. Source file: <code>site/miner-priors.json</code> in the repository, written from the fleet records by <code>tools/tuning.mjs --priors --site</code>.</p>`,
|
`<p>Rows: ${prows.length}${pj.generated ? ', generated ' + pj.generated : ''}. Source file: <code>site/miner-priors.json</code> in the repository, written from the fleet records by <code>tools/tuning.mjs --priors --site</code>.</p>`,
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
const toc = [{ lvl: 2, t: 'The table', id: 'table' }, { lvl: 2, t: 'How a row gets here', id: 'how' }, { lvl: 2, t: 'Fleet tuning priors', id: 'priors' }];
|
const toc = [{ lvl: 2, t: 'The table', id: 'table' }, { lvl: 2, t: 'How a row gets here', id: 'how' }, { lvl: 2, t: 'Fleet tuning priors', id: 'priors' }];
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,24 @@
|
||||||
"_about": "Rows of the fleet priors table at /miners (site/build.mjs), written by tools/tuning.mjs --priors --site from the TUNE records every Igneum Miner uploads. One row per card model, driver major and program class: the median tuned point, MH per watt, the spread and the sample count. No machine names, no addresses.",
|
"_about": "Rows of the fleet priors table at /miners (site/build.mjs), written by tools/tuning.mjs --priors --site from the TUNE records every Igneum Miner uploads. One row per card model, driver major and program class: the median tuned point, MH per watt, the spread and the sample count. No machine names, no addresses.",
|
||||||
"generated": null,
|
"generated": null,
|
||||||
"min_samples": 5,
|
"min_samples": 5,
|
||||||
"rows": []
|
"rows": [],
|
||||||
|
"_team": "Rows measured on the team's own hardware, from the job log named in each row; they stand until the fleet's prior for the model reaches five samples.",
|
||||||
|
"team": [
|
||||||
|
{
|
||||||
|
"card": "NVIDIA GeForce RTX 5090",
|
||||||
|
"driver_major": "617",
|
||||||
|
"class": "l128w0",
|
||||||
|
"date": "2026-10-06",
|
||||||
|
"job": "ember-tune-pc1-6",
|
||||||
|
"point": "1854 MHz core cap, power limit 100% (575 W)",
|
||||||
|
"mh_s": 127.71,
|
||||||
|
"watts": 226.8,
|
||||||
|
"mh_per_w": 0.563,
|
||||||
|
"untuned_mh_s": 127.9,
|
||||||
|
"untuned_watts": 311.0,
|
||||||
|
"untuned_mh_per_w": 0.411,
|
||||||
|
"sentence": "84 W saved for 0.15% of rate: 127.71 MH/s at 226.8 W (0.563 MH/W) against 127.9 MH/s at 311 W untuned (0.411 MH/W), 37% more hashes per watt.",
|
||||||
|
"note": "The chosen clock is the ladder's floor (60% of the maximum), not the optimum; the next cut's ladder goes to 45% and the climb walks on from here."
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,8 @@ th{font-family:var(--f-mono);font-size:12px;letter-spacing:.12em;text-transform:
|
||||||
<h2 id="priors">Fleet tuning priors</h2>
|
<h2 id="priors">Fleet tuning priors</h2>
|
||||||
<p>Ember Tune runs on every card the app mines with: the power limit and the core clock are stepped on the live program and the card keeps the point with the best MH per watt within 1% of its top rate. Every finished tune is reported back without anything that identifies the owner, and the fleet's median point per card model, driver major and program class comes back down inside the signed update manifest as the starting point for the next card of that model. A model needs 5 reports before its prior is used.</p>
|
<p>Ember Tune runs on every card the app mines with: the power limit and the core clock are stepped on the live program and the card keeps the point with the best MH per watt within 1% of its top rate. Every finished tune is reported back without anything that identifies the owner, and the fleet's median point per card model, driver major and program class comes back down inside the signed update manifest as the starting point for the next card of that model. A model needs 5 reports before its prior is used.</p>
|
||||||
<p>No tune reports yet. The first rows appear once five machines with the same card model have reported.</p>
|
<p>No tune reports yet. The first rows appear once five machines with the same card model have reported.</p>
|
||||||
|
<h3 id="team">Measured by the team</h3>
|
||||||
|
<ul><li><b>NVIDIA GeForce RTX 5090</b> (driver 617, class l128w0, 2026-10-06, job <code>ember-tune-pc1-6</code>): 1854 MHz core cap, power limit 100% (575 W). 84 W saved for 0.15% of rate: 127.71 MH/s at 226.8 W (0.563 MH/W) against 127.9 MH/s at 311 W untuned (0.411 MH/W), 37% more hashes per watt. The chosen clock is the ladder's floor (60% of the maximum), not the optimum; the next cut's ladder goes to 45% and the climb walks on from here.</li></ul>
|
||||||
<p>Rows: 0. Source file: <code>site/miner-priors.json</code> in the repository, written from the fleet records by <code>tools/tuning.mjs --priors --site</code>.</p></article>
|
<p>Rows: 0. Source file: <code>site/miner-priors.json</code> in the repository, written from the fleet records by <code>tools/tuning.mjs --priors --site</code>.</p></article>
|
||||||
</div>
|
</div>
|
||||||
<p class="gen">Generated from the repository at build time. Times are UTC. Machine names are model names.</p>
|
<p class="gen">Generated from the repository at build time. Times are UTC. Machine names are model names.</p>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue