Compare commits
24 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04166b209e | ||
|
|
c3e0f62d77 | ||
|
|
0507dbbfdc | ||
|
|
366e55c887 | ||
|
|
99f1b60f5d | ||
|
|
50e5d59183 | ||
|
|
ad74ce87bd | ||
|
|
bd6efd928d | ||
|
|
b059ddcdc7 | ||
|
|
c6aa376a5d | ||
|
|
f248bfd81f | ||
|
|
b1893ce831 | ||
|
|
31827c3586 | ||
|
|
a63345eee4 | ||
|
|
27a97184a3 | ||
|
|
aa4fb95ff4 | ||
|
|
3eb3309224 | ||
|
|
c635bd6414 | ||
|
|
f8779083b6 | ||
|
|
d7543a85e8 | ||
|
|
2f19bc9415 | ||
|
|
d59490c084 | ||
|
|
22b275792c | ||
|
|
f39d64a7e2 |
39 changed files with 2014 additions and 17 deletions
|
|
@ -111,8 +111,11 @@ fn card(index: usize, name: &str, vendor: &str, worker: &str, detail: &str, devi
|
|||
/// CL_DEVICE_NAME: PC 1's Ryzen iGPU is "gfx1036", 5 October 2026).
|
||||
pub fn looks_integrated(name: &str) -> bool {
|
||||
let n = name.to_ascii_lowercase();
|
||||
if n.contains("arc") && n.contains("intel") {
|
||||
return arc_is_integrated(&n);
|
||||
}
|
||||
let integrated = ["radeon(tm) graphics", "radeon graphics", "vega 8", "vega 7", "vega 6", "vega 3", "vega 11", "iris", "uhd graphics", "hd graphics", "intel(r) graphics", "intel graphics", "apu", "780m", "760m", "680m", "610m", "890m", "880m"];
|
||||
if integrated.iter().any(|k| n.contains(k)) && !n.contains("arc ") {
|
||||
if integrated.iter().any(|k| n.contains(k)) {
|
||||
return true;
|
||||
}
|
||||
// AMD APU graphics by gfx code (approximate list from AMD's ROCm and Mesa target tables): Raven/Picasso gfx902 and
|
||||
|
|
@ -123,6 +126,22 @@ pub fn looks_integrated(name: &str) -> bool {
|
|||
apu.iter().any(|k| code == *k || code.starts_with(&format!("{k}:")) || code.starts_with(&format!("{k} ")))
|
||||
}
|
||||
|
||||
/// An Intel name carrying "Arc" (lower-cased): the cards ("Arc(TM) A770", "Arc(TM) B580", "Arc(TM) Pro A60") are
|
||||
/// discrete; the Arc-branded processor graphics are integrated: the bare "Intel(R) Arc(TM) Graphics" of Meteor Lake
|
||||
/// and Arrow Lake, the "Arc(TM) 130V / 140V" of Lunar Lake and the "Arc(TM) 1xxT" of Panther Lake (approximate names
|
||||
/// from Intel's product pages, 7 October 2026; the B580 is the first Intel card in hand, docs/plans/intel-arc.md).
|
||||
fn arc_is_integrated(lower: &str) -> bool {
|
||||
let after = lower.split("arc").nth(1).unwrap_or("").trim_start_matches("(tm)").trim_start_matches("(r)").trim();
|
||||
let word = after.split(|c: char| !c.is_ascii_alphanumeric()).next().unwrap_or("");
|
||||
// "graphics" or nothing after "Arc": the processor graphics; "130v", "140t": digits first, the processor graphics
|
||||
if word.is_empty() || word == "graphics" || word.starts_with(|c: char| c.is_ascii_digit()) {
|
||||
return true;
|
||||
}
|
||||
// "pro", "a770", "b580": a card. Anything else unknown is read as a card (on by default; a wrong call costs an
|
||||
// iGPU 8 identities at 1 to 2 MH/s, a card called integrated would sit off with no reason the owner can see)
|
||||
false
|
||||
}
|
||||
|
||||
/// One row of Windows' adapter list (Win32_VideoController), the part this app reads.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct Adapter {
|
||||
|
|
@ -186,6 +205,10 @@ pub fn vendor_of(name: &str) -> &'static str {
|
|||
"amd"
|
||||
} else if n.contains("apple") {
|
||||
"apple"
|
||||
} else if n.contains("intel") {
|
||||
// Arc cards and Intel processor graphics alike (the kind tells them apart); the first Intel card is the B580
|
||||
// on PC 1, 7 October 2026 (docs/plans/intel-arc.md)
|
||||
"intel"
|
||||
} else {
|
||||
"other"
|
||||
}
|
||||
|
|
@ -440,6 +463,9 @@ impl ClDevice {
|
|||
"nvidia"
|
||||
} else if self.vendor.contains("Advanced Micro") || self.vendor.contains("AMD") || self.name.contains("Radeon") || self.name.contains("AMD") || self.name.to_ascii_lowercase().starts_with("gfx") {
|
||||
"amd"
|
||||
} else if self.vendor.contains("Intel") || self.name.contains("Intel") {
|
||||
// "Intel(R) Corporation" on the "Intel(R) OpenCL Graphics" platform (the Arc driver's runtime)
|
||||
"intel"
|
||||
} else {
|
||||
"other"
|
||||
}
|
||||
|
|
@ -809,6 +835,8 @@ mod tests {
|
|||
// the Mac: detect() labels Apple silicon "apple" itself; the name rules do not call it integrated
|
||||
assert!(!looks_integrated("Apple M5 Max"));
|
||||
assert_eq!(vendor_of("Apple M5 Max"), "apple");
|
||||
assert_eq!(vendor_of("Intel(R) Arc(TM) B580 Graphics"), "intel");
|
||||
assert_eq!(vendor_of("Intel(R) UHD Graphics 770"), "intel");
|
||||
assert_eq!(vendor_of("gfx1036"), "amd");
|
||||
assert_eq!(vendor_of("AMD Radeon RX 9070 XT"), "amd");
|
||||
assert_eq!(vendor_of("NVIDIA GeForce RTX 5090"), "nvidia");
|
||||
|
|
@ -951,6 +979,39 @@ mod tests {
|
|||
assert!(diff.removed.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// The first Intel card (the B580 on PC 1, 7 October 2026, docs/plans/intel-arc.md): the Arc driver's OpenCL
|
||||
/// platform lists it as "Intel(R) Arc(TM) B580 Graphics" under vendor "Intel(R) Corporation" (the line shape is
|
||||
/// the worker's; the device name and vendor string are Intel's documented forms, approximate until the PC 1 job
|
||||
/// prints them). It becomes a discrete 12 GB card, vendor intel, worker OpenCL, on with 8 identities; the
|
||||
/// Arc-branded processor graphics stay integrated; the NVIDIA cards beside it are untouched.
|
||||
#[test]
|
||||
fn an_intel_arc_card_on_the_intel_platform_is_a_discrete_intel_card() {
|
||||
let list = String::from(PC1_LIST)
|
||||
+ " [4] Intel(R) Arc(TM) B580 Graphics | Intel(R) OpenCL Graphics (OpenCL 3.0 )\n GPU, vendor Intel(R) Corporation, driver 32.0.101.9034, OpenCL C 3.0, 160 compute units, 2850 MHz, pci 05:00.0\n global 12208 MiB, max alloc 4095 MiB, local 64 KiB, max work-group 1024, sub-group extension: cl_intel_subgroups\n";
|
||||
let (devs, _) = parse_opencl_list(&list);
|
||||
let arc = devs.iter().find(|d| d.name.contains("B580")).expect("the Arc line parses");
|
||||
assert_eq!(arc.vendor_word(), "intel");
|
||||
assert_eq!(arc.mem_mb, 12208);
|
||||
assert_eq!(arc.bus, "05:00.0");
|
||||
let d = assemble(pc1_inputs(&list));
|
||||
let c = d.cards.iter().find(|c| c.vendor == "intel").expect("an intel card");
|
||||
assert_eq!((c.name.as_str(), c.kind.as_str(), c.worker.as_str(), c.device.as_str(), c.enabled, c.identities), ("Intel(R) Arc(TM) B580 Graphics", "discrete", "OpenCL", "4", true, 8));
|
||||
assert_eq!(c.key, "intel:Intel(R) Arc(TM) B580 Graphics");
|
||||
assert_eq!(c.vram_mb, 12208);
|
||||
assert!(!c.sweep_supported, "no cap through OpenCL");
|
||||
assert!(c.sweep_note.contains("Intel Arc"), "{}", c.sweep_note);
|
||||
assert!(d.listed(c), "the OpenCL list answered, so a vanished Arc can be called removed");
|
||||
// the Arc names: cards discrete, processor graphics integrated
|
||||
for card in ["Intel(R) Arc(TM) B580 Graphics", "Intel(R) Arc(TM) A770 Graphics", "Intel(R) Arc(TM) A380 Graphics", "Intel(R) Arc(TM) Pro A60 Graphics", "Intel(R) Arc(TM) B570 Graphics"] {
|
||||
assert_eq!(classify_kind(card, None), "discrete", "{card}");
|
||||
}
|
||||
for igpu in ["Intel(R) Arc(TM) Graphics", "Intel(R) Arc(TM) 140V GPU (16GB)", "Intel(R) Arc(TM) 130V GPU", "Intel(R) Arc(TM) 140T GPU"] {
|
||||
assert_eq!(classify_kind(igpu, None), "integrated", "{igpu}");
|
||||
}
|
||||
assert_eq!(classify_kind("Intel(R) UHD Graphics 770", None), "integrated");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keys_number_identical_cards() {
|
||||
let mut cards = vec![card(0, "NVIDIA GeForce RTX 5090", "nvidia", "CUDA", "", "0"), card(1, "NVIDIA GeForce RTX 5090", "nvidia", "CUDA", "", "1"), card(2, "gfx1201", "amd", "OpenCL", "", "2")];
|
||||
|
|
|
|||
|
|
@ -137,6 +137,12 @@ pub fn key_without_index(k: &str) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// The restore list of a `cards_leave_off` job: the same entries (identities and cap kept) with enabled=false, so the
|
||||
/// runner's restore on any exit leaves the card off, persisted by the app's own card path.
|
||||
pub fn leave_off(restore: Vec<crate::engine::CardChoice>) -> Vec<crate::engine::CardChoice> {
|
||||
restore.into_iter().map(|mut c| { c.enabled = false; c }).collect()
|
||||
}
|
||||
|
||||
/// One live card as `cards_off_choices` sees it: key, enabled, identities, power_pct, present.
|
||||
pub type LiveCard = (String, bool, u32, u32, bool);
|
||||
|
||||
|
|
@ -605,6 +611,10 @@ impl Jobs {
|
|||
return None;
|
||||
}
|
||||
a.waiting_for_cards = false;
|
||||
// cards_leave_off (7 October 2026, intel-arc): the hold still ends through the app's own card path on any
|
||||
// exit, but with enabled=false, so the card stays off and persisted (settings.json) after the job: the way
|
||||
// to hold a card out of mining past a job without a script touching api/cards (the 6 October rule).
|
||||
let restore = if a.job.bool_param("cards_leave_off") { leave_off(restore) } else { restore };
|
||||
a.cards = CardHold::hold(restore);
|
||||
if a.waiting_for_miners {
|
||||
return Some(Action::StopMiners(format!("job {}: {}", a.job.id, a.job.label())));
|
||||
|
|
@ -630,7 +640,11 @@ impl Jobs {
|
|||
if held.is_empty() {
|
||||
sink.line(&format!("cards-off: {} asked, no present card matched (nothing switched; the script's card may be loaded)", asked.join(",")));
|
||||
} else {
|
||||
sink.line(&format!("cards-off: {} switched off by the runner before this script; cards restored: {} by the runner on any exit (done, failed, timeout, aborted, app quit), with their own enabled flag, identities and cap", held.keys(), held.keys()));
|
||||
if job.bool_param("cards_leave_off") {
|
||||
sink.line(&format!("cards-off: {} switched off by the runner before this script; cards LEFT OFF: {} by the runner on any exit (done, failed, timeout, aborted, app quit), enabled=false with their own identities and cap, persisted by the app (cards_leave_off)", held.keys(), held.keys()));
|
||||
} else {
|
||||
sink.line(&format!("cards-off: {} switched off by the runner before this script; cards restored: {} by the runner on any exit (done, failed, timeout, aborted, app quit), with their own enabled flag, identities and cap", held.keys(), held.keys()));
|
||||
}
|
||||
}
|
||||
}
|
||||
let ctx = account_context();
|
||||
|
|
@ -1434,6 +1448,23 @@ fn collect_done(uploaded: u32, failed: u32, names: Vec<String>, ran: Option<Ran>
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// cards_leave_off (7 October 2026): the restore entries keep their identities and cap and carry enabled=false,
|
||||
/// so the job's exit leaves the card off through the same path; without the param they keep their own flag.
|
||||
#[test]
|
||||
fn cards_leave_off_restores_the_card_as_off_with_its_settings_kept() {
|
||||
let live: Vec<LiveCard> = vec![("other:Intel(R) Arc(TM) B580 Graphics".into(), true, 8, 0, true), ("nvidia:NVIDIA GeForce RTX 5090".into(), true, 2, 80, true)];
|
||||
let (off, restore) = cards_off_choices(&["other:Intel(R) Arc(TM) B580 Graphics".to_string()], &live);
|
||||
assert_eq!(off.len(), 1);
|
||||
assert!(restore[0].enabled, "the plain restore puts the card back on");
|
||||
let left = leave_off(restore.clone());
|
||||
assert_eq!(left.len(), 1);
|
||||
assert_eq!((left[0].key.as_str(), left[0].enabled, left[0].identities, left[0].power_pct), ("other:Intel(R) Arc(TM) B580 Graphics", false, 8, restore[0].power_pct));
|
||||
// the job's param decides, through the same hold
|
||||
let j = jobs::parse(r#"{"jobs":[{"id":"x","kind":"run","expires_at":"2099-01-01T00:00:00Z","target":{"machine_ids":["ae432dc7"]},"params":{"script":"ls","cards_off":["other:Intel(R) Arc(TM) B580 Graphics"],"cards_leave_off":true}}]}"#).unwrap().jobs.remove(0);
|
||||
assert!(j.bool_param("cards_leave_off"));
|
||||
assert_eq!(j.list_param("cards_off"), vec!["other:Intel(R) Arc(TM) B580 Graphics".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cards_off_matches_keys_with_and_without_the_device_index_and_restores_exactly() {
|
||||
// PC 1, 6 October 2026: settings.json holds amd:1:gfx1201 and amd:3:gfx1201, the live state's key is amd:gfx1201
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@
|
|||
//!
|
||||
//! Kinds and their params:
|
||||
//! run script (the body), shell powershell|bash (default per platform), elevated, stop_miners_first,
|
||||
//! timeout_minutes (default 60, at most 600)
|
||||
//! timeout_minutes (default 60, at most 600), cards_off [keys] (the runner switches them off before
|
||||
//! the script and restores them on any exit), cards_leave_off (with cards_off: restored as OFF, so
|
||||
//! the card stays off and persisted after the job; 7 October 2026, the Arc on PC 1)
|
||||
//! fetch url (https), sha256, size, to (file name), dir jobs|prove|packs|updates (default jobs, which is
|
||||
//! <app data>/app/jobs/<id>/), extract (tar -xf into the dir), fresh (empty extract_dir first), extract_dir
|
||||
//! collect globs ["logs/app-*.log", ...] relative to the app data root (* and ? per path component), command
|
||||
|
|
|
|||
|
|
@ -439,6 +439,9 @@ pub fn unsupported_reason(vendor: &str, power_default_w: f64, device: &str) -> O
|
|||
// Ember Tune (src/ember.rs, 5 October 2026): AMD is tuned through igneum-gpu-telemetry, Apple measures only;
|
||||
// the tune itself says which at its start (the card row's note)
|
||||
"apple" | "amd" => None,
|
||||
// the first Intel card, the B580 on PC 1 (7 October 2026, docs/plans/intel-arc.md section 2.3): OpenCL
|
||||
// exposes neither a cap nor a reading; Intel's path is IGCL (ControlLib.dll), the telemetry helper's next piece
|
||||
"intel" => Some("not available: Intel Arc exposes no power cap or reading through OpenCL (the driver's IGCL counters come next)"),
|
||||
_ => Some("not available: no power reading or cap for this card"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
:root{
|
||||
/* colour, dark */
|
||||
--obsidian:#0C0C0E;--graphite:#16161A;--row:#111114;--line:#2A2A30;--line-2:#3A3A42;--ember:#F2541B;--ember-hi:#FF6A2B;--molten:#FFB35C;--bone:#F4F1EC;--ash:#9A9A9E;--ink-2:#C9C7C2;--ember-ink:#0C0C0E;
|
||||
--ember-12:rgba(242,84,27,.12);--ember-40:rgba(242,84,27,.4);--molten-10:rgba(255,179,92,.1);--molten-40:rgba(255,179,92,.4);--nvidia:#8BE37A;--rail-bg:#111114;--hover:rgba(255,255,255,.04);--top-bg:rgba(12,12,14,.86);--shadow:rgba(0,0,0,.6);--scrim:rgba(12,12,14,.72);--ok:#FFB35C;
|
||||
--ember-12:rgba(242,84,27,.12);--ember-40:rgba(242,84,27,.4);--molten-10:rgba(255,179,92,.1);--molten-40:rgba(255,179,92,.4);--nvidia:#8BE37A;--intel:#7CC4FF;--rail-bg:#111114;--hover:rgba(255,255,255,.04);--top-bg:rgba(12,12,14,.86);--shadow:rgba(0,0,0,.6);--scrim:rgba(12,12,14,.72);--ok:#FFB35C;
|
||||
/* type scale */
|
||||
--t-xs:11px;--t-sm:12px;--t-base:13px;--t-md:14px;--t-lg:15px;--t-xl:17px;--t-num:24px;--t-hero:34px;--t-h2:28px;--t-h1:44px;
|
||||
/* spacing scale */
|
||||
|
|
@ -27,10 +27,10 @@
|
|||
--top:60px;--bottom:0px;--drawer-h:260px;--rail:196px}
|
||||
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]){
|
||||
--obsidian:#F4F1EC;--graphite:#FFFFFF;--row:#FAF8F5;--line:#E2DED8;--line-2:#CFCAC2;--ember:#E04A14;--ember-hi:#F2541B;--molten:#B8731F;--bone:#16161A;--ash:#6B6B70;--ink-2:#3C3C42;--ember-ink:#FFFFFF;
|
||||
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F}}
|
||||
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--intel:#1C6FD6;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F}}
|
||||
:root[data-theme="light"]{
|
||||
--obsidian:#F4F1EC;--graphite:#FFFFFF;--row:#FAF8F5;--line:#E2DED8;--line-2:#CFCAC2;--ember:#E04A14;--ember-hi:#F2541B;--molten:#B8731F;--bone:#16161A;--ash:#6B6B70;--ink-2:#3C3C42;--ember-ink:#FFFFFF;
|
||||
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F}
|
||||
--ember-12:rgba(224,74,20,.1);--ember-40:rgba(224,74,20,.4);--molten-10:rgba(184,115,31,.1);--molten-40:rgba(184,115,31,.4);--nvidia:#3C9B2C;--intel:#1C6FD6;--rail-bg:#EFEBE4;--hover:rgba(0,0,0,.04);--top-bg:rgba(244,241,236,.88);--shadow:rgba(0,0,0,.18);--scrim:rgba(244,241,236,.72);--ok:#B8731F}
|
||||
*{box-sizing:border-box}
|
||||
html,body{height:100%}
|
||||
body{margin:0;background:var(--obsidian);color:var(--bone);font-family:var(--sans);font-size:var(--t-md);line-height:1.5;-webkit-font-smoothing:antialiased;overflow:hidden;user-select:none;-webkit-user-select:none;font-variant-numeric:tabular-nums}
|
||||
|
|
@ -191,6 +191,7 @@ body[data-phase="welcome"] #screen-welcome,body[data-phase="cards"] #screen-card
|
|||
.badge.apple{color:var(--bone)}
|
||||
.badge.nvidia{color:var(--nvidia)}
|
||||
.badge.amd{color:var(--ember)}
|
||||
.badge.intel{color:var(--intel)}
|
||||
.gpu-row .info{flex:1;min-width:180px;display:flex;flex-direction:column;gap:var(--s-1)}
|
||||
.gpu-row .name{font-family:var(--head);font-weight:700;font-size:var(--t-xl);line-height:1.2;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
||||
.kind{font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;border-radius:999px;padding:2px 8px;border:1px solid var(--line-2);color:var(--ash);font-weight:500;white-space:nowrap}
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@ var View = (function () {
|
|||
if (cd.power_w > 0 || cd.temp_gpu > 0) return '';
|
||||
if (cd.vendor === 'apple') return 'No power or temperature reading on Apple silicon';
|
||||
if (cd.kind === 'integrated') return 'No power or temperature reading on an integrated GPU';
|
||||
if (cd.vendor === 'intel') return 'No power or temperature reading on Intel Arc yet: the driver exposes them through IGCL, not OpenCL';
|
||||
return '';
|
||||
}
|
||||
// the row's cells: rate, watts with MH/W, £ a day, temperature; a cell with no number is left out, never n/a
|
||||
|
|
@ -559,6 +560,7 @@ var TuneLine = (function () {
|
|||
// the model: {kind: running|tuned|measured|stopped|idle|off, text, note}
|
||||
function model(cd, now) {
|
||||
cd = cd || {};
|
||||
if (cd.vendor === 'intel') return { kind: 'off', text: 'tuning: measure only on Intel Arc once a power reading exists', note: 'no cap through OpenCL; IGCL next' };
|
||||
if (cd.vendor !== 'nvidia' && cd.vendor !== 'amd' && cd.vendor !== 'apple') return { kind: 'off', text: cd.sweep_note || 'tuning: no power or clock control for this card' };
|
||||
if (cd.sweep_state === 'running') return { kind: 'running', text: cd.sweep_note || 'tuning: running' };
|
||||
var when = cd.sweep_at && now ? ', ' + rel(now - cd.sweep_at) + ' ago' : '';
|
||||
|
|
@ -1146,7 +1148,7 @@ if (typeof document !== 'undefined') (function () {
|
|||
// ---------- render ----------
|
||||
function badgeHtml(cd) {
|
||||
if (cd.vendor === 'apple') return '<svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M16.4 12.6c0-2.5 2-3.6 2.1-3.7-1.2-1.7-3-1.9-3.6-2-1.5-.2-3 .9-3.8.9-.8 0-2-.9-3.3-.8-1.7 0-3.2 1-4.1 2.5-1.8 3-.5 7.6 1.3 10.1.9 1.2 1.9 2.6 3.2 2.5 1.3 0 1.8-.8 3.3-.8 1.6 0 2 .8 3.3.8 1.4 0 2.3-1.2 3.1-2.5 1-1.4 1.4-2.8 1.4-2.9 0 0-2.7-1-2.9-4.1zM13.9 5.3c.7-.8 1.2-2 1-3.2-1 0-2.2.7-2.9 1.5-.6.7-1.2 1.9-1 3 1.1.1 2.2-.5 2.9-1.3z"/></svg>';
|
||||
return cd.vendor === 'nvidia' ? 'NV' : cd.vendor === 'amd' ? 'AMD' : 'GPU';
|
||||
return cd.vendor === 'nvidia' ? 'NV' : cd.vendor === 'amd' ? 'AMD' : cd.vendor === 'intel' ? 'INTEL' : 'GPU';
|
||||
}
|
||||
function vramText(cd) { if (!cd.vram_mb) return ''; var gb = cd.vram_mb / 1024; return (gb >= 10 ? Math.round(gb) : gb.toFixed(gb < 4 ? 1 : 0)) + ' GB' + (cd.kind === 'apple' ? ' unified' : ''); }
|
||||
// ---------- the first-run GPU rows: one per card, with a switch ----------
|
||||
|
|
@ -1255,6 +1257,7 @@ if (typeof document !== 'undefined') (function () {
|
|||
'<p class="help">Lower is cooler and quieter; most cards hash nearly as fast at 70%. ' + (cd.pinned ? 'Set by hand, so the tune leaves it. <button class="btn tiny ghost" data-pin="0">Unpin</button>' : 'Ember Tune may move it.') + '</p></div>';
|
||||
h += cd.power_applied ? '<div class="line ok">cap applied: <b>' + want + ' W</b>' + (cd.sweep_pct === pct && cd.sweep_pct ? ' <span class="pin">chosen by the tune</span>' : '') + '</div>' : '<div class="line hot">cap not applied: Windows needs the administrator prompt <button class="btn tiny" data-power-retry="1">Retry</button></div>';
|
||||
} else if (cd.vendor === 'apple') h += '<p class="help">Apple silicon manages its own power; there is no cap to set.</p>';
|
||||
else if (cd.vendor === 'intel') h += '<p class="help">Intel Arc exposes no power cap through OpenCL; the card runs at the driver’s default limit (Intel Graphics Software can set one). No watts reading yet, so the MH/W cell stays empty until the IGCL reader lands.</p>';
|
||||
var tl = [];
|
||||
if (cd.temp_mem > 0) tl.push('memory <b>' + Math.round(cd.temp_mem) + ' °C</b>');
|
||||
if (cd.gclk_mhz > 0) tl.push('core clock <b>' + Math.round(cd.gclk_mhz) + ' MHz</b>' + (cd.clock_cap_mhz > 0 ? ' (cap ' + cd.clock_cap_mhz + ')' : ''));
|
||||
|
|
|
|||
|
|
@ -50,6 +50,23 @@ test('a GPU row: temperatures turn amber then red; unknown numbers are empty', (
|
|||
assert.equal(apple.meta, '128 GB unified');
|
||||
});
|
||||
|
||||
test('an Intel Arc card is a discrete Intel row: INTEL badge, no power reading sentence, measure-only tune words (intel-arc, 7 October 2026)', () => {
|
||||
const arc = card({ key: 'intel:Intel(R) Arc(TM) B580 Graphics', name: 'Intel(R) Arc(TM) B580 Graphics', vendor: 'intel', kind: 'discrete', worker: 'OpenCL', vram_mb: 12208, enabled: true, state: 'mining', hash_now: 15.2, power_w: 0, temp_gpu: 0, sweep_supported: false, sweep_state: 'unsupported', sweep_note: 'not available: Intel Arc exposes no power cap or reading through OpenCL (the driver\'s IGCL counters come next)' });
|
||||
const r = V.cardRow(arc);
|
||||
assert.equal(r.vendor, 'intel');
|
||||
assert.equal(r.kindWord, 'Discrete');
|
||||
assert.equal(r.integrated, false);
|
||||
assert.equal(r.on, true);
|
||||
assert.ok(r.meta.startsWith('12 GB'), r.meta);
|
||||
assert.equal(r.power, '', 'no watts cell without a reading');
|
||||
assert.equal(V.noReading(arc), 'No power or temperature reading on Intel Arc yet: the driver exposes them through IGCL, not OpenCL');
|
||||
assert.equal(V.tunable(arc), false, 'no cap, no clock control: not tunable');
|
||||
assert.equal(V.proveTier(arc), 'Intel(R) Arc(TM) B580 Graphics: cannot prove yet (the prover is CUDA only).');
|
||||
const tm = mod.exports.TuneLine.model(arc, 1000);
|
||||
assert.equal(tm.kind, 'off');
|
||||
assert.equal(tm.text, 'tuning: measure only on Intel Arc once a power reading exists');
|
||||
});
|
||||
|
||||
test('an integrated GPU is shown as integrated and off, with its reason', () => {
|
||||
const r = V.cardRow(card({ key: 'intel:1:UHD', name: 'Intel UHD Graphics 770', vendor: 'other', kind: 'integrated', enabled: false, state: 'off', hash_now: 0, reason: 'integrated GPU: slow and shares the machine memory', power_w: 0, temp_gpu: 0 }));
|
||||
assert.equal(r.integrated, true);
|
||||
|
|
|
|||
|
|
@ -2633,3 +2633,55 @@ in the same shape and reports a box behind its wanted binary.
|
|||
| Rig | the same, and a rig that leaves is itself a weight removal: at 459 MH/s on tonight's devnet it is about 20 percent of the weight, over the hour's budget by itself |
|
||||
| Pool | a pool node is one voter carrying its members' whole weight; a pool restart is the largest single removal on the network and must be sliced like the fleet's |
|
||||
| The network | finality by miner weight is only as steady as the miners' uptime; until public hash dwarfs the fleet, the fleet's supervisor is a consensus component |
|
||||
|
||||
## 7 October 2026, the first Intel card: Arc B580 on PC 1 (branch intel-arc)
|
||||
|
||||
Machine: PC 1 (`ae432dc7`, Windows 11, Ryzen 7 9800X3D), the Intel Arc B580 (12 GB GDDR6, Battlemage) in the RTX 4070's slot beside the RTX 5090 and the RX 9070 XT (eGPU); Intel Arc driver 32.0.101.9034 (`gfx_win_101.9034.exe`, 932,631,144 bytes, SHA512 equal to Intel's page 785597, Authenticode "CN=Intel Corporation"; run b of `tools/intel-arc/pc1-intel-driver.ps1` put on Level Zero 1.32.0, the Support Assistant and Intel Graphics Software, run c bound the display driver, one restart each); the installed app 0.3.18 then 0.3.19, its own `igneum-worker-opencl.exe` (sha256 `0cbb65f0...`). Jobs `run-ia-arc-bench-20261007-b` and `-c` (`tools/intel-arc/pc1-arc-bench.ps1`, the Arc alone through the runner's `--cards-off`, the other two cards mining; `-a` benched the Ryzen iGPU by a script fault and is void). Read back with `node tools/jobs.mjs <id>`.
|
||||
|
||||
As the worker lists it: `[2] Intel(R) Arc(TM) B580 Graphics | Intel(R) OpenCL Graphics (OpenCL 3.0)`, `vendor Intel(R) Corporation, driver 32.0.101.9034, OpenCL C 1.2, 160 compute units, 2850 MHz`, `global 11930 MiB, max alloc 11930 MiB, local 128 KiB, max work-group 1024, sub-group extension: cl_khr_subgroup_shuffle`.
|
||||
|
||||
**The memprobe** (`--device 2 --memprobe --probe-mib N`, device event time, best of 3, 256 dependent steps per lane, work-group 256; `chase` = one dependent random 4-byte load per step):
|
||||
|
||||
| Buffer | Lanes in flight | chase G loads/s | ns per dependent load |
|
||||
|---|---|---|---|
|
||||
| 4 MiB (inside the 18 MB L2, approximate size) | 4,096 | 27.81 | 147 |
|
||||
| 4 MiB | 262,144 | 55.05 | 4,762 |
|
||||
| 64 MiB | 4,096 | 2.34 | 1,747 |
|
||||
| 64 MiB | 262,144 | 2.47 | 106,003 |
|
||||
| 1024 MiB (GDDR6) | 4,096 | 1.37 | 2,995 |
|
||||
| 1024 MiB | 262,144 | 1.41 | 186,078 |
|
||||
|
||||
So the card does 1.41 G dependent random 4-byte reads a second over the 1 GiB dataset, and at 128 loads a hash the ceiling is 11.0 MH/s (the RX 9070 XT 2.42 to 2.68 G and 18 to 19 MH/s measured; the RTX 5090 16 to 18 G and 127 to 137; the M5 Max 3.45 G and 23 to 28). The 64 MiB row shows no large on-chip cache beyond the L2 (the 9070 XT's Infinity Cache gives 9.2 there).
|
||||
|
||||
**The hash, every pack** (`--bench-pack --batches 5 --batch-log2 24`): the kernel builds on Intel's compiler in 236 to 274 ms (`-cl-std=CL3.0 -D IGNEUM_GROUP=32 -D IGNEUM_EXCHANGE=1`; `sub_group_shuffle_xor` through `cl_khr_subgroup_shuffle`, "sub-group size 32 for a 32-item work-group" by `clGetKernelSubGroupInfoKHR`), cache head, last and FNV equal to the pack's (448274a57f508cbc on the class v4 and v3 packs, 7334fa46e5d972eb on the live pack), dataset head, last and 64 samples ok, hot table none, and the vectors FAIL 96 of 96 lanes on `v4-devnet-epoch0` (device 6f5b1bddfdab2d63, expected cefc114b62c2013b at lane 0), on the class v3 control `mx8-devnet-epoch0` (b8dc4930c388267a against d424577fce4a7a60) and on the app's live pack (ae4a942cf98f8012 against d3aedf14be93082b). `--exchange local` (`-cl-std=CL1.2`, the local-memory exchange with barrier) fails with the identical device value, so the exchange is not the cause: the per-lane program arithmetic differs on IGC (the 64-bit families are the suspects; the dataset derivation, also 64-bit, passes, so it is a family, not the whole ALU). The installed app saw the same from its first start on the card: `worker error: ... self-test FAIL ... vector lanes 96 bad of 96`, worker exit 1, the restart loop, until the card was switched off (persisted).
|
||||
|
||||
| Row | Value |
|
||||
|---|---|
|
||||
| Intel Arc B580, class v4, OpenCL on Intel's runtime | 0 MH/s today: wrong hash on every lane (the fingerprint rows owed) |
|
||||
| Dependent-read ceiling | 1.41 G loads/s at 1 GiB, 11.0 MH/s at 128 loads a hash |
|
||||
| Watts and clocks | not exposed through OpenCL (Intel's IGCL, `ControlLib.dll`, is the source to come) |
|
||||
| Card-picker entry (`site/yourcard.js`) | not added: no measured rate |
|
||||
|
||||
Consequences per tier (the rule of 5 October 2026): a B580 owner (12 GB, Windows) cannot mine today; the app lists the card and the worker fails its self-test, so the row should sit off with the reason until the worker carries the Intel fix (the branch's app makes the vendor `intel` and the words say why). When it does, about 10 to 11 MH/s is the expectation (the 0.9 latency share of the other cards applied to the 11.0 ceiling, approximate), about 2,800 blocks a day at the 312 MH/s the devnet showed at 10:54Z, at 150 to 190 W (approximate, no reading) about 0.06 MH/W, under the 9070 XT per watt (0.09) and about even per pound at a third of its price; mine only (the prover is CUDA only). 8 GB and 16 GB Intel rows owed; Linux and HiveOS take the same worker on `intel-opencl-icd`, owed a line. Next: the OpenCL family probe on the Arc (bit-exact per instruction family against the CPU reference) names the wrong family; the worker's Intel text for it; bench d.
|
||||
|
||||
Found on the way: the Intel installer's bootstrapper wraps the inner installer's exit code as 1000 + code (1007 = an unsupported parameter, 1014 = restart required); with the device in Code 12 (Windows' inbox driver 32.0.101.6733 before any restart) the display driver does not bind and a second run after the restart is needed; the runner's `--stop-miners` hold on a run job is released only after the NEXT job ends; PC 1's three miners faulted on the node's block-template RPC (5-s fetches timing out at 8 identities a card), not on the Arc: with 2 identities a card the 5090 and the 9070 XT mine at 121 to 122.5 and 18.9 MH/s (`run-ia-cards-watch-20261007-d` and `-e`, 10:42Z to 10:50Z).
|
||||
|
||||
**Bench d, the same day, 11:20:41 to 11:22:26Z, with the fix (job `run-ia-arc-bench-20261007-d`, the Arc alone, kit worker sha256 `3c62470f...` = `proto-opencl/host.c` at 26e135a3: on an Intel platform the one helper line `rotr_var(x, n) = rotate(x, (0u - n) & 31u)` is rewritten to the shift form before the build).** The register-trace bisect (`run-ia-arc-trace-20261007-b`, `tools/intel-arc/pc1-arc-trace.ps1`: lane 0's eight registers after the init and after every instruction, 55,809 snapshots, GPU against `igneum-pow trace-regs`) found the first divergence at instruction 6 of iteration 0, `rotr`: r0 = d1affd94, n = f183b605, the CPU a68d7fec = rotr(x, 5), the Arc 35ffb29a = rotl(x, 5): Intel's compiler folds the negated amount into a LEFT rotate; nothing before differed and nothing after agreed, in both exchange modes. With the rewrite (`run-ia-arc-trace-20261007-c`) the trace under the local exchange equals the CPU's in all 55,809 snapshots.
|
||||
|
||||
| Pack | Exchange | Build ms | Self-test | Fingerprint 2^24 at base 0 | MH/s |
|
||||
|---|---|---|---|---|---|
|
||||
| v4-devnet-epoch0 (class v4, the current class) | sub-group (khr), size 32 | 703 | PASS, 96 of 96 lanes | f410c731b6bc2d31 (= Metal, Apple OpenCL, RTX 5090) | 11.011 |
|
||||
| mx8-devnet-epoch0 (the class v3 control) | sub-group | 541 | PASS | 90f794dd556f7a3b (= the control everywhere) | 11.019 |
|
||||
| the app's live pack (epoch 76, class v3) | sub-group | 553 | PASS | 4f385ad3c2793064 | 11.002 |
|
||||
| v4-devnet-epoch0 | local memory, sub-group size 16 under that build | 1,084 | PASS | f410c731b6bc2d31 | 10.882 |
|
||||
|
||||
| Row | Value |
|
||||
|---|---|
|
||||
| Intel Arc B580, class v4, OpenCL on Intel's runtime, driver 32.0.101.9034 | 11.0 MH/s (latency share 1.00 of the 11.02 ceiling; the class v4 shadow costs 0.1 percent against the control, the 9070 XT pays 3) |
|
||||
| Watts and clocks | not exposed through OpenCL (IGCL next) |
|
||||
| Card-picker entry (`site/yourcard.js`) | `['Intel Arc B580', 11.0]`, added |
|
||||
|
||||
Consequences per tier, measured: a B580 owner (12 GB, Windows) mines at 11.0 MH/s once the shipped worker carries 26e135a3 (0.3.20): about 2,450 blocks a day at the 388 MH/s the devnet showed at 11:19Z (one every 35 s, about 77,600 IGN a day at 31.69 IGN a block; approximate, the rate moves), at 150 to 190 W (approximate, no reading) about 0.06 to 0.07 MH/W, under the 9070 XT per watt (0.09) and about even per pound at a third of its price; mine only (the prover is CUDA only; the 12 GB are not the limit). Until 0.3.20 the card sits off on PC 1 (persisted) and the branch's app row says why. 8 GB and 16 GB Intel rows owed; Linux and HiveOS take the same worker and the same rewrite on `intel-opencl-icd`. The AMD and NVIDIA paths see no change (`proto-opencl/test_intel_rotr.c`, in the gate).
|
||||
|
||||
**PC 2, the same card in a Razer Core X V2 over Thunderbolt (7 October 2026, 16:14 to 16:16Z; job `run-ia-pc2-arc-bench-20261007-b`, the 0.3.20 worker sha256 af53f194..., the Arc alone through `--cards-off`, the 5090 and 4070 mining):** self-test PASS 96 of 96 on every pack, fingerprints 867dbc45cfb36b4d (the amended class v4 pack) and 90f794dd556f7a3b (the control), rates 10.982 MH/s class v4, 10.998 control, 10.989 live pack, 10.784 local exchange; the memprobe 1.403 G loads/s at 1 GiB (ceiling 10.96): within 0.3 percent of the same card in PC 1's PCIe slot (11.011, 1.41 G), so the eGPU link does not bind the hash (the dataset and cache live on the card; a job crosses the link with 32 bytes up and the hits down). What the link does cost: the cache and dataset build 192 + 683 ms against 107 + 320 ms in the slot (2.1x, the one-off upload per epoch), and the device's PnP properties read `CurrentLinkWidth 1, CurrentLinkSpeed 1 (2.5 GT/s)` with the same maxima, before and after the restart: either the Thunderbolt tunnel's downstream port reports x1 gen 1 to the OS or the property does not describe the tunnel; the 2.1x on a 1.25 GiB upload says about 1 GB/s effective, a Thunderbolt 3 or 4 PCIe tunnel's figure, not x1 gen 1's 250 MB/s; the reading is recorded as the property gave it, approximate. Driver: run 1 of the PC 2 driver job (exit 1014) and the job's own restart took the card from Code 12 on Windows' inbox 32.0.101.6733 to Code 0 on the same inbox driver with the Intel OpenCL platform live; run c binds 32.0.101.9034 (as on PC 1). The 0.3.20 app on PC 2 lists the card as `intel:Intel(R) Arc(TM) B580 Graphics`, enabled, 8 identities.
|
||||
|
||||
|
|
|
|||
145
docs/plans/intel-arc.md
Normal file
145
docs/plans/intel-arc.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Intel Arc B580 on Igneum: the first Intel card (plan, 7 October 2026)
|
||||
|
||||
Branch `intel-arc` (worktree `../igneum-wt-intel-arc`, scratch prefix `ia-*`). the project lead, 7 October 2026, 13:4x UK: "the intel card has arrived ... it will go into pc1 and replace one of the cards, it is the b580". CLAUDE.md names Intel as a tier "when it exists"; it exists today. This page says which worker runs on the card first, what to expect, what can go wrong, and the fallback with its hours. Every number below is either cited or labelled approximate; the measured rows land in `docs/bench-log.md` when the PC 1 job runs.
|
||||
|
||||
## 1. The card
|
||||
|
||||
| Fact | Value | Source |
|
||||
|---|---|---|
|
||||
| Name, generation | Intel Arc B580, Battlemage (BMG-G21), Xe2 architecture | Intel's product page, approximate (not re-read today) |
|
||||
| Memory | 12 GB GDDR6, 192-bit, 19 Gbps, 456 GB/s | the brief; Intel's specification, approximate |
|
||||
| Compute | 20 Xe2 cores (160 vector engines, SIMD16 each), 2,670 MHz boost (approximate) | approximate |
|
||||
| Board power | 190 W TBP, one 8-pin, two slots, PCIe 4.0 x8 | approximate |
|
||||
| Cache | 18 MB L2 (approximate) | approximate |
|
||||
| Driver for Windows | Intel Arc Graphics driver 32.0.101.9034 (non-WHQL, released 2 October 2026; 9033 is the WHQL one), `gfx_win_101.9034.exe`, 932,631,144 bytes, SHA512 `3fc6f9b4...3cf3f0` | Intel download page 785597, read 7 October 2026; the file is on PC 1 (job `run-ia-intel-driver-20261007`, SHA512 equal, Authenticode "CN=Intel Corporation") |
|
||||
| Compute APIs in the driver | OpenCL 3.0 (platform "Intel(R) OpenCL Graphics", the Intel Graphics Compute Runtime with the IGC compiler), Level Zero, DirectX 12, Vulkan | Intel's runtime documentation, approximate |
|
||||
| As the installed worker lists it (PC 1, 7 October 2026, 10:07Z, on Windows' inbox driver 32.0.101.6733) | `[2] Intel(R) Arc(TM) B580 Graphics | Intel(R) OpenCL Graphics (OpenCL 3.0)`; `GPU, vendor Intel(R) Corporation, OpenCL C 1.2, 160 compute units, 2850 MHz`; `global 11928 MiB, max alloc 11928 MiB, local 128 KiB, max work-group 1024, sub-group extension: cl_khr_subgroup_shuffle` | job `run-ia-intel-readback-20261007-b` (measured) |
|
||||
|
||||
So the runtime lists the Khronos sub-group shuffle extension: the worker's `--exchange auto` tries `sub_group_shuffle_xor` first and keeps it only if the kernel's sub-group size at a 32-item work-group reads 32 (section 2.3); 160 "compute units" are the 160 vector engines (20 Xe cores x 8), and the whole 11.65 GiB is one allocation (no 4 GB cap as on AMD and NVIDIA).
|
||||
|
||||
PC 1 on 7 October 2026 (`ae432dc7`, Windows 11, the installed app 0.3.18): RTX 5090, RX 9070 XT (eGPU), the Ryzen iGPU gfx1036, and the B580 in the RTX 4070's slot (the 4070 went out; Windows' PnP list still carries its ghost row, present=False). The driver story of the day: Windows' inbox driver 6733 bound the card with Code 12 until the restart; run b of the driver job (`tools/intel-arc/README.md`) installed Intel's auxiliary packages but the 9034 display driver did not bind while the device was in Code 12; after the restart the card is OK, Code 0, on 6733 with a working OpenCL 3.0 platform; run c binds 9034.
|
||||
|
||||
## 2. Which worker first: the OpenCL worker, unchanged
|
||||
|
||||
The app drives NVIDIA by the CUDA worker, AMD by the OpenCL worker (`proto-opencl/host.c`, shipped as `igneum-worker-opencl.exe`, `OpenCL.dll` loaded at run time) and Apple by Metal. The Intel driver ships an OpenCL 3.0 platform, so the first run is the shipped OpenCL worker on Intel's runtime with nothing rebuilt: `detect.rs` already reads the worker's `--list` for "AMD, Intel" and `assemble` makes a card of every non-NVIDIA GPU device it prints. The 4 October United States laptop mined on an Intel UHD iGPU through this path at 1.46 MH/s (bench-log, 4 October 2026), so Intel's compiler has already built the kernel text once; the Arc is the first discrete Intel card.
|
||||
|
||||
What the installed 0.3.18 app will do by itself the moment the driver is in: list the Arc as vendor "other", kind discrete (the name carries "Arc"), 12 GB, enabled with 8 identities, and start an OpenCL worker on it with `--device <index> --pack packs\devnet`. Its ready line or its error is the first light; the PC 1 job reads it before the bench (section 5).
|
||||
|
||||
### 2.1 The expected ceiling
|
||||
|
||||
The hash is bound by dependent random 4-byte reads over the 1 GiB dataset, 128 per hash (bench-log, 5 October 2026, "the 9070 XT on the eGPU"; `docs/plans/read-width.md` section 1). The measured dependent-read rates at 1024 MiB:
|
||||
|
||||
| Card | Memory bandwidth | Dependent random loads at 1 GiB | Rate at 128 loads per hash | Watts | MH/W |
|
||||
|---|---|---|---|---|---|
|
||||
| RTX 5090 | 1,792 GB/s, 96 MB L2 | 16 to 18 G/s | 127 to 137 MH/s measured | 227 W tuned (Ember run 6) to 311 W | 0.41 to 0.56 |
|
||||
| RX 9070 XT | 640 GB/s, 64 MB Infinity Cache | 2.42 to 2.68 G/s | 18.0 to 18.2 MH/s measured (87 to 95% of the ceiling) | 199 W | 0.09 |
|
||||
| Apple M5 Max | 546 GB/s unified | 3.45 G/s | 23 to 28 MH/s measured | 38 W | 0.70 |
|
||||
| Arc B580 (expected) | 456 GB/s, 18 MB L2 | 1.7 to 2.5 G/s (approximate: the 9070 XT's rate scaled by bandwidth gives 1.8; a 64-byte line per 4-byte read on both) | 13 to 20 MH/s, the job decides | 150 to 190 W under load (approximate) | 0.07 to 0.13 |
|
||||
|
||||
So the first-light expectation is "a 9070 XT-class rate at a 9070 XT-class efficiency", 13 to 20 MH/s, and the job's `--memprobe` row replaces the guess with the card's own dependent-load ceiling. A B580 sells for about a third of a 9070 XT (approximate, street prices), so the per-pound figure is the one to watch.
|
||||
|
||||
### 2.2 Memory: 12 GB
|
||||
|
||||
Mining needs about 3.4 GB resident (the 1 GiB dataset, the 256 MiB cache, the program and the context: `memory.used min 3,396 MiB` on the 5090 with the miner alone, bench-log 5 October). 12 GB holds that three times over. Proving is CUDA-only today (SP1's GPU prover; the app's words: "cannot prove yet (the prover is CUDA only)"), so an Arc owner mines only, by the prover's vendor and not by the 12 GB; and even on NVIDIA a 12 GB card cannot mine and prove at once on this build (15.6 GB peak, CLAUDE.md consequences rule). The row for a B580 owner therefore reads "mine only" twice over, and the prover question for Intel is a Level Zero or SYCL port of the prover, out of scope here.
|
||||
|
||||
### 2.3 The known Arc OpenCL gotchas, and where the worker already stands
|
||||
|
||||
| Gotcha | What it does to this kernel | Where the worker stands |
|
||||
|---|---|---|
|
||||
| Sub-group size: Xe2 executes SIMD16 natively; IGC picks a sub-group size of 8, 16 or 32 per kernel and `cl_intel_subgroups` lets the kernel require one (`intel_reqd_sub_group_size(32)`) | the worker's lane unit is 32 (one warp = one 32-lane work-group); a 16-wide sub-group means two sub-groups per unit, so the sub-group shuffle exchange is wrong at width 16 | `setupProgram` already handles it: it asks `clGetKernelSubGroupInfo` for the size at a 32-item work-group and falls back to the local-memory exchange unless it reads 32 (`exchange 2` = `intel_sub_group_shuffle_xor`, host.c line 508; the probe kernel `igneum_probe_subgroup` is the second check). The local-memory path is the one AMD runs (wave32, exchange 0) at no measured cost against sub-group shuffles (the 5090's OpenCL path is 4% under CUDA). First run: `--exchange auto`; a `--exchange local` comparison row in the same job |
|
||||
| Local memory: 64 KB SLM per Xe core shared by the work-groups resident on it (approximate); a big local footprint cuts occupancy | the exchange buffer is small (32 lanes x a few words); the dataset and cache live in global memory | no change; the kernel-info line prints `local memory N bytes`, read in the report |
|
||||
| Private memory and spills: IGC spills large per-lane state to scratch, and the `private memory` figure in the kernel query is the signal | the hash keeps its state in registers on AMD (`private memory 0`); a non-zero figure on Intel means spills, and the rate would show it | the job prints the kernel-info line; a spill is a finding, not a blocker |
|
||||
| 64-bit integer multiply: Xe has no native 64-bit integer multiply (emulated from 32-bit, approximate 3 to 4x the cost of a 32-bit one) | the program's `mul`, `mulhi` and `mad` families are 64-bit; on a latency-bound hash this is in the shadow of the memory reads, and the shadow work of class v4 (`sh256x27`) is the one place the ALU budget could bind on a weak-ALU card | the job's class v4 row against the class v3 control shows whether the card holds its rate under the shadow; the 5 percent rule of Counter ASIC 2.0 is the test |
|
||||
| The compiler: IGC compiles OpenCL C 1.2 and 2.0 fine; `-cl-std=CL3.0` with features the device lacks fails the build; generic address space and `cl_khr_fp64` are the usual failure sites; build logs are long | the kernel is C99-style OpenCL C 1.2 on the local-memory path (`-cl-std=CL1.2`), CL2.0 or CL3.0 only for the sub-group variant | if the sub-group variant fails to build, `setupProgram` already falls to local memory with the log printed ("the sub-group variant did not compile") |
|
||||
| Compile time: IGC takes seconds for a kernel of this size (the UHD iGPU built packs in 3.0 to 6.4 s, bench-log M11) against 18 ms on Metal and 150 to 180 ms on NVRTC | the per-epoch compile-ahead window is 600 s (`docs/plans/epoch-length.md`); 6 s is 1 percent of it | fine; the job records `build <ms> clBuildProgram` for the Arc |
|
||||
| Two Intel platforms: the driver may also expose a CPU OpenCL platform (Intel's CPU runtime, when installed) and, with an Intel iGPU present, the same "Intel(R) OpenCL Graphics" platform lists both GPUs | a CPU device would be a false card | `dedupe_platforms` keeps GPUs only (`is_gpu`); PC 1 has no Intel iGPU |
|
||||
| Power: OpenCL exposes no power limit and no power reading; Intel's control path is IGCL (the Intel Graphics Control Library, `ControlLib.dll`, shipped with the driver: `ctlPowerTelemetryGet` gives an energy counter, clocks, temperature; `ctlPowerLimitsSet` sets the sustained limit, approximate names from the SDK) | the app's cap slider is NVIDIA's (nvidia-smi); AMD goes through ADLX in `igneum-gpu-telemetry` | the Cards row says so (section 3); an IGCL reader in the telemetry helper is the next piece, scoped in section 4 |
|
||||
| Driver resets: an Intel display driver reset (TDR) kills the context; the worker exits and the miner restarts it (`WORKER FAULT`) | the app's watchdog path, already exercised on AMD | nothing new |
|
||||
|
||||
## 3. The app side (this branch)
|
||||
|
||||
| Piece | Change |
|
||||
|---|---|
|
||||
| Vendor word | `detect.rs`: `vendor_of` and `ClDevice::vendor_word` return `"intel"` for a vendor string or name carrying "Intel" (the OpenCL vendor string is "Intel(R) Corporation", the device name "Intel(R) Arc(TM) B580 Graphics"); the card's key becomes `intel:Intel(R) Arc(TM) B580 Graphics`. The jobs runner's `--cards-off` matches that key with or without the index |
|
||||
| Kind | `classify_kind` already answers discrete for an Arc A-series or B-series name; the integrated guard now reads the Arc-branded iGPUs ("Intel(R) Arc(TM) Graphics" of Meteor Lake, "Arc(TM) 1xxV" of Lunar Lake, "Arc(TM) 1xxT" of Panther Lake, approximate names) as integrated and the "Arc(TM) A", "Arc(TM) B" and "Arc(TM) Pro" cards as discrete |
|
||||
| Badge | the Cards row badge reads `INTEL` in the Intel blue token (`--intel`), light and dark |
|
||||
| Defaults | unchanged: discrete, 12 GB, on with 8 identities |
|
||||
| Power cap | `sweep.rs::unsupported_reason`: `"intel"` answers "not available: Intel Arc exposes no power cap or reading through OpenCL (the driver's IGCL counters come next)"; the row's details panel says "Intel Arc exposes no power cap through OpenCL; the card runs at the driver's default limit (Intel Graphics Software can set one). No watts reading yet, so the MH/W cell stays empty until the IGCL reader lands." The tune line says "tuning: measure only on Intel Arc once a power reading exists" |
|
||||
| Tests | detect.rs: an Intel platform line parses to vendor `intel`, a B580 card named and keyed, Arc iGPU names integrated, Arc cards discrete; view.test.mjs: the Intel discrete row's words, badge and no-reading sentence; the mock scenario `intel` for the capture |
|
||||
|
||||
## 4. The fallback if OpenCL fails on Arc, scoped in hours
|
||||
|
||||
"Fails" means: the kernel does not build on IGC in either exchange mode, or it builds and the vectors mismatch, or it runs under 5 MH/s with the memprobe saying the card can do more. In that order the fallbacks are:
|
||||
|
||||
| Step | What | Hours (Claude side, per the project lead's rule: hours, not weeks) |
|
||||
|---|---|---|
|
||||
| F1: SPIR-V into the same OpenCL runtime | build the kernel text offline with clang (`-cl-std=CL1.2 -target spir64-unknown-unknown -emit-llvm` then `llvm-spirv`) on the box, feed it with `clCreateProgramWithIL`: this skips IGC's OpenCL C front end and keeps every other line of host.c | 2 to 3 hours plus one PC 1 job; needs clang and llvm-spirv on igneum-build-1 (apt) |
|
||||
| F2: a Level Zero host | a second worker, `proto-l0/host.c`, mirroring host.c's serve loop (the job protocol, pack read, dataset and cache fill, the bound kernel, read-back) on `ze*` calls: `zeModuleCreate` with the SPIR-V from F1 (`ZE_MODULE_FORMAT_IL_SPIRV`), `zeCommandListAppendLaunchKernel`, events for timing; Level Zero's loader `ze_loader.dll` ships with the driver | 6 to 10 hours for the worker plus 2 for the app (a third worker kind beside CUDA and OpenCL in `detect.rs` and the launcher), one PC 1 job per round |
|
||||
| F3: a SYCL (oneAPI) build | the kernel ported to SYCL and built with icpx on the box; the largest rewrite and the most toolchain | 12 to 16 hours; last resort |
|
||||
| Not a fallback | a CUDA-to-Intel translation layer (ZLUDA and the like): foreign runtime, no provenance, never in the shipped app | none |
|
||||
|
||||
The decision rule: F1 first because it is the cheapest test of "is it IGC's front end"; F2 only if F1 also fails or the rate stays under half the memprobe ceiling (then the OpenCL runtime's queueing is the suspect and Level Zero's explicit command lists are the fix).
|
||||
|
||||
The telemetry piece (not a fallback, needed either way): an IGCL reader in `proto-opencl/gpu-telemetry.c` (load `ControlLib.dll` at run time, `ctlInit`, `ctlEnumerateDevices`, `ctlPowerTelemetryGet` per card; watts from the energy counter difference over the sample interval, clocks and temperature from the same call; the `intel <ordinal> ...` line beside the `amd` lines), 2 to 3 hours plus a PC 1 sample. Until it lands the bench row records watts as "not exposed" and names IGCL as the source to come.
|
||||
|
||||
## 5. The PC 1 job (written on this branch, published only on the project lead's go after the card is in)
|
||||
|
||||
`tools/intel-arc/pc1-arc-bench.ps1`, published as a signed `run` job with the RUNNER's `--cards-off <the Arc's key>` (never `--stop-miners`: the 5090 and whatever else is in keep mining; the script never posts to `api/cards`, `api/pause` or `api/quit`), after a small fetch job that places the class v4 pack `v4-devnet-epoch0` and the class v3 control `mx8-devnet-epoch0` (`tools/intel-arc/make-kit.sh`, packs only, no exe: the installed worker is the one under test). Steps and lines:
|
||||
|
||||
| Step | Line | What it settles |
|
||||
|---|---|---|
|
||||
| detect | `RESULT list ...` from the installed worker's `--list`; `RESULT device <n> intel ...`; the Windows adapter row (name, driver version, the class key's `qwMemorySize` = 12,288 MiB) | the card is enumerated by name with 12 GB on the Intel OpenCL platform, driver 32.0.101.9034 |
|
||||
| first light | the installed app's own worker line for the card from its log (`ready ... exchange N ... sub-group`), read only | OpenCL compiled and self-tested on Arc inside the app, or the exact error |
|
||||
| memprobe | `igneum-worker-opencl.exe --device <n> --memprobe` | the dependent-load ceiling at 4, 64 and 1024 MiB: the number that bounds every rate below |
|
||||
| bench, class v4 | `--bench-pack --pack <kit>\packs\v4-devnet-epoch0 --batches 5 --batch-log2 24 --device <n>` (fingerprint expected `f410c731b6bc2d31`, the Mac's and the 5090's) | bit-exact on the fourth vendor, and the rate on the current class |
|
||||
| bench, class v3 control | the same on `mx8-devnet-epoch0` (expected `90f794dd556f7a3b`) | whether the shadow work costs the Arc more than 5 percent (the 2.0 rule) |
|
||||
| bench, the live pack | `--bench-pack --pack <app>\packs\devnet` | the rate on today's program as the app mines it |
|
||||
| exchange | the class v4 pack again with `--exchange local` | the sub-group path against the local-memory path |
|
||||
| watts and clocks | `not exposed` through OpenCL; the PDH GPU Engine counter gives busy percent only; the line names IGCL as the source to come | the row's watts cell honest |
|
||||
| row | `RESULT ROW card="Intel Arc B580" mhs= ceiling_mhs= mhw=not-exposed blocks_per_day= hours_per_block= ...` and the consequences line | the bench-log row, the card-picker entry (`['Intel Arc B580', <mhs>]` in `site/yourcard.js`), the owner's sentence |
|
||||
|
||||
Time to a block: the network estimate was 675 MH/s at 09:34Z on 7 October and 463 MH/s at 10:23Z (`live_state.hashes_per_second_estimate`); the devnet makes 86,400 blocks a day, so a card at X MH/s expects X / network x 86,400 blocks a day: 15 MH/s is about 1,900 blocks a day at 675 MH/s (one every 45 s), 2,800 at 463, each worth the subsidy of 31.69 IGN (spec 02 section 2.5). The job computes the line from the live number at run time.
|
||||
|
||||
## 6. Consequences per tier (the rule of 5 October 2026), before the measurement
|
||||
|
||||
| Tier | What the Arc means | What this branch does |
|
||||
|---|---|---|
|
||||
| Home miner, one 12 GB Intel card (the B580 owner) | mines on the OpenCL worker from the app's first run after the driver; expected 13 to 20 MH/s (section 2.1, approximate until measured), 1,700 to 2,600 blocks a day at 675 MH/s of network (one every 33 to 50 s); no proving (CUDA only); no power cap and no watts reading in the app until IGCL lands, so the £ a day cell stays empty | the job's row replaces the estimate; the IGCL reader is the next 2 to 3 hours |
|
||||
| Home miner, 8 GB Intel (Arc A750, B570 10 GB) | the same path; 3.4 GB resident fits; rate lower by the card's dependent-read rate | the A-series is not in hand; the row is owed |
|
||||
| 16 GB Intel (Arc A770 16 GB, B770 if it ships) | the same; still mine only | owed |
|
||||
| Rig | several Arcs on one OpenCL platform: one worker per device index, as AMD; the PCIe 4.0 x8 link is no cost (the eGPU's USB4 cost 6 percent per job and was removed by the select read-back) | nothing new |
|
||||
| Pool user | sees nothing different | nothing |
|
||||
| Windows | this path | this branch |
|
||||
| Linux and HiveOS | Intel's compute runtime is a distro package (`intel-opencl-icd`), the same worker binary | a line in the install notes once the Windows row exists |
|
||||
| macOS | no Intel GPUs | nothing |
|
||||
|
||||
## 7. What the card measured on 7 October 2026 (bench-log entry "the first Intel card")
|
||||
|
||||
| Reading | Value | Where |
|
||||
|---|---|---|
|
||||
| Dependent random 4-byte loads at 1 GiB | 1.41 G loads/s (4 MiB: 55.1 in the L2; 64 MiB: 2.47) | `run-ia-arc-bench-20261007-c`, `--memprobe` |
|
||||
| Ceiling at 128 loads a hash | 11.0 MH/s | the same |
|
||||
| The hash, every pack, both exchange modes | builds in 236 to 274 ms; cache and dataset bit-exact; vectors 96 of 96 lanes WRONG, the same device value in the sub-group and the local-memory exchange | the same; the app's own worker saw the same from its first start |
|
||||
| Watts, clocks | not exposed through OpenCL | IGCL next (section 4) |
|
||||
| Rate, bench d with the fix | 11.011 MH/s on class v4 (11.019 on the v3 control, 11.002 on the live pack, 10.882 local exchange), vectors PASS, fingerprints equal to the Mac's and the 5090's | `run-ia-arc-bench-20261007-d` |
|
||||
| The fault, found by the register trace | instruction `rotr`: Intel's compiler folds `rotate(x, (0u - n) & 31u)` into a LEFT rotate by n; the worker rewrites the one helper line to the shift form on Intel (commit 26e135a3, `proto-opencl/intel_rotr.h`, the gate test `test_intel_rotr.c`); the trace then matches in all 55,809 snapshots | `run-ia-arc-trace-20261007-b` and `-c` |
|
||||
|
||||
Section 2.3's first gotcha (the sub-group size) is cleared (32 for a 32-item work-group) and the fallback ladder of section 4 was not needed: IGC builds the kernel, and the family probe (`tools/intel-arc/pc1-arc-family.ps1`, runs a and b) read every family exact, `mulhi` and `mad` included, because its `rotr` variant is the shift form. The register trace named the one line: the pack's `rotr_var` through the `rotate()` builtin with a computed negative amount, which Intel's compiler turns into the opposite rotate. The worker-side rewrite (26e135a3) is the whole fix: no pack, emitter or consensus text moves, AMD and NVIDIA see no change. The B580 owner's row: 11.0 MH/s from the cut that ships it (0.3.20); until then the card sits off with the reason.
|
||||
|
||||
### PC 2's driver install (7 October 2026, 16:24 to 17:48Z): the installer took the kernel down, 9034 is not installed
|
||||
|
||||
Job `run-ia-pc2-intel-driver-20261007-c` (elevated, the app's own OpenCL worker mining on the Arc in the Razer Core X V2 at the time): download 102 s, sha512 equal to Intel's page, Authenticode valid (Intel Corporation), `-s` install started 16:26:44Z. The job's local `elevated-output.log` ends on that line. The System log: bugcheck **0x0000003B** (SYSTEM_SERVICE_EXCEPTION, c0000005 at fffff80518bdca26), minidump `C:\WINDOWS\Minidump\100726-22234-01.dmp`, Kernel-Power 41 "rebooted without cleanly shutting down", the OS back up at 16:27:56Z (about 70 s after the installer started). Nobody was signed in after the restart, so the per-user app did not start until the logon at 17:34:46Z: 67 minutes off the network (the 5090 at 122 and the 5060 Ti at 40 MH/s, not only the Arc). Driver store after the restart (`pnputil /enum-drivers`): one Intel display package, `oem41.inf` = 32.0.101.6733 (the inbox driver); 9034 is neither staged nor bound. The Arc is listed with Code 0, the Intel OpenCL platform answers, and the app has its OpenCL worker on the card (read-back `run-ia-pc2-intel-readback-20261007-c`, 17:35:49Z; freeze read `run-ia-pc2-freeze-events-20261007`, `tools/intel-arc/pc2-freeze-events.ps1`). The link reads CurrentLinkWidth 1, CurrentLinkSpeed 1 on the device's own PnP properties (the Thunderbolt-tunnelled view; the rate is unaffected, section above).
|
||||
|
||||
Read-back d (18:14:51Z, the app then on 0.3.21 for a few minutes): the Arc row **mining** on 6733 (pid 25168, 8 identities), and its kind read **discrete**, not external, in the Razer Core X V2 (no USB4 or Thunderbolt router in the device's parent chain on this board; the link still x1 gen1): the external-kind hold alone would have missed this card, so 0.3.22 holds every card of the vendor (branch driver-hold-22 f8ed911e). The minidump: the unelevated read is refused (`Access to the path 'C:\WINDOWS\Minidump' is denied`, run-ia-pc2-minidump-20261007, 18:14:55Z), so the copy rides the elevated retry (`tools/intel-arc/pc2-intel-driver-b.ps1`: the dump as base64 chunks in the job's folder before the installer starts, a collect job after; the Mac parser `triage.py` names the module). Order tonight (the shipper owns PC 2): the project lead's hand reinstall of 0.3.20, the 0.3.22 installer job and smoke, then the retry on the shipper's pass line with the project lead's one click. Stop rule (main): a second crash ends it; 6733 mines at 11 MH/s and the driver-check table then treats 6733 as acceptable for the B580.
|
||||
|
||||
**Cancelled for tonight (main, 7 October 2026, 19:3x BST; the project lead's rule: no PC job may need a click or a UAC prompt, and he is away two hours):** no 9034 retry; 6733 stays on PC 2 (11.0 MH/s), the driver-check table accepts 6733 for the B580 (`min_version` 32.0.101.6733, branch driver-hold-22) until an unattended install path exists through the app's Power Helper task; the minidump copy waits for that path too (the unelevated read was refused at 18:14:55Z), never for a click.
|
||||
|
||||
What was to follow before that rule: the retry on PC 2 runs with the Arc idle (`--cards-off "intel:Intel(R) Arc(TM) B580 Graphics"`, the runner holds the worker through the install) and only on a go; the same rule is now the app's (driver-check branch 46cc41e9: a card the app reads as external has its worker stopped before the installer starts). The faulting module in the minidump is not read yet (no debugger on PC 2; a copy of the 2 MB minidump by a fetch-and-collect job is one line if wanted). Per tier: a home miner with one eGPU who clicks the install with the card mining risks this crash and an hour off the network if nobody is at the keyboard; with the hold the installer runs over an idle card, which is the shape PC 1 took at 15:xxZ (stop-miners, exit 14, 9034 bound after the restart).
|
||||
|
||||
## 8. What is not done here
|
||||
|
||||
The IGCL telemetry reader (section 4), an Intel prover, the Linux package line, the driver-check feature (queued: per-vendor minimum-driver table in the manifest and one-click install, branch `driver-check` off the miner-ui-5 tip), and the site's bench table row, which takes the measured number only.
|
||||
|
||||
### 8 October 2026, 09:36 to 09:51 BST: a kit worker without the fix read the Arc as broken; the fix is now on master
|
||||
|
||||
The hash lane's class v5 fingerprint job on PC 2 (08:35:59Z) read its kit worker (igneum-worker-opencl.exe 27faa253, built from class-v5 1095eaa8) failing the self-test on the Arc before any batch, "vector lanes 96 bad of 96" on the v5 pack and on the class v4 control alike, every cache and dataset FNV matching. The question that decided the served row: the installed 0.3.21 worker's own self-test on the same Arc. Read from the intake, no job on PC 2: app run win-1ccfe586-20261007-201954, 20:23:56Z and 20:24:38Z, "self-test PASS (... 96 of 96 vector lanes)" with the devnet pack, then 54 blocks accepted with cpu re-check at 10.58 MH/s wall (STATUS at 21:06:33Z). So the kit worker regressed on Intel: class-v5 and master (3a4ba893) did not carry `proto-opencl/intel_rotr.h` (26e135a3 rode only the release-0.3.21 line and its children, release-0.3.23 and 0.3.24), so every OpenCL worker built from master or a branch of it evaluated every variable right-rotate as a left rotate on Intel. Landed on the mirror's master as a92bcce7 in 66192d65 (09:51 BST, the gate line and the manifest entry with it). The class: a fix that rides a release line and not master is lost by every branch cut from master; the Arc row on /miners stands (11.0 MH/s, 7 October).
|
||||
|
|
@ -179,6 +179,18 @@ fn main() {
|
|||
let (e, _) = epoch_of(&a, mode);
|
||||
println!("{:016x}", e.hash(a.nonce as u32));
|
||||
}
|
||||
"trace-regs" => {
|
||||
// the register-trace bisect (7 October 2026, the Arc B580): lane 0's registers after the init and after
|
||||
// every instruction for the program's own seed words at --nonce, one line each, "TRACE k label r0..r7"
|
||||
let (e, _) = epoch_of(&a, mode);
|
||||
let snaps = igneum_pow::verify::interpret_warp_trace(&e.program, &e.program.seed, a.nonce as u32, &e.dataset);
|
||||
// the labels in the kernel's spelling ("0 add", "s0 mulhi": tools/intel-arc/make-trace-pack.py), so the two
|
||||
// traces diff line for line
|
||||
for (k, (label, r)) in snaps.iter().enumerate() {
|
||||
println!("TRACE {k} {} {}", label.to_lowercase(), r.iter().map(|w| format!("{w:08x}")).collect::<Vec<_>>().join(" "));
|
||||
}
|
||||
eprintln!("{} snapshots", snaps.len());
|
||||
}
|
||||
"hash-bound" => {
|
||||
let bytes = igneum_pow::bind::unhex(&a.prehash).unwrap_or_else(|| usage());
|
||||
let prehash: [u8; 32] = bytes.as_slice().try_into().unwrap_or_else(|_| usage());
|
||||
|
|
|
|||
|
|
@ -338,6 +338,36 @@ pub fn interpret_warp_scratch(
|
|||
base_nonce: u32,
|
||||
ds: &DatasetSource,
|
||||
trace: bool,
|
||||
) -> (WarpResult, Vec<ScratchEvent>) {
|
||||
interpret_warp_hook(program, seed, base_nonce, ds, trace, &mut None)
|
||||
}
|
||||
|
||||
/// Lane 0's eight registers after the init and after every instruction (main and shadow), in execution order: the
|
||||
/// register-trace bisect of the Arc B580 (7 October 2026, branch intel-arc; `tools/intel-arc/make-trace-pack.py`
|
||||
/// stores the same sequence from the OpenCL kernel, and the PC 1 job diffs the two). Each entry: a label
|
||||
/// ("init", "<n> <op>", "s<n> <op>") and the registers.
|
||||
pub fn interpret_warp_trace(program: &Program, seed: &[u32; 8], base_nonce: u32, ds: &DatasetSource) -> Vec<(String, [u32; 8])> {
|
||||
let mut out: Vec<(String, [u32; 8])> = Vec::new();
|
||||
let mut hook: Option<&mut dyn FnMut(String, [u32; 8])> = Some(&mut |label, regs| out.push((label, regs)));
|
||||
let _ = interpret_warp_hook(program, seed, base_nonce, ds, false, &mut hook);
|
||||
out
|
||||
}
|
||||
|
||||
fn snapshot(r: &[[u32; LANES]; 8]) -> [u32; 8] {
|
||||
let mut s = [0u32; 8];
|
||||
for (i, v) in s.iter_mut().enumerate() {
|
||||
*v = r[i][0];
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn interpret_warp_hook(
|
||||
program: &Program,
|
||||
seed: &[u32; 8],
|
||||
base_nonce: u32,
|
||||
ds: &DatasetSource,
|
||||
trace: bool,
|
||||
hook: &mut Option<&mut dyn FnMut(String, [u32; 8])>,
|
||||
) -> (WarpResult, Vec<ScratchEvent>) {
|
||||
let mask = ds.mask;
|
||||
let log2 = ds.log2_words;
|
||||
|
|
@ -367,9 +397,12 @@ pub fn interpret_warp_scratch(
|
|||
let h = ds.hot.as_ref().expect("a hot-table program needs the epoch's hot table on the dataset source");
|
||||
assert_eq!(h.n_words(), program.hot_words(), "the hot table's size is the class's");
|
||||
}
|
||||
if let Some(h) = hook.as_mut() {
|
||||
h("init".to_string(), snapshot(&r));
|
||||
}
|
||||
for _ in 0..ITERATIONS {
|
||||
let sel = r[0];
|
||||
for ins in &program.instrs {
|
||||
for (n, ins) in program.instrs.iter().enumerate() {
|
||||
step(ins, &mut r, &sel, mask, log2, era.as_ref(), layout, ds, &mut idx, &mut val, &mut items_derived);
|
||||
if ins.op == Op::Scratch {
|
||||
let m = scratch.as_mut().expect("a scratch op needs a scratch class");
|
||||
|
|
@ -379,12 +412,18 @@ pub fn interpret_warp_scratch(
|
|||
r[d][lane] = m.rmw(&program.seed, base_nonce, lane, slot, r[d][lane]);
|
||||
}
|
||||
}
|
||||
if let Some(h) = hook.as_mut() {
|
||||
h(format!("{n} {:?}", ins.op), snapshot(&r));
|
||||
}
|
||||
}
|
||||
// Latency-shadow block (Counter ASIC 3.0 item 8): the block runs `reps` times after instruction 63 with the
|
||||
// iteration's `sel`; it is empty on every class without a shadow, so version 2 and class v3 run nothing here.
|
||||
for _ in 0..program.shadow_reps() {
|
||||
for ins in &program.shadow {
|
||||
for (n, ins) in program.shadow.iter().enumerate() {
|
||||
step(ins, &mut r, &sel, mask, log2, era.as_ref(), layout, ds, &mut idx, &mut val, &mut items_derived);
|
||||
if let Some(h) = hook.as_mut() {
|
||||
h(format!("s{n} {:?}", ins.op), snapshot(&r));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@
|
|||
# as run_id job-<id>-<machine id8> (read back with tools/jobs.mjs).
|
||||
#
|
||||
# packaging/ota/publish-jobs.sh add --kind run --target 1ccfe586 --script path.ps1 [--elevated] [--stop-miners] \
|
||||
# [--cards-off key,key] [--timeout-minutes 60] [--shell powershell|bash] --title "..." [--expires-hours 48] [--deploy]
|
||||
# [--cards-off key,key] [--cards-leave-off] [--timeout-minutes 60] [--shell powershell|bash] --title "..." [--expires-hours 48] [--deploy]
|
||||
# (--cards-leave-off, with --cards-off: the runner restores the cards as OFF on any exit, so they stay off and
|
||||
# persisted by the app after the job; the way to hold a card out of mining without a script on api/cards, 7 October 2026)
|
||||
# (--cards-off: the RUNNER switches these cards off through the app's own card path before the script and puts them
|
||||
# back exactly on any exit, done, failed, timeout, aborted or the app quitting; keys with or without the device
|
||||
# index; a script never posts to /api/cards itself: tools/ci/playbook-quit-check.sh, 6 October 2026)
|
||||
|
|
@ -51,7 +53,7 @@ SIGNER="$ROOT/app/igneum-app/target/release/igneum-ota-sign"
|
|||
|
||||
CMD="${1:-}"; [ $# -gt 0 ] && shift
|
||||
KIND="" TARGET="" PLATFORM="" REQUIRES="" REQUIRES_SET=0 ID="" TITLE="" EXPIRES_H="48" DEPLOY=0 BASE="" DEST="" TRIES=12
|
||||
SCRIPT="" SHELL_KIND="" ELEVATED=0 STOP_MINERS=0 TIMEOUT_MIN="" CARDS_OFF=""
|
||||
SCRIPT="" SHELL_KIND="" ELEVATED=0 STOP_MINERS=0 TIMEOUT_MIN="" CARDS_OFF="" CARDS_LEAVE_OFF=0
|
||||
FILE="" URL="" SHA="" SIZE="" DIR="" TO="" EXTRACT=0 EXTRACT_DIR="" FRESH=0
|
||||
GLOBS=() COMMAND="" WHAT=""
|
||||
ZIP="" FIXTURES="" CAP_MIN="" DISTRO="" WSL_USER="" REMOVE_ID=""
|
||||
|
|
@ -73,6 +75,7 @@ while [ $# -gt 0 ]; do
|
|||
--elevated) ELEVATED=1; shift ;;
|
||||
--stop-miners) STOP_MINERS=1; shift ;;
|
||||
--cards-off) CARDS_OFF="$2"; shift 2 ;;
|
||||
--cards-leave-off) CARDS_LEAVE_OFF=1; shift ;;
|
||||
--timeout-minutes) TIMEOUT_MIN="$2"; shift 2 ;;
|
||||
--file) FILE="$2"; shift 2 ;;
|
||||
--url) URL="$2"; shift 2 ;;
|
||||
|
|
@ -276,7 +279,7 @@ if [ "$CMD" = add ]; then
|
|||
fi
|
||||
out="$(bash "$ROOT/tools/ci/prover-socket-check.sh" "$SCRIPT" 2>&1)" || { echo "run: prover-socket-check.sh refuses $SCRIPT:" >&2; printf '%s\n' "$out" >&2; exit 1; }
|
||||
out="$(bash "$ROOT/tools/ci/kit-path-check.sh" "$SCRIPT" 2>&1)" || { echo "run: kit-path-check.sh refuses $SCRIPT:" >&2; printf '%s\n' "$out" >&2; exit 1; }
|
||||
PARAMS="$(python3 -c 'import json,sys; print(json.dumps({"script": open(sys.argv[1]).read(), "shell": sys.argv[2], "elevated": sys.argv[3]=="1", "stop_miners_first": sys.argv[4]=="1", **({"timeout_minutes": int(sys.argv[5])} if sys.argv[5] else {}), **({"cards_off": [k.strip() for k in sys.argv[6].split(",") if k.strip()]} if sys.argv[6] else {})}))' "$SCRIPT" "$SHELL_KIND" "$ELEVATED" "$STOP_MINERS" "$TIMEOUT_MIN" "$CARDS_OFF")"
|
||||
PARAMS="$(python3 -c 'import json,sys; print(json.dumps({"script": open(sys.argv[1]).read(), "shell": sys.argv[2], "elevated": sys.argv[3]=="1", "stop_miners_first": sys.argv[4]=="1", **({"timeout_minutes": int(sys.argv[5])} if sys.argv[5] else {}), **({"cards_off": [k.strip() for k in sys.argv[6].split(",") if k.strip()]} if sys.argv[6] else {}), **({"cards_leave_off": True} if sys.argv[7]=="1" else {})}))' "$SCRIPT" "$SHELL_KIND" "$ELEVATED" "$STOP_MINERS" "$TIMEOUT_MIN" "$CARDS_OFF" "$CARDS_LEAVE_OFF")"
|
||||
[ -n "$TITLE" ] || TITLE="run $(basename "$SCRIPT")"
|
||||
;;
|
||||
fetch)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ static const char* K_ALU =
|
|||
" out[g] = x ^ y;\n"
|
||||
"}\n";
|
||||
static const char* K_ROTR = "CHAIN(acc = rotr_var(y, x))\n";
|
||||
static const char* K_MULHI = "CHAIN(acc = mul_hi(x, y))\n";
|
||||
static const char* K_MAD = "CHAIN(acc = x * y + acc)\n";
|
||||
static const char* K_SHL = "CHAIN(acc = y << (x & 31u))\n";
|
||||
static const char* K_SHR = "CHAIN(acc = y >> (x & 31u))\n";
|
||||
static const char* K_ANDN = "CHAIN(acc = y & ~x)\n";
|
||||
|
|
@ -157,6 +159,10 @@ typedef struct { const char* family; const char* variant; const char* path; cons
|
|||
static const Variant VARIANTS[] = {
|
||||
{ "alu", "alu", "sequence", NULL, 0 },
|
||||
{ "rotr", "rotr_c", "sequence", NULL, 0 },
|
||||
/* the two live families the probe did not carry until the Arc B580 (7 October 2026: every other family exact on
|
||||
Intel's compiler while the hash fails on every lane): mul_hi on uint and the mad form x * y + acc */
|
||||
{ "mulhi", "mulhi_c", "sequence", NULL, 0 },
|
||||
{ "mad", "mad_c", "sequence", NULL, 0 },
|
||||
{ "shflx", "shflx_bperm", "native", NULL, 1 },
|
||||
{ "shflx", "shflx_swz", "native", NULL, 1 },
|
||||
{ "shflx", "shflx_khr", "native", NULL, 0 },
|
||||
|
|
@ -186,6 +192,8 @@ static const int NVARIANTS = (int)(sizeof(VARIANTS) / sizeof(VARIANTS[0]));
|
|||
static const char* bodyOf(const char* variant) {
|
||||
if (!strcmp(variant, "alu")) return K_ALU;
|
||||
if (!strcmp(variant, "rotr_c")) return K_ROTR;
|
||||
if (!strcmp(variant, "mulhi_c")) return K_MULHI;
|
||||
if (!strcmp(variant, "mad_c")) return K_MAD;
|
||||
if (!strcmp(variant, "shflx_bperm")) return K_SHFLX_BPERM;
|
||||
if (!strcmp(variant, "shflx_swz")) return K_SHFLX_SWZ;
|
||||
if (!strcmp(variant, "shflx_khr")) return K_SHFLX_KHR;
|
||||
|
|
@ -240,6 +248,8 @@ static int group_ref(const char* family, uint32_t g0, uint32_t seed, uint32_t st
|
|||
for (l = 0; l < 32; ++l) {
|
||||
uint32_t xv = xs[l], yv = y[l];
|
||||
if (!strcmp(family, "rotr")) acc[l] = rotr_var(yv, xv);
|
||||
else if (!strcmp(family, "mulhi")) acc[l] = (uint32_t)(((uint64_t)xv * (uint64_t)yv) >> 32);
|
||||
else if (!strcmp(family, "mad")) acc[l] = xv * yv + acc[l];
|
||||
else if (!strcmp(family, "shflx")) acc[l] = acc[l] ^ xs[l ^ 8];
|
||||
else if (!strcmp(family, "shl")) acc[l] = yv << (xv & 31u);
|
||||
else if (!strcmp(family, "shr")) acc[l] = yv >> (xv & 31u);
|
||||
|
|
|
|||
|
|
@ -214,6 +214,8 @@ typedef struct {
|
|||
// then only the build-time placeholder of the prebuilt exe (4 October 2026)
|
||||
int readback; // --readback: 0 select (a GPU-side pass reads back only the hits and 34 sentinel words), 1 full
|
||||
// (every output word comes back, 8 bytes per nonce, the path before 5 October 2026)
|
||||
int trace; // --trace: with --bench-pack, run the pack's igneum_hash_trace (tools/intel-arc/make-trace-pack.py)
|
||||
const char* traceOut; // --trace-out F: the TRACE lines to F (55,809 of them on a class v4 pack) instead of stdout
|
||||
int memprobe; // --memprobe: dependent-load latency and throughput, independent-load throughput and an ALU
|
||||
// chain on the chosen device, no pack needed (5 October 2026, the 9070 XT on the eGPU)
|
||||
int probeMib; // --probe-mib N: --memprobe at that one buffer size only (default 0 = 4, 64 and 1024 MiB)
|
||||
|
|
@ -251,6 +253,9 @@ static void usage(void) {
|
|||
" GPU-side pass; full reads back every output (8 bytes per nonce). IGNEUM_READBACK=full does the same.\n"
|
||||
" --bench-pack with --pack D: read the pack at run time, build and self-test it, time its bound kernel (one exe, any pack)\n"
|
||||
" --warps N persistent warps for a variant-5 pack (a 1 MiB scratch each; default 2048; the batch rounds to 32 x N)\n"
|
||||
" --trace with --bench-pack on a trace pack (tools/intel-arc/make-trace-pack.py): lane 0's registers after\n"
|
||||
" every instruction through igneum_hash_trace, one TRACE line each (--trace-out F writes them to F);\n"
|
||||
" the self-test's failure does not stop it (the Arc B580 bisect, 7 October 2026)\n"
|
||||
" --memprobe no pack: dependent random loads (latency and throughput against lanes in flight), independent random\n"
|
||||
" loads and an ALU chain on the chosen device, at 4, 64 and 1024 MiB (--probe-mib N for one size)\n", packMib(), IGNEUM_KERNEL_PATH);
|
||||
}
|
||||
|
|
@ -262,7 +267,7 @@ static Options parseArgs(int argc, char** argv) {
|
|||
Options o;
|
||||
int i;
|
||||
o.datasetMib = 1024; o.batchLog2 = 24; o.batches = 5; o.groupWarps = 1; o.sweep = 0; o.device = -1;
|
||||
o.exchange = 0; o.list = 0; o.timeWall = -1; o.kernelPath = IGNEUM_KERNEL_PATH; o.extraOpts = ""; o.serve = 0; o.noPrepare = 0; o.kernelGiven = 0; o.vendor = NULL; o.packDir = NULL;
|
||||
o.exchange = 0; o.list = 0; o.timeWall = -1; o.kernelPath = IGNEUM_KERNEL_PATH; o.extraOpts = ""; o.serve = 0; o.noPrepare = 0; o.kernelGiven = 0; o.vendor = NULL; o.packDir = NULL; o.trace = 0; o.traceOut = NULL;
|
||||
o.readback = (getenv("IGNEUM_READBACK") && strcmp(getenv("IGNEUM_READBACK"), "full") == 0) ? 1 : 0; o.memprobe = 0; o.probeMib = 0; o.benchPack = 0; o.warps = 0;
|
||||
for (i = 1; i < argc; ++i) {
|
||||
const char* a = argv[i];
|
||||
|
|
@ -289,6 +294,8 @@ static Options parseArgs(int argc, char** argv) {
|
|||
else { printf("--readback must be select or full\n"); exit(2); }
|
||||
}
|
||||
else if (strcmp(a, "--memprobe") == 0) o.memprobe = 1;
|
||||
else if (strcmp(a, "--trace") == 0) o.trace = 1;
|
||||
else if (strcmp(a, "--trace-out") == 0) { if (i + 1 >= argc) { usage(); exit(2); } o.traceOut = argv[++i]; }
|
||||
else if (strcmp(a, "--bench-pack") == 0) o.benchPack = 1;
|
||||
else if (strcmp(a, "--warps") == 0) { if (i + 1 >= argc) { usage(); exit(2); } o.warps = atoi(argv[++i]); }
|
||||
else if (strcmp(a, "--probe-mib") == 0) { if (i + 1 >= argc) { usage(); exit(2); } o.probeMib = atoi(argv[++i]); }
|
||||
|
|
@ -508,8 +515,17 @@ typedef struct {
|
|||
static const char* exchangeName(int m) { return m == 1 ? "sub_group_shuffle_xor (cl_khr_subgroup_shuffle)" : m == 2 ? "intel_sub_group_shuffle_xor (cl_intel_subgroups)" : "local-memory exchange with barrier"; }
|
||||
|
||||
// Returns 0 on success, 1 on build failure (log printed).
|
||||
#include "intel_rotr.h" /* the Intel rotate fold: rotr_var rewritten on an Intel platform (7 October 2026) */
|
||||
static char* intelRotrPatch(const DeviceInfo* di, const char* src, size_t* srcLen, int* patched) {
|
||||
if (!di) { *patched = 0; return (char*)src; }
|
||||
return igneum_intel_rotr_patch(di->vendor, di->platformName, src, srcLen, patched, 0);
|
||||
}
|
||||
|
||||
static int buildProgram(Device* dv, const DeviceInfo* di, const char* src, size_t srcLen, int exchangeMode, int groupSize, const char* extra) {
|
||||
cl_int err = 0;
|
||||
int patched = 0;
|
||||
char* psrc = intelRotrPatch(di, src, &srcLen, &patched);
|
||||
src = psrc;
|
||||
double tb = wallMs(); /* the pack's compile cost (Counter ASIC 3.0 item 2, 6 October 2026): printed as one line below */
|
||||
const char* std;
|
||||
// The sub-group built-ins need OpenCL C 2.0 or 3.0. OpenCL 3.0 devices may report "OpenCL C 1.2" as the default
|
||||
|
|
@ -522,6 +538,7 @@ static int buildProgram(Device* dv, const DeviceInfo* di, const char* src, size_
|
|||
else std = "-cl-std=CL1.2";
|
||||
snprintf(dv->buildOptions, sizeof(dv->buildOptions), "%s -D IGNEUM_GROUP=%d -D IGNEUM_EXCHANGE=%d %s", std, groupSize, exchangeMode, extra);
|
||||
dv->prog = clCreateProgramWithSource(dv->ctx, 1, &src, &srcLen, &err);
|
||||
if (patched) free(psrc);
|
||||
CL_CHECK_ERR(err, "clCreateProgramWithSource");
|
||||
err = clBuildProgram(dv->prog, 1, &di->device, dv->buildOptions, NULL, NULL);
|
||||
if (err != CL_SUCCESS) {
|
||||
|
|
@ -1315,7 +1332,12 @@ static void prepareRun(PrepareTask* t) {
|
|||
src = readFile(path, &srcLen);
|
||||
if (!src) { snprintf(t->error, sizeof(t->error), "cannot read %s", path); releasePair(p); t->doneAt = wallMs(); t->done = 1; return; }
|
||||
tb = wallMs();
|
||||
p->prog = clCreateProgramWithSource(t->dv->ctx, 1, (const char**)&src, &srcLen, &err);
|
||||
{
|
||||
int patched = 0;
|
||||
char* psrc = intelRotrPatch(t->di, src, &srcLen, &patched);
|
||||
p->prog = clCreateProgramWithSource(t->dv->ctx, 1, (const char**)&psrc, &srcLen, &err);
|
||||
if (patched) free(psrc);
|
||||
}
|
||||
free(src);
|
||||
if (err != CL_SUCCESS) { prepareFail(t, "clCreateProgramWithSource", err); releasePair(p); t->doneAt = wallMs(); t->done = 1; return; }
|
||||
err = clBuildProgram(p->prog, 1, &t->di->device, t->dv->buildOptions, NULL, NULL);
|
||||
|
|
@ -1354,6 +1376,77 @@ static int startPrepareThread(PrepareTask* t) { pthread_t th; if (pthread_create
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/* --trace (the Arc B580 register-trace bisect, 7 October 2026, docs/plans/intel-arc.md section 7): the trace pack's
|
||||
* igneum_hash_trace (igneum_hash_bound with a trace argument; tools/intel-arc/make-trace-pack.py) is run for one
|
||||
* 32-lane unit at base nonce 0 with the pack's seed words as init, exactly as the self-test's vector warp 0, and lane
|
||||
* 0's eight registers after the init and after every instruction come back: one "TRACE k label r0..r7" line each,
|
||||
* the labels from the pack's trace_steps.txt (init, then per iteration the main and the shadow instructions).
|
||||
* igneum-pow's `trace-regs` prints the same sequence from the CPU interpreter; the first differing line is the bug. */
|
||||
static int tracePack(Device* dv, ServePair* cur, uint32_t words, const Options* o) {
|
||||
#if IGNEUM_DATASET_MODE != 1
|
||||
(void)dv; (void)cur; (void)words; (void)o;
|
||||
return 2;
|
||||
#else
|
||||
char path[1024], line[256];
|
||||
FILE* f; FILE* outF = stdout;
|
||||
cl_int e = 0;
|
||||
cl_kernel k;
|
||||
cl_mem out = NULL, init = NULL, tr = NULL;
|
||||
size_t g = 32, local = 32;
|
||||
cl_uint base = 0, mask = words - 1u;
|
||||
uint32_t total = 0, nMain = 0, nShadow = 0, reps = 0, i, it, n, idx = 0;
|
||||
char (*mainL)[32] = NULL; char (*shadowL)[32] = NULL;
|
||||
uint32_t* buf;
|
||||
snprintf(path, sizeof(path), "%s/trace_steps.txt", o->packDir);
|
||||
f = fopen(path, "r");
|
||||
if (!f) { printf("FAIL: trace: no %s (not a trace pack)\n", path); return 2; }
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
if (sscanf(line, "total %u", &total) == 1) continue;
|
||||
if (sscanf(line, "main %u", &nMain) == 1) { mainL = calloc(nMain ? nMain : 1, 32); continue; }
|
||||
if (sscanf(line, "shadow %u", &nShadow) == 1) { shadowL = calloc(nShadow ? nShadow : 1, 32); continue; }
|
||||
if (sscanf(line, "reps %u", &reps) == 1) continue;
|
||||
line[strcspn(line, "\r\n")] = 0;
|
||||
if (line[0] == 'm' && line[1] == ' ' && mainL && idx < nMain) { snprintf(mainL[idx++], 32, "%s", line + 2); }
|
||||
else if (line[0] == 's' && shadowL) { static uint32_t sidx = 0; if (sidx < nShadow) snprintf(shadowL[sidx++], 32, "%s", line); }
|
||||
}
|
||||
fclose(f);
|
||||
if (!total || !mainL) { printf("FAIL: trace: trace_steps.txt has no totals\n"); return 2; }
|
||||
k = clCreateKernel(cur->prog, "igneum_hash_trace", &e);
|
||||
if (e != CL_SUCCESS) { printf("FAIL: trace: clCreateKernel igneum_hash_trace (%s): not a trace pack?\n", clErrName(e)); return 2; }
|
||||
out = clCreateBuffer(dv->ctx, CL_MEM_READ_WRITE, 32 * sizeof(uint64_t), NULL, &e);
|
||||
if (e == CL_SUCCESS) init = clCreateBuffer(dv->ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, 32, gPack.seedw, &e);
|
||||
if (e == CL_SUCCESS) tr = clCreateBuffer(dv->ctx, CL_MEM_READ_WRITE, (size_t)total * 32u, NULL, &e);
|
||||
if (e == CL_SUCCESS) e = clSetKernelArg(k, 0, sizeof(cl_mem), &cur->ds);
|
||||
if (e == CL_SUCCESS) e = clSetKernelArg(k, 1, sizeof(cl_mem), &out);
|
||||
if (e == CL_SUCCESS) e = clSetKernelArg(k, 2, sizeof(cl_uint), &base);
|
||||
if (e == CL_SUCCESS) e = clSetKernelArg(k, 3, sizeof(cl_uint), &mask);
|
||||
if (e == CL_SUCCESS) e = clSetKernelArg(k, 4, sizeof(cl_mem), &init);
|
||||
if (e == CL_SUCCESS) e = clSetKernelArg(k, 5, sizeof(cl_mem), &tr);
|
||||
if (e == CL_SUCCESS) e = clEnqueueNDRangeKernel(dv->q, k, 1, NULL, &g, &local, 0, NULL, NULL);
|
||||
if (e == CL_SUCCESS) e = clFinish(dv->q);
|
||||
if (e != CL_SUCCESS) { printf("FAIL: trace: launch (%s)\n", clErrName(e)); return 2; }
|
||||
buf = (uint32_t*)malloc((size_t)total * 32u);
|
||||
e = clEnqueueReadBuffer(dv->q, tr, CL_TRUE, 0, (size_t)total * 32u, buf, 0, NULL, NULL);
|
||||
if (e != CL_SUCCESS) { printf("FAIL: trace: read-back (%s)\n", clErrName(e)); return 2; }
|
||||
if (o->traceOut) { outF = fopen(o->traceOut, "w"); if (!outF) { printf("FAIL: trace: cannot write %s\n", o->traceOut); return 2; } }
|
||||
idx = 0;
|
||||
fprintf(outF, "TRACE %u init %08x %08x %08x %08x %08x %08x %08x %08x\n", idx, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); idx++;
|
||||
for (it = 0; it < 8 && idx < total; ++it) {
|
||||
for (n = 0; n < nMain && idx < total; ++n, ++idx) { const uint32_t* r = buf + (size_t)idx * 8u; fprintf(outF, "TRACE %u %s %08x %08x %08x %08x %08x %08x %08x %08x\n", idx, mainL[n], r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]); }
|
||||
for (i = 0; i < reps && idx < total; ++i) for (n = 0; n < nShadow && idx < total; ++n, ++idx) { const uint32_t* r = buf + (size_t)idx * 8u; fprintf(outF, "TRACE %u %s %08x %08x %08x %08x %08x %08x %08x %08x\n", idx, shadowL[n], r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]); }
|
||||
}
|
||||
if (outF != stdout) fclose(outF);
|
||||
{
|
||||
uint64_t h0 = 0;
|
||||
clEnqueueReadBuffer(dv->q, out, CL_TRUE, 0, 8, &h0, 0, NULL, NULL);
|
||||
printf("RESULT trace snapshots=%u lane0_hash=%016llx out=%s\n", idx, (unsigned long long)h0, o->traceOut ? o->traceOut : "stdout");
|
||||
}
|
||||
free(buf); free(mainL); free(shadowL);
|
||||
clReleaseMemObject(out); clReleaseMemObject(init); clReleaseMemObject(tr); clReleaseKernel(k);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --bench-pack (read-width experiment, 5 October 2026): the pack in --pack is built and self-tested exactly as the
|
||||
* first pair of --serve (pairBuffers: cache, dataset, cache FNV, dataset words, the vector warps through
|
||||
* igneum_hash_bound with the pack's seed words), then the bound kernel is timed over --batches dispatches of
|
||||
|
|
@ -1394,7 +1487,12 @@ static int runBenchPack(Device* dv, const DeviceInfo* di, const Options* o) {
|
|||
dv->kHashBound = dv->kCacheFill = dv->kBuild = NULL; dv->prog = NULL;
|
||||
memcpy(cur->sw, gPack.seedw, 32); memcpy(cur->kw, gPack.keyw, 32);
|
||||
if (!pairHotKernel(cur, o->packDir, perr, sizeof(perr))) { printf("FAIL: pack %s: %s\n", o->packDir, perr); return 1; }
|
||||
if (!pairBuffers(dv, di, dv->q, cur, words, gServeCacheWords, gServeSegments, o->packDir, perr, sizeof(perr))) { printf("FAIL: pack %s: %s\n", o->packDir, perr); return 1; }
|
||||
if (!pairBuffers(dv, di, dv->q, cur, words, gServeCacheWords, gServeSegments, o->packDir, perr, sizeof(perr))) {
|
||||
printf("FAIL: pack %s: %s\n", o->packDir, perr);
|
||||
if (!o->trace || !cur->ds) return 1;
|
||||
printf("trace: the self-test failed; tracing lane 0 anyway (the point of the trace)\n");
|
||||
}
|
||||
if (o->trace) return tracePack(dv, cur, words, o);
|
||||
printf("pack %s: cache %.0f dataset %.0f hot %.0f check %.0f ms (%.0f ms in all); %s\n", o->packDir, cur->cacheMs, cur->datasetMs, cur->hotMs, cur->checkMs, wallMs() - t0, cur->check);
|
||||
printKernelInfo(di, cur->kHashBound, "igneum_hash_bound", (int)groupSize, "");
|
||||
dOut = clCreateBuffer(dv->ctx, CL_MEM_READ_WRITE, (size_t)nonces * sizeof(uint64_t), NULL, &err); CL_CHECK_ERR(err, "clCreateBuffer out");
|
||||
|
|
|
|||
40
proto-opencl/intel_rotr.h
Normal file
40
proto-opencl/intel_rotr.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* intel_rotr.h: the Intel rotate fold (7 October 2026, the Arc B580 register-trace bisect, docs/plans/intel-arc.md
|
||||
* section 7). Intel's OpenCL compiler turns the pack's `rotr_var(x, n) = rotate(x, (0u - n) & 31u)` into a rotate LEFT
|
||||
* by n (the negation dropped), so every variable right-rotate of every program came out wrong on the Arc while every
|
||||
* other family and the dataset kernels were bit-exact (lane 0's trace diverged at instruction 6 of iteration 0, rotr,
|
||||
* and nowhere before; job run-ia-arc-trace-20261007-b). On an Intel platform the worker rewrites that one helper line
|
||||
* to the shift form the CPU interpreter and the family probe compute, before clCreateProgramWithSource; the text is
|
||||
* otherwise untouched and no other vendor sees a change. Shared by host.c and test_intel_rotr.c (the gate's test). */
|
||||
#ifndef IGNEUM_INTEL_ROTR_H
|
||||
#define IGNEUM_INTEL_ROTR_H
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static const char* IGNEUM_ROTR_BUILTIN = "static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }";
|
||||
static const char* IGNEUM_ROTR_SHIFTS = "static inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }";
|
||||
|
||||
/* Returns the source to build: the original pointer when nothing applies, else a new buffer (the caller frees it when
|
||||
* `*patched` is set) with the one line replaced and `*srcLen` updated. `vendor` and `platform` are the device's
|
||||
* CL_DEVICE_VENDOR and the platform name; only a string holding "Intel" is rewritten. `quiet` suppresses the line. */
|
||||
static char* igneum_intel_rotr_patch(const char* vendor, const char* platform, const char* src, size_t* srcLen, int* patched, int quiet) {
|
||||
const char* at;
|
||||
char* out;
|
||||
size_t a = strlen(IGNEUM_ROTR_BUILTIN), b = strlen(IGNEUM_ROTR_SHIFTS), pre;
|
||||
*patched = 0;
|
||||
if (!((vendor && strstr(vendor, "Intel")) || (platform && strstr(platform, "Intel")))) return (char*)src;
|
||||
at = strstr(src, IGNEUM_ROTR_BUILTIN);
|
||||
if (!at) return (char*)src;
|
||||
pre = (size_t)(at - src);
|
||||
out = (char*)malloc(*srcLen - a + b + 1);
|
||||
if (!out) return (char*)src;
|
||||
memcpy(out, src, pre);
|
||||
memcpy(out + pre, IGNEUM_ROTR_SHIFTS, b);
|
||||
memcpy(out + pre + b, at + a, *srcLen - pre - a);
|
||||
out[*srcLen - a + b] = 0;
|
||||
*srcLen = *srcLen - a + b;
|
||||
*patched = 1;
|
||||
if (!quiet) printf("intel: rotr_var rewritten to the shift form before the build (the rotate fold of 7 October 2026)\n");
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
47
proto-opencl/test_intel_rotr.c
Normal file
47
proto-opencl/test_intel_rotr.c
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* test_intel_rotr.c: the gate's test of intel_rotr.h (7 October 2026). Feeds a kernel text holding the pack's rotr_var
|
||||
* helper line through the rewrite under an Intel vendor string and under AMD's and NVIDIA's, and asserts: the Intel
|
||||
* output carries the shift form and nothing else changed (the bytes before and after the line equal), the AMD and
|
||||
* NVIDIA outputs are the input pointer, untouched; a text without the line is returned untouched on Intel too.
|
||||
* cc -std=c99 -Wall -Wextra -o test_intel_rotr proto-opencl/test_intel_rotr.c && ./test_intel_rotr */
|
||||
#include "intel_rotr.h"
|
||||
#include <assert.h>
|
||||
|
||||
int main(void) {
|
||||
const char* pre = "static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }\n";
|
||||
const char* post = "\nstatic inline uint ds_elem(uint i, uint d0, uint d1) { return i ^ d0 ^ d1; }\n";
|
||||
char text[1024];
|
||||
size_t len, plen, blen = strlen(IGNEUM_ROTR_BUILTIN), slen = strlen(IGNEUM_ROTR_SHIFTS);
|
||||
int patched = -1;
|
||||
char* out;
|
||||
snprintf(text, sizeof(text), "%s%s%s", pre, IGNEUM_ROTR_BUILTIN, post);
|
||||
len = strlen(text);
|
||||
/* Intel: the one line becomes the shift form, the rest is byte for byte the same */
|
||||
plen = len;
|
||||
out = igneum_intel_rotr_patch("Intel(R) Corporation", "Intel(R) OpenCL Graphics", text, &plen, &patched, 1);
|
||||
assert(patched == 1);
|
||||
assert(out != text);
|
||||
assert(plen == len - blen + slen);
|
||||
assert(strlen(out) == plen);
|
||||
assert(memcmp(out, pre, strlen(pre)) == 0);
|
||||
assert(memcmp(out + strlen(pre), IGNEUM_ROTR_SHIFTS, slen) == 0);
|
||||
assert(strcmp(out + strlen(pre) + slen, post) == 0);
|
||||
assert(strstr(out, "rotate(x, (0u - n)") == NULL);
|
||||
free(out);
|
||||
/* the platform name alone names Intel too (a vendor string of another spelling) */
|
||||
plen = len; patched = -1;
|
||||
out = igneum_intel_rotr_patch("GenuineIntel", "Intel(R) OpenCL Graphics", text, &plen, &patched, 1);
|
||||
assert(patched == 1 && out != text); free(out);
|
||||
/* AMD and NVIDIA: untouched, the input pointer back, the length unchanged */
|
||||
plen = len; patched = -1;
|
||||
out = igneum_intel_rotr_patch("Advanced Micro Devices, Inc.", "AMD Accelerated Parallel Processing", text, &plen, &patched, 1);
|
||||
assert(patched == 0 && out == text && plen == len && strstr(text, IGNEUM_ROTR_BUILTIN) != NULL);
|
||||
plen = len; patched = -1;
|
||||
out = igneum_intel_rotr_patch("NVIDIA Corporation", "NVIDIA CUDA", text, &plen, &patched, 1);
|
||||
assert(patched == 0 && out == text && plen == len);
|
||||
/* Intel with no helper line (a probe kernel): untouched */
|
||||
plen = strlen(pre); patched = -1;
|
||||
out = igneum_intel_rotr_patch("Intel(R) Corporation", "Intel(R) OpenCL Graphics", pre, &plen, &patched, 1);
|
||||
assert(patched == 0 && out == pre && plen == strlen(pre));
|
||||
printf("test_intel_rotr: ok (Intel rewritten to the shift form, AMD and NVIDIA untouched, no line untouched)\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
second is the target, so the wait is network hashes per second over the card's rate. The rates are the bench table's
|
||||
measured numbers (site/miners.html); nothing here is a promise. Reads /api/live every 15 s. */
|
||||
(function () {
|
||||
var CARDS = [['NVIDIA RTX 5090', 127.7], ['NVIDIA RTX 4070', 28.8], ['Apple M5 Max', 26.7], ['AMD RX 9070 XT', 17.8]];
|
||||
var CARDS = [['NVIDIA RTX 5090', 127.7], ['NVIDIA RTX 4070', 28.8], ['Apple M5 Max', 26.7], ['AMD RX 9070 XT', 17.8], ['Intel Arc B580', 11.0]];
|
||||
var els = document.querySelectorAll('[data-yourcard]'); if (!els.length) return;
|
||||
var net = null, chosen = (function () { try { return localStorage.getItem('igneum-card'); } catch (e) { return null; } })();
|
||||
function dur(sec) { if (!(sec > 0)) return 'pending'; if (sec < 90) return Math.round(sec) + ' s'; if (sec < 5400) return Math.round(sec / 60) + ' min'; if (sec < 172800) { var h = Math.floor(sec / 3600), m = Math.round((sec % 3600) / 60); return h + ' h' + (m ? ' ' + m + ' min' : ''); } return Math.round(sec / 86400) + ' days'; }
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ tree_checks() {
|
|||
run "ship tool self-test" node tools/ship-app.mjs --self-test
|
||||
run "relay unit tests" node --test relay/test/parse.test.mjs relay/test/auth.test.mjs relay/test/wake.test.mjs relay/test/ember.test.mjs
|
||||
run "miner app notice strip and update card tests" node --test app/igneum-app/ui/notices.test.mjs app/igneum-app/ui/update-card.test.mjs app/igneum-app/ui/view.test.mjs app/igneum-app/ui/tune-line.test.mjs
|
||||
run "the Intel rotate-fold rewrite: Intel gets the shift form, AMD and NVIDIA untouched (proto-opencl/test_intel_rotr.c)" bash -c 'cc -std=c99 -Wall -Wextra -o "${TMPDIR:-/tmp}/test_intel_rotr.$$" proto-opencl/test_intel_rotr.c && "${TMPDIR:-/tmp}/test_intel_rotr.$$"; s=$?; rm -f "${TMPDIR:-/tmp}/test_intel_rotr.$$"; exit $s'
|
||||
run "no secret file names and no 64-hex secrets in the tree" bash -c 'bash tools/ci/no-secrets-check.sh --self-test && bash tools/ci/no-secrets-check.sh'
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,10 @@ for f in sorted(set(files)):
|
|||
if line.lstrip().startswith('#'): continue
|
||||
if sig.search(line):
|
||||
print(f'ps-params: {f}:{n}: a parameter named $args is shadowed by the automatic $args; rename it'); fail = 1
|
||||
for seg in re.findall(r'"(?:[^"\\]|\\.|`")*"', line):
|
||||
# a backtick escape (`" or any `x) is consumed FIRST: before 7 October 2026 the plain-character branch ate the backtick
|
||||
# and the escaped quote ended the string, so `$first:` after a `" in pc1-arc-trace.ps1 sat outside every string
|
||||
# and the job failed to parse on PC 1 (run-ia-arc-trace-20261007, 0 s)
|
||||
for seg in re.findall(r'"(?:`.|[^"`\\]|\\.)*"', line):
|
||||
for m in pat.finditer(seg):
|
||||
if m.group(1) in ok_names: continue
|
||||
print(f'ps-drive-ref: {f}:{n}: `${m.group(1)}:` inside a double-quoted string parses as a drive-qualified variable in PowerShell 5.1; write `${{{m.group(1)}}}:`')
|
||||
|
|
|
|||
72
tools/intel-arc/README.md
Normal file
72
tools/intel-arc/README.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Intel Arc B580 on PC 1: the jobs (7 October 2026, branch intel-arc)
|
||||
|
||||
Plan: `docs/plans/intel-arc.md`. PC 1 (`ae432dc7`) is the project lead's desk: nothing here runs without his go for that run; every job is a signed `run` job through `packaging/ota/publish-jobs.sh` from the main checkout (its signer binary lives there), never pausing, resuming or quitting the installed app, never posting to `api/cards` (the RUNNER's `--cards-off` takes a card alone and restores it on any exit; `tools/ci/playbook-quit-check.sh`).
|
||||
|
||||
## 1. The driver (ran 7 October 2026)
|
||||
|
||||
`pc1-intel-driver.ps1`, elevated (one UAC prompt, the project lead's click), no `--stop-miners`. Downloads `gfx_win_101.9034.exe` from `downloadmirror.intel.com/929959/` (932,631,144 bytes), checks the SHA512 Intel prints on page 785597 and the Authenticode signature, runs the installer with `-s` alone (silent, no `-b`: never reboots by itself), reads Intel's own logs under `C:\ProgramData\Intel\GFXInstaller`, then every adapter row, the PnP display devices, the class key's memory size and the installed worker's `--list`.
|
||||
|
||||
| Run | Published | Outcome |
|
||||
|---|---|---|
|
||||
| `run-ia-intel-driver-20261007` | 09:25:20Z | download 10 s, SHA512 equal, Authenticode "CN=Intel Corporation"; installer exit 1007 after 17 s: the bootstrapper wrapping the inner `Installer.exe`'s exit 7 ("unsupported parameter", my `-report <path>`); no device check ran; the .NET Desktop Runtime it needs was installed (exit 0) |
|
||||
| `run-ia-intel-driver-20261007-b` | 09:43:59Z, the card in | `-s` alone, 203 s, exit 1014 = the inner installer's 14 wrapped (installed, restart required): Level Zero 1.32.0, the Support Assistant 26.2.0.7 and Intel Graphics Software 26.32.2604.4 went on; the display driver did NOT bind (the device sat in Code 12 on Windows' inbox 6733 at install time) |
|
||||
| `run-ia-pc1-reboot-20261007` | 10:00:31Z | `pc1-reboot.ps1`, a plain run job (the project lead's word): `shutdown /r /t 20`, exit 0; the PC was back at 10:02Z |
|
||||
| `run-ia-intel-readback-20261007` and `-b` | 10:02:35Z, 10:06:11Z | `pc1-intel-readback.ps1` (read only): the Arc OK, Code 0, still on 6733; the Intel OpenCL 3.0 platform lists it with 11,928 MiB, 160 compute units, 2,850 MHz, local 128 KiB, `cl_khr_subgroup_shuffle`; the app's row `other:Intel(R) Arc(TM) B580 Graphics`, 8 identities (every card faulted on "no status line from the miner for 90 s" after the restart: the miner side, not the workers) |
|
||||
| `run-ia-intel-driver-20261007-c` | 10:07:45Z, the device healthy | `-s` alone, 156 s, exit 1014: 32.0.101.9034 bound to the device, Code 12 until the restart |
|
||||
| `run-ia-pc1-reboot-20261007-b`, `run-ia-intel-readback-20261007-c` | 10:13:12Z, 10:15:22Z | restart b taken; read-back: driver 32.0.101.9034, status OK, Code 0; the Intel OpenCL 3.0 platform lists the card on 9034 with 11,930 MiB |
|
||||
| `run-ia-arc-bench-20261007` | 10:23:04Z | VOID: the script benched device 0 (the Ryzen iGPU gfx1036; a second `-match` reset the capture); fixed, the next run carries the device's name on every row |
|
||||
|
||||
packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ia-intel-driver-<date>-<x> --elevated \
|
||||
--script tools/intel-arc/pc1-intel-driver.ps1 --timeout-minutes 60 --title "Intel Arc driver 32.0.101.9034 on PC 1" --deploy
|
||||
|
||||
## 1b. PC 1 not mining after the restart (7 October 2026, 11:2x to 11:4x UK)
|
||||
|
||||
Every card faulted on "no status line from the miner for 90 s" after each restart. The chain under main's words: `pc1-node-wait.ps1` (read only: the node was synced with an executed tip at once), the signed `restart --what miners`, `pc1-cards-watch.ps1` (read only, 3 minutes). The miners' own logs then said what they waited on: "template fetch timed out (5 s) for identity 1..8; mining continues on the current template" on every card, the app's export once "export-pack: no template", and from 10:39Z the app's own row message "waiting for the node to answer block templates" on all three with live pids: the node's block-template RPC, not the GPU side. `pc1-export-ahead.ps1` (`--stop-miners --cards-off <the Arc>`) showed the export itself takes 10.7 s and the pack was sound. Found on the way: the runner's miner hold stays until the NEXT job ends (watch b read "held for a remote job" for 3 minutes); two `igneum-miner.exe` processes alive under `--stop-miners` with the app's rows at pid 0.
|
||||
|
||||
ONE-OFF EXCEPTION, recorded by main at 12:3x UK on the project lead's word ("im not at pc1 now you will have to fix it"): `run-ia-cards-set-20261007` POSTed `api/cards` once from a scratchpad script (`ia-pc1-cards-set.ps1`, not in the tree): the 5090 and the 9070 XT enabled with 2 identities, the Arc off, persisted by the app in settings.json; then `restart-ia-miners-20261007-b`, then watch d. The rule stands for everything else.
|
||||
|
||||
## 2. The bench (published only on the project lead's go, after the card is in and the driver is on)
|
||||
|
||||
The kit first (packs only; the installed worker is the one under test):
|
||||
|
||||
tools/intel-arc/make-kit.sh "$TMPDIR/igneum-ia-arc-packs.zip" # prints sha256 and bytes
|
||||
packaging/ota/publish-jobs.sh add --kind fetch --target ae432dc7 --id fetch-ia-arc-packs-20261007 \
|
||||
--file "$TMPDIR/igneum-ia-arc-packs.zip" --dir jobs --extract --title "Intel Arc bench packs (class v4 and the v3 control)" --deploy
|
||||
|
||||
Published 7 October 2026 10:10:31Z: `fetch-ia-arc-packs-20261007`, sha256 `2c51181b0e9e2f7b236906730f5dcc270a62caa02346e8f91e23abbf1eb12c05`, 105,908 bytes (an app update wipes the jobs folder: republish under a new id after one).
|
||||
|
||||
Then the bench, with the live network rate substituted (the consequences line's blocks a day) and the Arc's key for the runner's `--cards-off` (read it from the app's Cards page or `api/state`: on the 0.3.18 app the Arc is `other:Intel(R) Arc(TM) B580 Graphics`, on an app that carries this branch `intel:Intel(R) Arc(TM) B580 Graphics`; the runner matches the key with or without the device index, and both can be named):
|
||||
|
||||
NET=$(node -e "..." ) # live_state.hashes_per_second_estimate / 1e6, or read it from the observer (docs/plans/intel-arc.md section 5: 675 at 09:34Z on 7 October)
|
||||
sed "s/__NETWORK_MHS__/$NET/" tools/intel-arc/pc1-arc-bench.ps1 > "$TMPDIR/ia-pc1-arc-bench.ps1"
|
||||
packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ia-arc-bench-20261007 \
|
||||
--cards-off "other:Intel(R) Arc(TM) B580 Graphics,intel:Intel(R) Arc(TM) B580 Graphics" \
|
||||
--script "$TMPDIR/ia-pc1-arc-bench.ps1" --timeout-minutes 30 --title "Intel Arc B580: first light, memprobe, class v4 and v3 benches" --deploy
|
||||
|
||||
What it prints (`node tools/jobs.mjs run-ia-arc-bench-20261007`): `RESULT list ...` (the Intel OpenCL platform line), `RESULT device <n> name= global_mib=`, `RESULT app_card key= vendor= kind= state=` (read only), `RESULT firstlight ...` (the app's own worker line for the card), `RESULT memprobe | chase | 1024 | ...` (the dependent-load ceiling), `RESULT G1 pack=v4-devnet-epoch0 fingerprint= match=yes|no` (bit-exact on the fourth vendor), the same for the v3 control, the live pack and the `--exchange local` row, `RESULT watts not-exposed source=IGCL...`, then `RESULT ROW ...`, `RESULT PICKER ['Intel Arc B580', <mhs>]` (the `site/yourcard.js` entry, measured so it may go on the site) and `RESULT CONSEQUENCES ...` (the B580 owner's sentence), and a `SUMMARY {json}` line. About 3 to 5 minutes: the card switch up to 150 s, the memprobe under a minute, four benches of 5 dispatches of 2^24 (about 1 s each at 15 MH/s, plus IGC's compile of a few seconds per pack).
|
||||
|
||||
If OpenCL fails on the Arc the `RESULT G1 ... error=` and `RESULT buildlog ...` lines carry the compiler's exact words; the fallback order and hours are in the plan's section 4.
|
||||
|
||||
## 2b. The register-trace bisect (7 October 2026, 12:0x UK, main's go)
|
||||
|
||||
Bench c and the two family-probe runs left one fact: every instruction family of the live hash is bit-exact on Intel's compiler (alu, rotr, shl, shr, bfe, andn, perm, popc, clz, sel, dot4, mulhi, mad and every lane exchange), and the hash is still wrong on every lane in both exchange modes. So the bisect: `make-trace-pack.py` appends `igneum_hash_trace` to a copy of the class v4 pack (igneum_hash_bound with a trace argument, lane 0's eight registers stored after the init and after every main and shadow instruction, 55,809 snapshots); `proto-opencl/host.c --trace --trace-out F` runs it after the pack self-test (the self-test's failure does not stop it); `igneum-pow trace-regs` prints the same sequence from the CPU interpreter (`interpret_warp_trace`, the hook form of `interpret_warp_scratch`); `pc1-arc-trace.ps1` runs the GPU trace in both exchange modes and diffs each against the CPU trace shipped in the kit, printing the first divergence with six lines of context and which registers differ.
|
||||
|
||||
# the CPU trace, on the build box (igneum-pow built there): the pack's own seeds, class and era as g2-recheck.sh derives them
|
||||
igneum-pow trace-regs --nonce 0 --epoch-hex <IGNEUM_SEED_BYTES_HEX> --day-hex <IGNEUM_DAY_BYTES_HEX> --class mx8+sh256x27 --era 0:<IGNEUM_ERA_SEED_HEX> > cpu-trace.txt
|
||||
IGNEUM_REDIST=... tools/intel-arc/make-trace-kit.sh cpu-trace.txt "$TMPDIR/igneum-ia-trace-kit.zip"
|
||||
packaging/ota/publish-jobs.sh add --kind fetch --target ae432dc7 --id fetch-ia-trace-kit-20261007 --file "$TMPDIR/igneum-ia-trace-kit.zip" --dir jobs --extract --title "Arc trace kit" --deploy
|
||||
packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ia-arc-trace-20261007 --cards-off "other:Intel(R) Arc(TM) B580 Graphics" --script tools/intel-arc/pc1-arc-trace.ps1 --timeout-minutes 20 --title "Arc B580: register trace against the CPU interpreter" --deploy
|
||||
|
||||
## 2c. What the bisect found and the fix (7 October 2026, 12:1x to 12:2x UK)
|
||||
|
||||
| Job | Result |
|
||||
|---|---|
|
||||
| `run-ia-arc-family-20261007` and `-b` | every family exact on Intel's compiler, `mulhi` and `mad` included (60 rows, 0 `exact=no`) |
|
||||
| `run-ia-arc-trace-20261007-b` | the first divergence at snapshot 7, instruction `6 rotr` (`r0 = rotr_var(r0, r2)`, r0 d1affd94, n f183b605): CPU a68d7fec = rotr(x, 5), the Arc 35ffb29a = rotl(x, 5): Intel's compiler folds `rotate(x, (0u - n) & 31u)` into a LEFT rotate by n; the same in both exchange modes |
|
||||
| the fix | commit 26e135a3: `proto-opencl/intel_rotr.h` rewrites the one helper line to the shift form on an Intel platform before the build (host.c's buildProgram and the prepare path); `proto-opencl/test_intel_rotr.c` in the pre-push gate (Intel rewritten, AMD and NVIDIA untouched); taken into release-0.3.20 by the shipper as 9088293a |
|
||||
| `run-ia-arc-trace-20261007-c` | with the fix, the local-exchange trace equals the CPU's in all 55,809 snapshots (the sub-group trace kernel alone diverges at a shuffle: the divergent lane-0 store before a sub-group shuffle, a trace-kernel artefact; the real kernel's vectors pass) |
|
||||
| `run-ia-arc-bench-20261007-d` | self-test PASS on every pack, fingerprints equal to the Mac's and the 5090's; 11.011 MH/s on class v4, 11.019 on the v3 control, 11.002 on the live pack, 10.882 local exchange; the bench-log row and the card-picker entry |
|
||||
|
||||
## 3. Dry run
|
||||
|
||||
The playbooks are PowerShell; this Mac has no `pwsh` (the Homebrew cask is gone from the tap and the Command Line Tools are too old for a cask install today), so the Mac-side dry run against the UI mock (`node tools/ui-mock/server.mjs 4310` with `?scenario=intel`, whose `api/state` carries the Intel row) is owed until a PowerShell is on the Mac (Microsoft's pkg, the project lead's word for the download). What stands in: the driver playbook ran live on PC 1 twice with the same helper shapes (Stamp, Summary, the adapter and worker-list readers), and the four CI checks pass on both scripts (`kit-path-check`, `ps-drive-ref-check`, `kill-by-name-check`, `playbook-quit-check`).
|
||||
25
tools/intel-arc/make-kit.sh
Normal file
25
tools/intel-arc/make-kit.sh
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
# The packs kit for tools/intel-arc/pc1-arc-bench.ps1 (7 October 2026, docs/plans/intel-arc.md section 5): the class v4
|
||||
# pack v4-devnet-epoch0 (the current class, fingerprint f410c731b6bc2d31 at 2^24) and the class v3 control
|
||||
# mx8-devnet-epoch0 (90f794dd556f7a3b), both from proto-cuda/packs-ca3-v4 as the 6 October gate run exported them, plus
|
||||
# SHA256SUMS. Packs only: the INSTALLED igneum-worker-opencl.exe on PC 1 is the worker under test, so no exe travels.
|
||||
# tools/intel-arc/make-kit.sh [out.zip] default $TMPDIR/igneum-ia-arc-packs.zip
|
||||
# Prints the zip's sha256 and size: the README's fetch line takes them.
|
||||
set -euo pipefail
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$HERE/../.." && pwd)"
|
||||
OUT="${1:-${TMPDIR:-/tmp}/igneum-ia-arc-packs.zip}"
|
||||
STAGE="$(mktemp -d)"
|
||||
mkdir -p "$STAGE/packs"
|
||||
for p in mx8-devnet-epoch0 v4-devnet-epoch0; do
|
||||
src="$ROOT/proto-cuda/packs-ca3-v4/$p"
|
||||
[ -f "$src/kernel_bound.cl" ] || { echo "pack $p has no kernel_bound.cl at $src" >&2; exit 1; }
|
||||
mkdir -p "$STAGE/packs/$p"
|
||||
cp "$src"/* "$STAGE/packs/$p/"
|
||||
done
|
||||
( cd "$STAGE" && find . -type f ! -name SHA256SUMS | sort | xargs shasum -a 256 > SHA256SUMS )
|
||||
rm -f "$OUT"
|
||||
( cd "$STAGE" && zip -X -q -r "$OUT" . )
|
||||
rm -rf "$STAGE"
|
||||
echo "kit $OUT"
|
||||
echo "sha256 $(shasum -a 256 "$OUT" | cut -c1-64) bytes $(wc -c < "$OUT" | tr -d ' ')"
|
||||
36
tools/intel-arc/make-trace-kit.sh
Normal file
36
tools/intel-arc/make-trace-kit.sh
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/env bash
|
||||
# The register-trace kit for tools/intel-arc/pc1-arc-trace.ps1 (7 October 2026, the Arc B580 bisect, docs/plans/intel-arc.md
|
||||
# section 7): the OpenCL worker from THIS tree's proto-opencl/host.c (with --trace; cross-compiled with mingw exactly as
|
||||
# tools/ca3-pc1-amd/make-kit.sh does, OpenCL.dll at run time), the trace pack made from proto-cuda/packs-ca3-v4/v4-devnet-epoch0
|
||||
# by make-trace-pack.py (igneum_hash_trace appended, trace_steps.txt beside), the CPU trace of the same pack from
|
||||
# igneum-pow's `trace-regs` (made on the build box; passed in as the first argument), the sources and SHA256SUMS.
|
||||
# tools/intel-arc/make-trace-kit.sh <cpu-trace.txt> [out.zip] default $TMPDIR/igneum-ia-trace-kit.zip
|
||||
# Needs x86_64-w64-mingw32-gcc and the Khronos OpenCL headers at $IGNEUM_REDIST/include (the ca2-mixer worktree holds a
|
||||
# copy: IGNEUM_REDIST=/Users/joshm/Projects/igneum-wt-ca2-mixer/proto-cuda/nvrtc/redist).
|
||||
set -euo pipefail
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$HERE/../.." && pwd)"
|
||||
CPU="${1:?cpu-trace.txt from igneum-pow trace-regs}"
|
||||
OUT="${2:-${TMPDIR:-/tmp}/igneum-ia-trace-kit.zip}"
|
||||
RED="${IGNEUM_REDIST:-$ROOT/proto-cuda/nvrtc/redist}"
|
||||
[ -f "$RED/include/CL/cl.h" ] || { echo "no OpenCL headers at $RED/include/CL/cl.h (set IGNEUM_REDIST)" >&2; exit 1; }
|
||||
[ -s "$CPU" ] || { echo "no CPU trace at $CPU" >&2; exit 1; }
|
||||
CC=x86_64-w64-mingw32-gcc; STRIP=x86_64-w64-mingw32-strip
|
||||
command -v "$CC" >/dev/null || { echo "$CC not found (brew install mingw-w64)" >&2; exit 1; }
|
||||
PLACEHOLDER="$ROOT/proto-cuda/packs/igneum-devnet-v4-epoch0"
|
||||
STAGE="$(mktemp -d)"
|
||||
mkdir -p "$STAGE/bin" "$STAGE/src" "$STAGE/packs"
|
||||
echo "== igneum-worker-opencl.exe (kit build of proto-opencl/host.c with --trace)"
|
||||
"$CC" -std=c99 -O2 -Wall -Wextra -Wno-stringop-truncation -Wno-format-truncation -static -DIGNEUM_CL_DYNAMIC -DCL_TARGET_OPENCL_VERSION=120 \
|
||||
-I "$RED/include" -I "$PLACEHOLDER" -DIGNEUM_KERNEL_PATH='"kernel_bound.cl"' -o "$STAGE/bin/igneum-worker-opencl.exe" "$ROOT/proto-opencl/host.c"
|
||||
"$STRIP" "$STAGE/bin/igneum-worker-opencl.exe"
|
||||
echo "== the trace pack"
|
||||
python3 "$HERE/make-trace-pack.py" "$ROOT/proto-cuda/packs-ca3-v4/v4-devnet-epoch0" "$STAGE/packs/v4-trace"
|
||||
cp "$CPU" "$STAGE/cpu-trace.txt"
|
||||
cp "$ROOT/proto-opencl/host.c" "$ROOT/proto-opencl/cl_dynamic.h" "$HERE/make-trace-pack.py" "$STAGE/src/"
|
||||
( cd "$STAGE" && find . -type f ! -name SHA256SUMS | sort | xargs shasum -a 256 > SHA256SUMS )
|
||||
rm -f "$OUT"
|
||||
( cd "$STAGE" && zip -X -q -r "$OUT" . )
|
||||
rm -rf "$STAGE"
|
||||
echo "kit $OUT"
|
||||
echo "sha256 $(shasum -a 256 "$OUT" | cut -c1-64) bytes $(wc -c < "$OUT" | tr -d ' ')"
|
||||
74
tools/intel-arc/make-trace-pack.py
Normal file
74
tools/intel-arc/make-trace-pack.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#!/usr/bin/env python3
|
||||
"""The register-trace pack for the Arc B580 bisect (7 October 2026, branch intel-arc, docs/plans/intel-arc.md section 7).
|
||||
|
||||
Copies a pack directory and appends to its kernel_bound.cl a kernel `igneum_hash_trace`: the text of
|
||||
`igneum_hash_bound` (the kernel the worker's self-test runs on the vectors, init words from `initw`) with one more
|
||||
argument, `__global uint* trace`, and a store of lane 0's eight registers (the work-item with get_global_id(0) == 0,
|
||||
nonce = baseNonce) after the init block and after EVERY program instruction (`// N family`) and shadow instruction
|
||||
(`// sN family`), in execution order: the same order igneum-pow's `trace-regs` prints from the CPU interpreter. A
|
||||
labels file (trace_steps.txt: one line per snapshot, "k it n family") goes beside the pack; the host reads its line
|
||||
count for the buffer size and prints the labels with the registers.
|
||||
|
||||
tools/intel-arc/make-trace-pack.py <pack dir> <out dir>
|
||||
"""
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
src = Path(sys.argv[1])
|
||||
out = Path(sys.argv[2])
|
||||
if out.exists():
|
||||
shutil.rmtree(out)
|
||||
shutil.copytree(src, out)
|
||||
text = (out / "kernel_bound.cl").read_text()
|
||||
lines = text.split("\n")
|
||||
start = next(i for i, l in enumerate(lines) if l.startswith("IGNEUM_KERNEL_HASH void igneum_hash_bound("))
|
||||
end = next(i for i in range(start, len(lines)) if lines[i] == "}")
|
||||
body = lines[start : end + 1]
|
||||
head = body[0]
|
||||
assert ", __global const uint* initw" in head, head
|
||||
# the trace argument LAST, so the self-test's argument order (ds, out, baseNonce, mask, initw[, hot][, scratch]) holds
|
||||
head = head.replace("igneum_hash_bound(", "igneum_hash_trace(").replace(") {", ", __global uint* trace) {")
|
||||
out_lines = [head]
|
||||
labels = []
|
||||
k = 0
|
||||
it_depth = 0
|
||||
STORE = "if (gid == 0u) { trace[tc * 8u + 0u] = r0; trace[tc * 8u + 1u] = r1; trace[tc * 8u + 2u] = r2; trace[tc * 8u + 3u] = r3; trace[tc * 8u + 4u] = r4; trace[tc * 8u + 5u] = r5; trace[tc * 8u + 6u] = r6; trace[tc * 8u + 7u] = r7; } tc += 1u;"
|
||||
inst = re.compile(r"//\s+(s?)(\d+)\s+(\w+)\s*$")
|
||||
seen_loop = False
|
||||
for l in body[1:]:
|
||||
out_lines.append(l)
|
||||
if l.startswith(" uint r0, r1,"):
|
||||
out_lines.append(" uint tc = 0u;")
|
||||
continue
|
||||
if l.startswith(" for (uint it = 0u;") and not seen_loop:
|
||||
# the init snapshot sits before the loop: the registers after the eight init blocks
|
||||
out_lines.insert(len(out_lines) - 1, " " + STORE)
|
||||
labels.append("init")
|
||||
seen_loop = True
|
||||
continue
|
||||
m = inst.search(l)
|
||||
if m and seen_loop:
|
||||
indent = l[: len(l) - len(l.lstrip())]
|
||||
out_lines.append(indent + STORE)
|
||||
labels.append(("s" if m.group(1) else "") + m.group(2) + " " + m.group(3))
|
||||
# the snapshot count: init + 8 x (64 main + shadow reps x shadow instrs). The kernel loop carries the count itself;
|
||||
# the labels file lists one iteration's labels and the host multiplies, so write the per-iteration list and the totals.
|
||||
main_labels = [x for x in labels if x != "init" and not x.startswith("s")]
|
||||
shadow_labels = [x for x in labels if x.startswith("s")]
|
||||
ktext = "\n".join(lines[: end + 1] + ["", "// register trace of igneum_hash_bound for lane 0 (tools/intel-arc/make-trace-pack.py)"] + out_lines + lines[end + 1 :])
|
||||
(out / "kernel_bound.cl").write_text(ktext)
|
||||
reps = 0
|
||||
for l in body:
|
||||
m2 = re.search(r"for \(uint sh = 0u; sh < (\d+)u; \+\+sh\)", l)
|
||||
if m2:
|
||||
reps = int(m2.group(1))
|
||||
total = 1 + 8 * (len(main_labels) + reps * len(shadow_labels))
|
||||
with (out / "trace_steps.txt").open("w") as f:
|
||||
f.write(f"total {total}\nmain {len(main_labels)}\nshadow {len(shadow_labels)}\nreps {reps}\n")
|
||||
for x in main_labels:
|
||||
f.write(f"m {x}\n")
|
||||
for x in shadow_labels:
|
||||
f.write(f"{x}\n")
|
||||
print(f"trace pack {out}: {len(main_labels)} main, {len(shadow_labels)} shadow x {reps} reps, {total} snapshots of 8 registers ({total * 32} bytes)")
|
||||
193
tools/intel-arc/pc1-arc-bench.ps1
Normal file
193
tools/intel-arc/pc1-arc-bench.ps1
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
# Igneum run job: the first Intel card, the Arc B580 on PC 1 (machine ae432dc7), 7 October 2026 (branch intel-arc,
|
||||
# docs/plans/intel-arc.md section 5). NOT published until the project lead says the card is in and gives the go.
|
||||
# Published as a signed `run` job with the RUNNER's --cards-off <the Arc's key> (NOT --stop-miners): the runner switches
|
||||
# the Arc off through the app's own card path before this script and puts it back on ANY exit; every other card keeps
|
||||
# mining. This script never switches a card, never stops, holds or ends the installed app (the playbook-quit rule and
|
||||
# tools/ci/playbook-quit-check.sh); it reads api/state once for the record. It never quits, pauses or updates the installed app and writes no settings.
|
||||
# After the fetch job that places the two packs (tools/intel-arc/make-kit.sh: v4-devnet-epoch0 = class v4, the current
|
||||
# class; mx8-devnet-epoch0 = the class v3 control; packs only, the INSTALLED worker is the one under test).
|
||||
# Steps: detect (the worker's --list, the Windows adapter and class-key rows: name, driver, 12 GB), first light (the
|
||||
# app's own worker line for the card from its log, read only), --memprobe (the dependent-load ceiling), --bench-pack on
|
||||
# class v4 (fingerprint f410c731b6bc2d31 expected), on the class v3 control (90f794dd556f7a3b), on the app's live pack,
|
||||
# then class v4 again with --exchange local; watts and clocks: not exposed through OpenCL (IGCL named as the source to
|
||||
# come); the row, the card-picker entry and the owner's consequences line from the live network rate.
|
||||
# Lines start with RESULT; a SUMMARY {json} line ends the job. Read back with `node tools/jobs.mjs <job id>`.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-arc-bench'
|
||||
$kitId = 'fetch-ia-arc-packs-20261007-b' # the packs (class v4, the v3 control)
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
$expected = @{ 'v4-devnet-epoch0' = 'f410c731b6bc2d31'; 'mx8-devnet-epoch0' = '90f794dd556f7a3b' } # bench-log, 6 October 2026, the G1 table (Metal = Apple OpenCL = RTX 5090)
|
||||
$batches = 5
|
||||
$networkMhs = [double] '__NETWORK_MHS__' # the publisher substitutes live_state.hashes_per_second_estimate / 1e6 (README); 0 = unknown, the line says so
|
||||
if ($networkMhs -isnot [double] -or [double]::IsNaN($networkMhs)) { $networkMhs = 0 }
|
||||
$subsidyIgn = 31.69 # IGN a block, spec 02 section 2.5 (BASE_SUBSIDY_PER_SECOND_SOMPI, 1 block a second)
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION network_mhs=$networkMhs"
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kit = Join-Path $jobs $kitId
|
||||
if (-not (Test-Path (Join-Path $kit 'packs\v4-devnet-epoch0\kernel_bound.cl'))) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; an app update wipes the jobs folder: republish it under a new id after one)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-intel-arc' }; New-Item -ItemType Directory -Force -Path $job | Out-Null
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$exe = $null
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $exe = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
if (-not $exe) { "RESULT error no installed igneum-worker-opencl.exe under $inst"; Summary 'failed' @{ error = 'no worker' }; exit 2 }
|
||||
# bench d (7 October 2026): the worker with the Intel rotate fold rewritten (host.c intelRotrPatch) from the trace kit,
|
||||
# when the kit is there; else the installed one (the kit-path rule: tested before use)
|
||||
$patchedKit = Join-Path $jobs 'fetch-ia-trace-kit-20261007-b'
|
||||
if (Test-Path (Join-Path $patchedKit 'bin\igneum-worker-opencl.exe')) { $exe = Join-Path $patchedKit 'bin\igneum-worker-opencl.exe'; "RESULT worker_source patched-kit $patchedKit" } else { "RESULT worker_source installed" }
|
||||
"RESULT app install=$inst app_dir=$appDir worker=$exe sha256=$((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower())"
|
||||
|
||||
# ---- detect: Windows' rows for the Intel device, then the worker's own list ----
|
||||
try {
|
||||
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) { "RESULT adapter name=`"$($a.Name)`" driver=$($a.DriverVersion) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID)" }
|
||||
} catch { "RESULT adapter error=$($_.Exception.Message)" }
|
||||
try {
|
||||
# the class key carries the true memory size (the 32-bit AdapterRAM caps at 4 GB); readable without elevation on PC 1? the driver job's run said no: printed when it answers, else the worker's global MiB is the size
|
||||
foreach ($k in @(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' })) {
|
||||
$p = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
|
||||
if ($p -and $p.DriverDesc -match 'Intel') { $mem = '-'; if ($null -ne $p.'HardwareInformation.qwMemorySize') { $mem = [string]([math]::Round([double]$p.'HardwareInformation.qwMemorySize' / 1MB)) + ' MiB' }; "RESULT class key=$($k.PSChildName) desc=`"$($p.DriverDesc)`" driver=$($p.DriverVersion) memory=$mem" }
|
||||
}
|
||||
} catch { "RESULT class not_readable=$($_.Exception.Message)" }
|
||||
$list = @(& $exe --list 2>&1 | ForEach-Object { "$_" })
|
||||
$list | ForEach-Object { "RESULT list $_" }
|
||||
$dev = $null; $devName = ''
|
||||
# The 10:25Z run benched device 0 (the Ryzen iGPU): a second -match on the same line reset $Matches before the capture
|
||||
# was read, so [int]$null = 0 became the device. The capture is taken with [regex]::Match and tested on its own.
|
||||
for ($i = 0; $i -lt $list.Count; $i++) {
|
||||
$l = $list[$i]
|
||||
if ($l -notmatch 'Intel' -or $l -match '^\s*dup') { continue }
|
||||
$m = [regex]::Match($l, '^\s*\[(\d+)\]\s+(.*?)\s+\|')
|
||||
if (-not $m.Success) { continue }
|
||||
# the next line says GPU or CPU: Intel's CPU runtime, when present, lists a CPU device on its own platform
|
||||
$next = if ($i + 1 -lt $list.Count) { $list[$i + 1] } else { '' }
|
||||
if ($next -match '^\s*CPU') { continue }
|
||||
$dev = [int]$m.Groups[1].Value; $devName = $m.Groups[2].Value; break
|
||||
}
|
||||
if ($null -ne $dev -and $devName -notmatch 'Intel') { "RESULT error device $dev is `"$devName`", not an Intel device: refusing to bench the wrong card"; Summary 'failed' @{ error = 'wrong device' }; exit 2 }
|
||||
if ($null -eq $dev) { "RESULT error no Intel GPU device in --list (is the driver 32.0.101.9034 in and the card seen by Windows? the adapter rows above say)"; Summary 'failed' @{ error = 'no intel device' }; exit 2 }
|
||||
$globalMib = ''
|
||||
for ($j = $i; $j -lt [math]::Min($list.Count, $i + 4); $j++) { if ($list[$j] -match 'global (\d+) MiB') { $globalMib = $Matches[1]; break } }
|
||||
"RESULT device $dev name=`"$devName`" global_mib=$globalMib"
|
||||
# READ ONLY: the app's view of the card (its key is what the publisher's --cards-off named; the runner, not this script, switched it)
|
||||
$cardKey = ''
|
||||
try {
|
||||
$urlFile = Join-Path $appDir 'app.url'
|
||||
if (Test-Path $urlFile) {
|
||||
$url = (Get-Content -LiteralPath $urlFile -Raw).Trim()
|
||||
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
|
||||
$all = $st.mining.cards; if (-not $all) { $all = $st.cards }
|
||||
foreach ($c in @($all | Where-Object { $_.name -match 'Intel' })) { $cardKey = $c.key; "RESULT app_card key=$($c.key) vendor=$($c.vendor) kind=$($c.kind) enabled=$($c.enabled) identities=$($c.identities) state=$($c.state) pid=$($c.pid) hash_now=$($c.hash_now) vram_mb=$($c.vram_mb) message=`"$($c.message)`"" }
|
||||
}
|
||||
} catch { "RESULT app_card error=$($_.Exception.Message)" }
|
||||
# first light, read only: the app's log lines for the Intel worker (ready, exchange, sub-group, or its error)
|
||||
try {
|
||||
$logDir = $env:IGNEUM_LOG_DIR; if (-not $logDir) { $logDir = Join-Path $appDir 'logs' }
|
||||
$newest = Get-ChildItem -Path $logDir -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($newest) { Get-Content -LiteralPath $newest.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'Intel|intel:|--device ' + $dev + '(\s|$)' } | Select-Object -Last 12 | ForEach-Object { "RESULT firstlight $_" } }
|
||||
} catch { "RESULT firstlight error=$($_.Exception.Message)" }
|
||||
# the card quiet? read-only, by the process list: the runner's --cards-off took, or not
|
||||
function CardWorkers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match "--device\s+$dev(\s|$)" } | ForEach-Object { $_.ProcessId }) }
|
||||
$cardAlone = $false
|
||||
$t = 0
|
||||
while ($t -lt 150) { if ((@(CardWorkers)).Count -eq 0) { $cardAlone = $true; break }; Start-Sleep -Seconds 5; $t += 5 }
|
||||
if ($cardAlone) { "RESULT card $(Stamp) quiet after $t s: no igneum-worker-opencl process with --device $dev (the card to itself)" } else { "RESULT card $(Stamp) UNCONFIRMED after $t s: an opencl worker still carries --device $dev (was the job published with --cards-off $cardKey ?); the rows below are LOADED-card figures" }
|
||||
|
||||
# Runs the worker as a child under a wall-clock cap; a child past its cap is ended by ITS OWN pid (taskkill /PID, never
|
||||
# a name): the 7 October run's memprobe on the Arc never returned inside the job's 30-minute cap. Output comes back
|
||||
# through a file; the lines are returned with a trailing "timeout <cap>s" line when the cap fired.
|
||||
function Capped([string] $tag, [string[]] $argv, [int] $capS) {
|
||||
$out = Join-Path $job ("$tag.out")
|
||||
Remove-Item -LiteralPath $out -Force -ErrorAction SilentlyContinue
|
||||
$p = Start-Process -FilePath $exe -ArgumentList $argv -NoNewWindow -PassThru -RedirectStandardOutput $out -RedirectStandardError (Join-Path $job "$tag.err")
|
||||
$done = $p.WaitForExit($capS * 1000)
|
||||
if (-not $done) { try { & taskkill /T /F /PID $p.Id 2>&1 | Out-Null } catch { }; Start-Sleep -Seconds 2 }
|
||||
$lines = @(); if (Test-Path -LiteralPath $out) { $lines = @(Get-Content -LiteralPath $out -ErrorAction SilentlyContinue) }
|
||||
$errf = Join-Path $job "$tag.err"; if (Test-Path -LiteralPath $errf) { $lines += @(Get-Content -LiteralPath $errf -ErrorAction SilentlyContinue) }
|
||||
if (-not $done) { $lines += "timeout ${capS}s: the worker was ended by its pid $($p.Id)" }
|
||||
if ($done) { try { $p.Refresh() } catch { } }
|
||||
$script:lastExit = if ($done) { $p.ExitCode } else { 124 }
|
||||
return $lines
|
||||
}
|
||||
$script:lastExit = 0
|
||||
|
||||
# ---- memprobe: the dependent-load ceiling (3 minutes per size: 4, 64 and 1024 MiB, one call each) ----
|
||||
"RESULT memprobe start $(Stamp)"
|
||||
$mp = @()
|
||||
foreach ($mib in @(4, 64, 1024)) {
|
||||
$part = @(Capped "memprobe-$mib" @('--device', "$dev", '--memprobe', '--probe-mib', "$mib") 180)
|
||||
"RESULT memprobe size=$mib exit=$script:lastExit lines=$($part.Count)"
|
||||
$mp += $part
|
||||
}
|
||||
$mp | Where-Object { $_ -match '^memprobe|^\||^timeout' -and $_ -notmatch '^\|---' } | ForEach-Object { "RESULT memprobe $_" }
|
||||
$on = $mp | Where-Object { $_ -match '^memprobe on ' } | Select-Object -First 1
|
||||
if ($on -and $on -notmatch 'Intel') { "RESULT error the memprobe ran on `"$on`": not the Intel device; the rows below are void"; Summary 'failed' @{ error = 'memprobe on the wrong device'; on = $on }; exit 2 }
|
||||
$ceilingGl = 0.0
|
||||
foreach ($l in $mp) { if ($l -match '^\|\s*chase\s*\|\s*1024\s*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|\s*([\d.]+)\s*\|') { $v = [double]$Matches[1]; if ($v -gt $ceilingGl) { $ceilingGl = $v } } }
|
||||
$ceilingMhs = if ($ceilingGl -gt 0) { [math]::Round($ceilingGl * 1000 / 128, 2) } else { 0 }
|
||||
"RESULT memprobe end $(Stamp) chase_1024mib_gloads_s=$ceilingGl ceiling_mhs_at_128_loads=$ceilingMhs"
|
||||
|
||||
# ---- the benches ----
|
||||
# A function's uncaptured output is its return value: "$v = Bench ..." swallowed every RESULT line of the 10:50Z run
|
||||
# (the pack-missing lines never reached the report). Bench writes its lines into $script:benchOut; Flush prints them.
|
||||
$script:benchOut = @()
|
||||
function Say([string] $l) { $script:benchOut += $l }
|
||||
function Flush { $script:benchOut | ForEach-Object { $_ }; $script:benchOut = @() }
|
||||
function Bench([string] $label, [string] $packDir, [string[]] $extra) {
|
||||
if (-not (Test-Path $packDir)) { Say "RESULT bench $label error=pack missing at $packDir"; return $null }
|
||||
$t0 = (Get-Date).ToUniversalTime()
|
||||
Say "RESULT bench $label start $(Stamp) pack=$packDir batches=$batches extra=$($extra -join ' ')"
|
||||
$lines = @(Capped "bench-$label" (@('--bench-pack', '--pack', $packDir, '--batches', "$batches", '--batch-log2', '24', '--device', "$dev") + $extra) 240)
|
||||
$rc = $script:lastExit
|
||||
$lines | Where-Object { $_ -match '^RESULT|^pack |^warm-up|^build |^kernel:|^exchange|sub-group|FAIL|error|MISMATCH|spill|private memory|^timeout' } | Select-Object -First 30 | ForEach-Object { Say "RESULT bench $label $_" }
|
||||
Say "RESULT bench $label end $(Stamp) exit=$rc wall_s=$([int]((Get-Date).ToUniversalTime() - $t0).TotalSeconds)"
|
||||
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
|
||||
# the child's exit code can read null after WaitForExit on a -PassThru process (bench d, 7 October 2026: every pack
|
||||
# PASSed and the rows said "exit , no RESULT line"); a present RESULT line with check=PASS is the verdict
|
||||
if ($null -eq $rc -or "$rc" -eq '') { $rc = 0 }
|
||||
if (-not $res -or $rc -ne 0) {
|
||||
$err = ($lines | Where-Object { $_ -match 'FAIL|error|MISMATCH' } | Select-Object -First 1); if (-not $err) { $err = "exit $rc, no RESULT line" }
|
||||
Say "RESULT G1 pack=$label error=$($err -replace '\s+', ' ')"
|
||||
# the build log, when the compiler refused the kernel: the exact error for the fallback decision (plan section 4)
|
||||
$lines | Where-Object { $_ -match 'error|warning|catastrophic|undefined|spir' } | Select-Object -First 20 | ForEach-Object { Say "RESULT buildlog $label $_" }
|
||||
return $null
|
||||
}
|
||||
$fp = ''; $mhs = ''; $check = ''
|
||||
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
|
||||
if ($res -match 'check=(\S+)') { $check = $Matches[1] }
|
||||
$want = $expected[$label]
|
||||
$match = if ($want) { if ($fp -eq $want) { 'yes' } else { 'no' } } else { 'n/a' }
|
||||
Say "RESULT G1 pack=$label fingerprint=$fp match=$match expected=$want check=$check card=intel-arc harness=opencl-installed"
|
||||
return [pscustomobject]@{ label = $label; mhs = [double]$mhs; fp = $fp; match = $match; check = $check }
|
||||
}
|
||||
$v4 = Bench 'v4-devnet-epoch0' (Join-Path $kit 'packs\v4-devnet-epoch0') @(); Flush
|
||||
$v3 = Bench 'mx8-devnet-epoch0' (Join-Path $kit 'packs\mx8-devnet-epoch0') @(); Flush
|
||||
$live = Bench 'live-devnet' (Join-Path $appDir 'packs\devnet') @(); Flush
|
||||
$v4local = Bench 'v4-local-exchange' (Join-Path $kit 'packs\v4-devnet-epoch0') @('--exchange', 'local'); Flush
|
||||
# watts and clocks: OpenCL exposes neither; the PDH GPU Engine counter gives busy percent only (named, not a watt)
|
||||
"RESULT watts not-exposed source=IGCL(ctlPowerTelemetryGet, ControlLib.dll) to come; clocks not-exposed; the helper igneum-gpu-telemetry reads AMD (ADLX) only today"
|
||||
|
||||
# ---- the row and the consequences ----
|
||||
$rate = if ($v4) { $v4.mhs } elseif ($live) { $live.mhs } else { 0 }
|
||||
$blocksDay = if ($networkMhs -gt 0 -and $rate -gt 0) { [math]::Round($rate / $networkMhs * 86400, 2) } else { 0 }
|
||||
$hoursBlock = if ($blocksDay -gt 0) { [math]::Round(24 / $blocksDay, 1) } else { 0 }
|
||||
$ignDay = [math]::Round($blocksDay * $subsidyIgn, 1)
|
||||
$shadowCost = if ($v4 -and $v3 -and $v3.mhs -gt 0) { [math]::Round((1 - $v4.mhs / $v3.mhs) * 100, 1) } else { 'owed' }
|
||||
$share = if ($ceilingMhs -gt 0 -and $rate -gt 0) { [math]::Round($rate / $ceilingMhs, 2) } else { 'owed' }
|
||||
$drvNow = ''
|
||||
try { $drvNow = (Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'Intel' } | Select-Object -First 1).DriverVersion } catch { }
|
||||
"RESULT ROW card=`"Intel Arc B580`" driver=$drvNow device=$dev global_mib=$globalMib class_v4_mhs=$(if ($v4) { $v4.mhs } else { 'failed' }) class_v3_mhs=$(if ($v3) { $v3.mhs } else { 'failed' }) live_mhs=$(if ($live) { $live.mhs } else { 'failed' }) local_exchange_mhs=$(if ($v4local) { $v4local.mhs } else { 'failed' }) ceiling_mhs=$ceilingMhs latency_share=$share shadow_cost_pct=$shadowCost watts=not-exposed mhw=not-exposed network_mhs=$networkMhs blocks_per_day=$blocksDay hours_per_block=$hoursBlock ign_per_day=$ignDay card_alone=$cardAlone"
|
||||
"RESULT PICKER ['Intel Arc B580', $rate] (site/yourcard.js CARDS entry; measured, so it may go on the site)"
|
||||
$prove = 'mine only: the prover is CUDA only, and even a 12 GB NVIDIA card cannot mine and prove at once on this build'
|
||||
"RESULT CONSEQUENCES a B580 owner (12 GB, Windows): $rate MH/s on the OpenCL worker, $blocksDay blocks a day at today's $networkMhs MH/s network (one every $hoursBlock h, about $ignDay IGN a day at $subsidyIgn IGN a block, approximate: the network rate moves); hash per watt not measurable yet (no power reading through OpenCL; IGCL reader next, 2 to 3 hours); $prove; 8 GB and 16 GB Intel rows owed (no card in hand); Linux and HiveOS take the same worker on intel-opencl-icd (owed a line)"
|
||||
$status = if ($v4 -and $v4.match -eq 'yes') { 'done' } elseif ($v4 -or $live) { 'partial' } else { 'failed' }
|
||||
"RESULT end $(Stamp)"
|
||||
Summary $status @{ device = $dev; name = $devName; rate_mhs = $rate; ceiling_mhs = $ceilingMhs; v4_match = $(if ($v4) { $v4.match } else { 'failed' }); v3_match = $(if ($v3) { $v3.match } else { 'failed' }); blocks_per_day = $blocksDay; card_alone = $cardAlone; watts = 'not-exposed' }
|
||||
if ($status -eq 'failed') { exit 1 }
|
||||
exit 0
|
||||
45
tools/intel-arc/pc1-arc-family.ps1
Normal file
45
tools/intel-arc/pc1-arc-family.ps1
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Igneum run job: the OpenCL family probe on the Arc B580, PC 1 (machine ae432dc7), 7 October 2026 (branch intel-arc).
|
||||
# Bench c showed the hash wrong on every lane on Intel's compiler in both exchange modes while the cache and dataset
|
||||
# are bit-exact: a per-lane instruction family differs on IGC. This probe (proto-opencl/family-probe.c, kit
|
||||
# fetch-ia-family-kit-20261007, built by tools/ca3-pc1-amd/make-family-kit.sh) runs one op of each family per step per
|
||||
# lane and checks every variant bit for bit against the CPU reference on two whole 32-lane groups: `exact=no` names
|
||||
# the family. Published with the runner's --cards-off <the Arc's key> (the card to itself; the 5090 and the 9070 XT keep
|
||||
# mining); read only otherwise (one GET of api/state for the record); never a card switch, pause, resume or quit.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-arc-family'
|
||||
$kitId = 'fetch-ia-family-kit-20261007-b'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kit = Join-Path $jobs $kitId
|
||||
$probe = Join-Path $kit 'bin\family-probe-cl.exe'
|
||||
if (-not (Test-Path $probe)) { "RESULT error probe missing at $probe (the fetch job $kitId runs first; an app update wipes the jobs folder)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||
"RESULT probe $probe sha256=$((Get-FileHash -Algorithm SHA256 $probe).Hash.ToLower())"
|
||||
$list = @(& $probe --list 2>&1 | ForEach-Object { "$_" })
|
||||
$list | Where-Object { $_ -match 'Intel|OpenCL devices|\[' } | ForEach-Object { "RESULT list $_" }
|
||||
function CardWorkers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match 'device\s+2(\s|$)' } | ForEach-Object { $_.ProcessId }) }
|
||||
"RESULT card_workers pids=[$((CardWorkers) -join ' ')] (the Arc is off and persisted; the runner's --cards-off holds it through this job)"
|
||||
$rows = 0; $exactNo = @(); $exactYes = @(); $failed = @()
|
||||
for ($r = 1; $r -le 3; $r++) {
|
||||
"RESULT run=$r start $(Stamp)"
|
||||
$out = @(& $probe --device-name 'Arc' --reps 3 2>&1 | ForEach-Object { "$_" })
|
||||
foreach ($l in $out) {
|
||||
if ($l -match '^RESULT ') { "RESULT run=$r $($l.Substring(7))" } elseif ($l -match 'error|FAIL|build') { "RESULT run=$r note $l" }
|
||||
if ($l -match '^RESULT FAMILY name=(\S+) .*exact=(\S+)' ) { $rows++; if ($Matches[2] -eq 'no') { $exactNo += $Matches[1] } elseif ($Matches[2] -eq 'yes') { $exactYes += $Matches[1] } }
|
||||
if ($l -match '^RESULT FAMILY name=(\S+) .*build=failed') { $failed += $Matches[1] }
|
||||
}
|
||||
"RESULT run=$r end $(Stamp) exit=$LASTEXITCODE"
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
$no = @($exactNo | Sort-Object -Unique); $yes = @($exactYes | Sort-Object -Unique); $fb = @($failed | Sort-Object -Unique)
|
||||
"RESULT VERDICT rows=$rows exact_no=[$($no -join ' ')] exact_yes=[$($yes -join ' ')] build_failed=[$($fb -join ' ')]"
|
||||
"RESULT end $(Stamp)"
|
||||
Summary $(if ($rows -gt 0) { 'done' } else { 'failed' }) @{ rows = $rows; exact_no = $no; exact_yes = $yes; build_failed = $fb }
|
||||
if ($rows -eq 0) { exit 1 }
|
||||
exit 0
|
||||
74
tools/intel-arc/pc1-arc-trace.ps1
Normal file
74
tools/intel-arc/pc1-arc-trace.ps1
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Igneum run job: the register-trace bisect on the Arc B580, PC 1 (machine ae432dc7), 7 October 2026 (branch intel-arc,
|
||||
# docs/plans/intel-arc.md section 7). Every instruction family is bit-exact on Intel's compiler and the hash still fails
|
||||
# on every lane, so this job runs the kit worker's --trace on the trace pack (igneum_hash_trace: lane 0's eight
|
||||
# registers after the init and after every instruction, 55,809 snapshots on class v4) in BOTH exchange modes and diffs
|
||||
# each against the CPU interpreter's trace shipped in the kit (igneum-pow trace-regs): the first differing snapshot
|
||||
# names the instruction. Published with the runner's --cards-off <the Arc's key> (the card to itself; the 5090 and the
|
||||
# 9070 XT keep mining); read only otherwise (one GET of api/state for the record); never a card switch, pause, resume
|
||||
# or quit. Lines: RESULT; the first divergence with 6 lines of context from both traces; a SUMMARY {json} line.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-arc-trace'
|
||||
$kitId = 'fetch-ia-trace-kit-20261007-b'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kit = Join-Path $jobs $kitId
|
||||
if (-not (Test-Path (Join-Path $kit 'cpu-trace.txt'))) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; an app update wipes the jobs folder)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||
$exe = Join-Path $kit 'bin\igneum-worker-opencl.exe'
|
||||
$pack = Join-Path $kit 'packs\v4-trace'
|
||||
$cpu = Join-Path $kit 'cpu-trace.txt'
|
||||
$job = $env:IGNEUM_JOB_DIR
|
||||
"RESULT worker $exe sha256=$((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) cpu_trace_lines=$((Get-Content -LiteralPath $cpu | Measure-Object -Line).Lines)"
|
||||
$list = @(& $exe --list 2>&1 | ForEach-Object { "$_" })
|
||||
$dev = $null; $devName = ''
|
||||
for ($i = 0; $i -lt $list.Count; $i++) {
|
||||
$l = $list[$i]
|
||||
if ($l -notmatch 'Intel' -or $l -match '^\s*dup') { continue }
|
||||
$m = [regex]::Match($l, '^\s*\[(\d+)\]\s+(.*?)\s+\|')
|
||||
if (-not $m.Success) { continue }
|
||||
$next = if ($i + 1 -lt $list.Count) { $list[$i + 1] } else { '' }
|
||||
if ($next -match '^\s*CPU') { continue }
|
||||
$dev = [int]$m.Groups[1].Value; $devName = $m.Groups[2].Value; break
|
||||
}
|
||||
if ($null -eq $dev -or $devName -notmatch 'Intel') { "RESULT error no Intel GPU device in --list"; Summary 'failed' @{ error = 'no intel device' }; exit 2 }
|
||||
"RESULT device $dev name=`"$devName`""
|
||||
$cpuLines = @(Get-Content -LiteralPath $cpu)
|
||||
$verdicts = @{}
|
||||
foreach ($mode in @('auto', 'local')) {
|
||||
$gpuOut = Join-Path $job "gpu-trace-$mode.txt"
|
||||
$t0 = Get-Date
|
||||
"RESULT trace $mode start $(Stamp)"
|
||||
$lines = @(& $exe --bench-pack --pack $pack --batches 1 --batch-log2 10 --device $dev --exchange $mode --trace --trace-out $gpuOut 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$lines | Where-Object { $_ -match '^RESULT|^FAIL|^trace:|^build |^kernel:|exchange|error' } | Select-Object -First 12 | ForEach-Object { "RESULT trace $mode $_" }
|
||||
"RESULT trace $mode end $(Stamp) exit=$rc wall_s=$([int]((Get-Date) - $t0).TotalSeconds) file_present=$(Test-Path -LiteralPath $gpuOut)"
|
||||
if (-not (Test-Path -LiteralPath $gpuOut)) { $verdicts[$mode] = 'no trace'; continue }
|
||||
$gpuLines = @(Get-Content -LiteralPath $gpuOut)
|
||||
"RESULT compare $mode gpu_lines=$($gpuLines.Count) cpu_lines=$($cpuLines.Count)"
|
||||
$n = [math]::Min($gpuLines.Count, $cpuLines.Count)
|
||||
$first = -1
|
||||
for ($k = 0; $k -lt $n; $k++) { if ($gpuLines[$k] -ne $cpuLines[$k]) { $first = $k; break } }
|
||||
if ($first -lt 0) {
|
||||
if ($gpuLines.Count -eq $cpuLines.Count) { "RESULT VERDICT $mode identical: every one of $n snapshots equal (the GPU trace equals the CPU interpreter's)"; $verdicts[$mode] = 'identical' }
|
||||
else { "RESULT VERDICT $mode prefix equal over $n lines but the counts differ"; $verdicts[$mode] = 'count differs' }
|
||||
continue
|
||||
}
|
||||
"RESULT VERDICT $mode first divergence at snapshot ${first}: gpu `"$($gpuLines[$first])`" cpu `"$($cpuLines[$first])`""
|
||||
$lo = [math]::Max(0, $first - 6); $hi = [math]::Min($n - 1, $first + 6)
|
||||
for ($k = $lo; $k -le $hi; $k++) { "RESULT ctx $mode $k $(if ($gpuLines[$k] -eq $cpuLines[$k]) { 'same' } else { 'DIFF' }) gpu=$($gpuLines[$k] -replace '^TRACE \d+ ', '') cpu=$($cpuLines[$k] -replace '^TRACE \d+ ', '')" }
|
||||
# which registers differ at the first divergence, and how many of the later snapshots agree again (a one-off vs a cascade)
|
||||
$g = ($gpuLines[$first] -split ' '); $c = ($cpuLines[$first] -split ' ')
|
||||
$regs = @(); for ($r = 0; $r -lt 8; $r++) { if ($g[3 + $r] -ne $c[3 + $r]) { $regs += "r$r" } }
|
||||
$later = 0; for ($k = $first + 1; $k -lt $n; $k++) { if ($gpuLines[$k] -eq $cpuLines[$k]) { $later++ } }
|
||||
"RESULT VERDICT $mode registers_differ=[$($regs -join ' ')] label=`"$($c[2]) $($c[3 - 1])`" equal_after=$later of $($n - $first - 1)"
|
||||
$verdicts[$mode] = "diverges at $first ($($c[2]))"
|
||||
}
|
||||
"RESULT end $(Stamp)"
|
||||
Summary 'done' @{ device = $dev; verdicts = $verdicts }
|
||||
exit 0
|
||||
53
tools/intel-arc/pc1-cards-watch.ps1
Normal file
53
tools/intel-arc/pc1-cards-watch.ps1
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Igneum run job: watch PC 1's cards after the signed miners restart (machine ae432dc7), 7 October 2026 (branch
|
||||
# intel-arc). READ ONLY: GET api/state every 15 s for 3 minutes, each card's state, hash_now and message; the Intel
|
||||
# adapter's driver version (32.0.101.9034 expected) and the installed worker's OpenCL platform line for the Arc; and,
|
||||
# for any card that reads faulted or failed while the node is synced, the tail of the app's log for that card's worker
|
||||
# (the miner's own lines). No api/* request other than GET api/state; nothing posted, nothing restarted.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
$started = Get-Date
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$urlFile = Join-Path $appDir 'app.url'
|
||||
"RESULT start $(Stamp) job=pc1-cards-watch machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
try { foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'Intel' })) { "RESULT intel_device $($a.Name) driver $($a.DriverVersion) status $($a.Status) code $($a.ConfigManagerErrorCode)" } } catch { "RESULT intel_device error=$($_.Exception.Message)" }
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) {
|
||||
$list = @(& (Join-Path $inst 'igneum-worker-opencl.exe') --list 2>&1 | ForEach-Object { "$_" })
|
||||
for ($i = 0; $i -lt $list.Count; $i++) { if ($list[$i] -match 'Intel') { "RESULT list $($list[$i])"; if ($i + 1 -lt $list.Count) { "RESULT list $($list[$i + 1])" }; if ($i + 2 -lt $list.Count) { "RESULT list $($list[$i + 2])" }; break } }
|
||||
}
|
||||
$final = @{}
|
||||
$synced = $false
|
||||
for ($t = 0; $t -le 180; $t += 15) {
|
||||
try {
|
||||
$url = (Get-Content -LiteralPath $urlFile -Raw).Trim()
|
||||
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
|
||||
$synced = [bool]$st.node.synced
|
||||
$cards = @($st.mining.cards); if (-not $cards) { $cards = @($st.cards) }
|
||||
foreach ($c in $cards) {
|
||||
if ($c.kind -eq 'integrated') { continue }
|
||||
"RESULT t+${t}s $(Stamp) node_synced=$synced card=`"$($c.name)`" key=$($c.key) state=$($c.state) pid=$($c.pid) hash_now=$($c.hash_now) hash_avg=$($c.hash_avg) accepted=$($c.accepted) power_w=$($c.power_w) message=`"$($c.message)`""
|
||||
$final[$c.name] = $c
|
||||
}
|
||||
} catch { "RESULT t+${t}s $(Stamp) api/state: $($_.Exception.Message)" }
|
||||
if ($t -lt 180) { Start-Sleep -Seconds 15 }
|
||||
}
|
||||
$rates = @{}
|
||||
foreach ($name in $final.Keys) {
|
||||
$c = $final[$name]
|
||||
$rates[$name] = $c.hash_now
|
||||
"RESULT FINAL card=`"$name`" state=$($c.state) hash_now=$($c.hash_now) message=`"$($c.message)`""
|
||||
if ($c.state -match 'faulted|failed' -and $synced) {
|
||||
try {
|
||||
$logDir = $env:IGNEUM_LOG_DIR; if (-not $logDir) { $logDir = Join-Path $appDir 'logs' }
|
||||
$dev = "$($c.device)"
|
||||
$files = @(Get-ChildItem -Path $logDir -Filter '*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 3)
|
||||
foreach ($f in $files) {
|
||||
$hits = @(Get-Content -LiteralPath $f.FullName -ErrorAction SilentlyContinue | Where-Object { ($_ -match [regex]::Escape($c.key)) -or ($_ -match 'worker' -and $dev -ne '' -and $_ -match "--device $dev(\s|$)") -or ($_ -match 'WORKER FAULT|worker error|no status line') } | Where-Object { $_ -notmatch 'RESULT |job run-ia' } | Select-Object -Last 15)
|
||||
if ($hits.Count) { "RESULT minerlog card=`"$name`" file=$($f.FullName)"; $hits | ForEach-Object { "RESULT minerlog $_" } }
|
||||
}
|
||||
} catch { "RESULT minerlog error=$($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
"RESULT end $(Stamp)"
|
||||
'SUMMARY ' + ([ordered]@{ job = 'pc1-cards-watch'; status = 'done'; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp); node_synced = $synced; rates = $rates } | ConvertTo-Json -Compress -Depth 3)
|
||||
exit 0
|
||||
53
tools/intel-arc/pc1-export-ahead.ps1
Normal file
53
tools/intel-arc/pc1-export-ahead.ps1
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Igneum run job: PC 1 (machine ae432dc7), 7 October 2026, "fix pc1" (the project lead, 12:1x UK; branch intel-arc). Published with
|
||||
# the RUNNER's --stop-miners AND --cards-off <the Arc's key>: the runner switches the Arc off through the app's own card
|
||||
# path and stops the 5090's and the 9070 XT's miners before this script, and on exit restarts the miners and restores
|
||||
# the Arc. This script, with the miners stopped: (1) prints the packs\devnet folder as it stands (files, sizes, mtimes,
|
||||
# the seeds.txt epoch line) and the app's own export-pack lines from its log with their timings; (2) runs the app's
|
||||
# own export once, `igneum-miner export-pack grpc://127.0.0.1:26610 <app>\packs\devnet` (the exact command
|
||||
# engine.rs export_pack runs, under its 120-s cap; nothing else of the app is touched: no settings.json, no manifest,
|
||||
# no api/* request other than one GET of api/state), timed, so the workers the runner restarts find the hour's pack
|
||||
# ready and can report STATUS inside the 90-s watchdog; (3) prints the folder again and the miner logs' last lines
|
||||
# (the 5090's, the 9070 XT's, the Arc's) so what each miner waited on is on record. Read-back: `node tools/jobs.mjs <id>`.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
$started = Get-Date
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$logDir = $env:IGNEUM_LOG_DIR; if (-not $logDir) { $logDir = Join-Path $appDir 'logs' }
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$miner = if ($inst) { Join-Path $inst 'igneum-miner.exe' } else { $null }
|
||||
$pack = Join-Path $appDir 'packs\devnet'
|
||||
$rpcPort = 26610; if ($env:IGNEUM_APP_RPC_PORT) { try { $rpcPort = [int]$env:IGNEUM_APP_RPC_PORT } catch { } }
|
||||
"RESULT start $(Stamp) job=pc1-export-ahead machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION miner=$miner pack=$pack"
|
||||
function Folder([string] $tag) {
|
||||
if (-not (Test-Path $pack)) { "RESULT packs_$tag absent"; return }
|
||||
foreach ($f in @(Get-ChildItem -LiteralPath $pack -File -ErrorAction SilentlyContinue | Sort-Object Name)) { "RESULT packs_$tag file=$($f.Name) bytes=$($f.Length) mtime=$($f.LastWriteTimeUtc.ToString('yyyy-MM-ddTHH:mm:ssZ'))" }
|
||||
$seeds = Join-Path $pack 'seeds.txt'
|
||||
if (Test-Path $seeds) { Get-Content -LiteralPath $seeds -ErrorAction SilentlyContinue | Select-Object -First 4 | ForEach-Object { "RESULT packs_$tag seeds: $_" } }
|
||||
}
|
||||
function Workers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-miner.exe' OR Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId)" }) }
|
||||
"RESULT processes_before $((Workers) -join ' ')"
|
||||
Folder 'before'
|
||||
# the app's own export lines and their spacing, from the newest app log
|
||||
try {
|
||||
$app = Get-ChildItem -Path $logDir -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($app) { "RESULT applog file=$($app.FullName)"; Get-Content -LiteralPath $app.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'export-pack|exporting|prepare|loading the program|no status line|worker exited|self-test|ready' -and $_ -notmatch 'RESULT |job run-ia|job collect-ia' } | Select-Object -Last 40 | ForEach-Object { "RESULT applog $_" } }
|
||||
} catch { "RESULT applog error=$($_.Exception.Message)" }
|
||||
# the miners' own last lines
|
||||
foreach ($pat in @('miner-nvidia-ae432dc7-*.log', 'miner-amd-ae432dc7-*.log', 'miner-other-ae432dc7-*.log')) {
|
||||
$f = Get-ChildItem -Path $logDir -Filter $pat -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($f) { "RESULT minerlog file=$($f.Name) bytes=$($f.Length)"; Get-Content -LiteralPath $f.FullName -ErrorAction SilentlyContinue | Select-Object -Last 14 | ForEach-Object { "RESULT minerlog $($f.Name -replace '-20261007.*','') $_" } } else { "RESULT minerlog none for $pat" }
|
||||
}
|
||||
# the export, once, with the miners stopped
|
||||
$t0 = Get-Date
|
||||
if ($miner -and (Test-Path $miner)) {
|
||||
"RESULT export start $(Stamp) cmd=`"$miner export-pack grpc://127.0.0.1:$rpcPort $pack`""
|
||||
$out = @(& $miner export-pack "grpc://127.0.0.1:$rpcPort" $pack 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$out | Select-Object -Last 8 | ForEach-Object { "RESULT export $_" }
|
||||
"RESULT export end $(Stamp) exit=$rc wall_s=$([math]::Round(((Get-Date) - $t0).TotalSeconds, 1)) seeds_present=$(Test-Path (Join-Path $pack 'seeds.txt'))"
|
||||
} else { "RESULT export error=no igneum-miner.exe under $inst" }
|
||||
Folder 'after'
|
||||
"RESULT processes_after $((Workers) -join ' ')"
|
||||
"RESULT end $(Stamp): the runner now restarts the miners (and restores the Arc's row unless the project lead switched it off in the app, the persisted path)"
|
||||
'SUMMARY ' + ([ordered]@{ job = 'pc1-export-ahead'; status = 'done'; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp); export_wall_s = [math]::Round(((Get-Date) - $t0).TotalSeconds, 1) } | ConvertTo-Json -Compress)
|
||||
exit 0
|
||||
45
tools/intel-arc/pc1-identities-read.ps1
Normal file
45
tools/intel-arc/pc1-identities-read.ps1
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Igneum run job, PC 1 (machine ae432dc7), 7 October 2026, 11:5x UK: two READ-ONLY questions from main after the
|
||||
# identities-2 change. (1) The console's "nvidia-2" and "amd-4" rows read stale and "status never": which miner
|
||||
# processes exist (command lines carry the label), what each miner log says about its identities ("identity N" lines),
|
||||
# and whether any -2 or -4 miner log was written since the restart. (2) The 5090's "submit error" lines verbatim with
|
||||
# their times, and whether they stopped. No api/* request other than one GET of api/state; nothing written.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$logDir = $env:IGNEUM_LOG_DIR; if (-not $logDir) { $logDir = Join-Path $appDir 'logs' }
|
||||
"RESULT start $(Stamp) job=pc1-identities-read machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION boot=$((Get-CimInstance Win32_OperatingSystem).LastBootUpTime.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))"
|
||||
# the processes: every miner and worker with its command line (the label is the miner's 5th argument)
|
||||
foreach ($p in @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-miner.exe' OR Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue)) {
|
||||
"RESULT process name=$($p.Name) pid=$($p.ProcessId) started=$($p.CreationDate.ToUniversalTime().ToString('HH:mm:ssZ')) cmd=`"$(($p.CommandLine -replace '\s+', ' ') -replace 'C:\\Users\\Admin\\AppData\\Local\\Programs\\Igneum Miner\\', '')`""
|
||||
}
|
||||
# the app's rows
|
||||
try {
|
||||
$url = (Get-Content -LiteralPath (Join-Path $appDir 'app.url') -Raw).Trim()
|
||||
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
|
||||
$cards = @($st.mining.cards); if (-not $cards) { $cards = @($st.cards) }
|
||||
foreach ($c in $cards) { "RESULT app_card key=$($c.key) enabled=$($c.enabled) identities=$($c.identities) ids=[$($c.ids -join ' ')] state=$($c.state) pid=$($c.pid) hash_now=$($c.hash_now) accepted=$($c.accepted) rejected=$($c.rejected) message=`"$($c.message)`"" }
|
||||
} catch { "RESULT app_card error=$($_.Exception.Message)" }
|
||||
# every miner log of today, newest first: label, size, last write, its identity lines, its last STATUS line
|
||||
$boot = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
|
||||
foreach ($f in @(Get-ChildItem -Path $logDir -Filter 'miner-*-ae432dc7-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 12)) {
|
||||
$since = if ($f.LastWriteTime -gt $boot) { 'since-boot' } else { 'before-boot' }
|
||||
"RESULT minerlog file=$($f.Name) bytes=$($f.Length) last_write=$($f.LastWriteTimeUtc.ToString('HH:mm:ssZ')) $since"
|
||||
$lines = @(Get-Content -LiteralPath $f.FullName -ErrorAction SilentlyContinue)
|
||||
$lines | Where-Object { $_ -match 'identity \d+' } | Select-Object -First 8 | ForEach-Object { "RESULT minerlog $($f.Name -replace '-20261007.*','') $_" }
|
||||
$lines | Where-Object { $_ -match '^\S+ STATUS|hash=' } | Select-Object -Last 1 | ForEach-Object { "RESULT minerlog $($f.Name -replace '-20261007.*','') last-status: $_" }
|
||||
}
|
||||
# the submit errors, verbatim, on the 5090's miner log(s)
|
||||
foreach ($f in @(Get-ChildItem -Path $logDir -Filter 'miner-nvidia-ae432dc7-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 2)) {
|
||||
$hits = @(Get-Content -LiteralPath $f.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'submit error|rejected|stale|submit' })
|
||||
"RESULT submit file=$($f.Name) lines=$($hits.Count)"
|
||||
$hits | Select-Object -First 6 | ForEach-Object { "RESULT submit first $_" }
|
||||
$hits | Select-Object -Last 8 | ForEach-Object { "RESULT submit last $_" }
|
||||
}
|
||||
# the app log's lines on the second identities and on submits
|
||||
try {
|
||||
$app = Get-ChildItem -Path $logDir -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($app) { Get-Content -LiteralPath $app.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'identit|submit|reject|miner .* started' -and $_ -notmatch 'RESULT |job run-ia' } | Select-Object -Last 20 | ForEach-Object { "RESULT applog $_" } }
|
||||
} catch { "RESULT applog error=$($_.Exception.Message)" }
|
||||
"RESULT end $(Stamp)"
|
||||
'SUMMARY {"job":"pc1-identities-read","status":"done","finished_at":"' + (Stamp) + '"}'
|
||||
exit 0
|
||||
136
tools/intel-arc/pc1-intel-driver.ps1
Normal file
136
tools/intel-arc/pc1-intel-driver.ps1
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# Igneum run job: the Intel Arc graphics driver on PC 1 (machine ae432dc7), 7 October 2026 (branch intel-arc,
|
||||
# docs/plans/intel-arc.md). the project lead's word: "can you install drivers to pc1"; the B580 goes into PC 1 today.
|
||||
# Published as a signed `run` job with --elevated and WITHOUT --stop-miners: the RUNNER raises the one elevation prompt
|
||||
# (Start-Process -Verb RunAs) at the script's start, the project lead clicks it at the desk, and everything below runs under it:
|
||||
# the download, the hash check and the installer, with no second prompt. The installed app, its node and its miners
|
||||
# are never quit, paused, resumed or switched (no api/* request in this script); the Intel installer touches only
|
||||
# the Intel display device, never the NVIDIA or AMD driver, and runs with -s (silent) and no -b, so it never reboots
|
||||
# on its own; exit 14 = "reboot required", reported and left to the project lead. Lines start with RESULT so
|
||||
# `node tools/jobs.mjs <job id>` shows them; a SUMMARY {json} line ends the job.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-intel-driver'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
# Intel's download page 785597 (read 7 October 2026): version 32.0.101.9034, released 2 October 2026 (non-WHQL; 9033
|
||||
# is the WHQL one), file gfx_win_101.9034.exe, 889.4 MB, mirror folder 929959; the SHA512 is the string the page prints.
|
||||
$version = '32.0.101.9034'
|
||||
$url = 'https://downloadmirror.intel.com/929959/gfx_win_101.9034.exe'
|
||||
$expectedBytes = 932631144
|
||||
$expectedSha512 = '3fc6f9b4c8ef40bf066f1ad77f3c6aea55d47bae6dc8ab9575a7a9fe8eee84bf0c79333f81825cd7538ea1bb7ca78b6ddb0b75f96b321b4dcf524dec843cf3f0'
|
||||
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-intel-driver' }
|
||||
New-Item -ItemType Directory -Force -Path $job | Out-Null
|
||||
$exe = Join-Path $job 'gfx_win_101.9034.exe'
|
||||
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$elevated = (New-Object Security.Principal.WindowsPrincipal($id)).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION user=$($id.Name) elevated=$elevated version_wanted=$version"
|
||||
if (-not $elevated) { "RESULT error not elevated: the installer needs administrator rights (publish with --elevated)"; Summary 'failed' @{ error = 'not elevated' }; exit 2 }
|
||||
|
||||
# ---- what Windows sees before: every display adapter, the Intel ones by PnP, the OpenCL platforms the worker lists ----
|
||||
function Adapters([string] $tag) {
|
||||
try {
|
||||
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) {
|
||||
"RESULT adapter_$tag name=`"$($a.Name)`" driver=$($a.DriverVersion) date=$($a.DriverDate) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID) ram32=$($a.AdapterRAM)"
|
||||
}
|
||||
} catch { "RESULT adapter_$tag error=$($_.Exception.Message)" }
|
||||
# the 32-bit AdapterRAM field caps at 4 GB: the true size is the class key's qwMemorySize
|
||||
try {
|
||||
foreach ($k in @(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' })) {
|
||||
$p = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
|
||||
if ($p -and $p.DriverDesc) { $mem = ''; if ($null -ne $p.'HardwareInformation.qwMemorySize') { $mem = [string]([math]::Round([double]$p.'HardwareInformation.qwMemorySize' / 1MB)) + ' MiB' }; "RESULT class_$tag key=$($k.PSChildName) desc=`"$($p.DriverDesc)`" driver=$($p.DriverVersion) provider=`"$($p.ProviderName)`" memory=$mem" }
|
||||
}
|
||||
} catch { "RESULT class_$tag error=$($_.Exception.Message)" }
|
||||
try {
|
||||
foreach ($d in @(Get-PnpDevice -Class Display -ErrorAction Stop)) { "RESULT pnp_$tag name=`"$($d.FriendlyName)`" status=$($d.Status) present=$($d.Present) id=$($d.InstanceId)" }
|
||||
} catch { "RESULT pnp_$tag error=$($_.Exception.Message)" }
|
||||
}
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$worker = $null
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $worker = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
"RESULT app install=$inst worker=$worker"
|
||||
function OpenCLList([string] $tag) {
|
||||
if (-not $worker) { "RESULT opencl_$tag error=no installed igneum-worker-opencl.exe to list with"; return @() }
|
||||
$lines = @(& $worker --list 2>&1 | ForEach-Object { "$_" })
|
||||
foreach ($l in $lines) { "RESULT opencl_$tag $l" }
|
||||
return $lines
|
||||
}
|
||||
Adapters 'before'
|
||||
$clBefore = OpenCLList 'before'
|
||||
$intelBefore = @($clBefore | Where-Object { $_ -match 'Intel' }).Count
|
||||
"RESULT opencl_before intel_lines=$intelBefore"
|
||||
|
||||
# ---- the download: curl.exe (shipped with Windows 10 1803 and later), the size and the SHA512 Intel publishes ----
|
||||
$dl = $null
|
||||
if (Test-Path -LiteralPath $exe) {
|
||||
$len = (Get-Item -LiteralPath $exe).Length
|
||||
if ($len -eq $expectedBytes) { "RESULT download reuse $exe bytes=$len (already here)"; $dl = $true } else { Remove-Item -LiteralPath $exe -Force -ErrorAction SilentlyContinue }
|
||||
}
|
||||
if (-not $dl) {
|
||||
$curl = Join-Path $env:SystemRoot 'System32\curl.exe'
|
||||
if (-not (Test-Path -LiteralPath $curl)) { "RESULT error no curl.exe at $curl"; Summary 'failed' @{ error = 'no curl' }; exit 2 }
|
||||
$t0 = Get-Date
|
||||
"RESULT download start $(Stamp) url=$url expected_bytes=$expectedBytes"
|
||||
$attempt = 0
|
||||
while ($attempt -lt 3) {
|
||||
$attempt++
|
||||
$out = @(& $curl -sS -L --retry 3 --retry-delay 5 --connect-timeout 30 --max-time 1500 -o $exe $url 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$len = 0; if (Test-Path -LiteralPath $exe) { $len = (Get-Item -LiteralPath $exe).Length }
|
||||
"RESULT download attempt=$attempt curl_exit=$rc bytes=$len wall_s=$([int]((Get-Date) - $t0).TotalSeconds) $($out -join ' ')"
|
||||
if ($rc -eq 0 -and $len -eq $expectedBytes) { $dl = $true; break }
|
||||
Start-Sleep -Seconds 10
|
||||
}
|
||||
if (-not $dl) { "RESULT error download did not reach $expectedBytes bytes in 3 attempts"; Summary 'failed' @{ error = 'download' }; exit 2 }
|
||||
}
|
||||
$sha = (Get-FileHash -Algorithm SHA512 -LiteralPath $exe).Hash.ToLower()
|
||||
$sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $exe).Hash.ToLower()
|
||||
"RESULT file $exe bytes=$((Get-Item -LiteralPath $exe).Length) sha512=$sha sha256=$sha256"
|
||||
if ($sha -ne $expectedSha512) { "RESULT error sha512 mismatch: Intel publishes $expectedSha512; the installer is NOT run"; Summary 'failed' @{ error = 'sha512 mismatch'; sha512 = $sha }; exit 2 }
|
||||
"RESULT sha512 ok (equal to the string on Intel's page 785597)"
|
||||
try { $sig = Get-AuthenticodeSignature -LiteralPath $exe; "RESULT authenticode status=$($sig.Status) signer=`"$($sig.SignerCertificate.Subject)`"" } catch { "RESULT authenticode error=$($_.Exception.Message)" }
|
||||
|
||||
# ---- the installer: -s silent, no -b (no reboot by the installer); its exit code says what happened ----
|
||||
# Intel's codes (support article 000098195, read 7 October 2026): 0 success, 1 failed, 2 the installer rebooted the
|
||||
# system itself (-b only), 8 no driver for this device, 13 reboot required BEFORE setup, 14 reboot required after.
|
||||
$meaning = @{ 0 = 'installed, no reboot asked'; 1 = 'installer failed (general)'; 2 = 'the installer rebooted the system'; 5 = 'the OS or a device on this platform is not supported by the driver (no supported Intel GPU present?)'; 6 = 'the installer was closed before it finished'; 7 = 'unsupported command-line parameter'; 8 = 'no INF files found in the bundle'; 9 = 'driver files without an Intel signature'; 11 = 'no free space'; 13 = 'a reboot is required BEFORE this driver can install'; 14 = 'installed; a reboot is required to apply it (NOT taken: the project lead decides)'; 1007 = 'the bootstrapper wrapped an inner Installer.exe exit 7 (unsupported parameter)'; 1014 = 'installed; a reboot is required to apply it (the bootstrapper wrapping the inner 14; NOT taken: the project lead decides)' }
|
||||
# Run 1 (09:27Z): `-s -report <path>` made the inner Installer.exe exit 7 ("unsupported parameter") and the bootstrapper
|
||||
# answer 1007 before any device check (its log: C:\ProgramData\Intel\GFXInstaller\Bootstrapper\IntelGFX_Bootstrapper_*.log).
|
||||
# So: -s alone. The bootstrapper also installs the .NET Desktop Runtime it needs (it did on run 1, exit 0).
|
||||
$t1 = Get-Date
|
||||
"RESULT install start $(Stamp) args=-s (silent, no -b); the screen may blink while the Intel device resets; NVIDIA and AMD drivers are not touched"
|
||||
$p = $null
|
||||
try { $p = Start-Process -FilePath $exe -ArgumentList @('-s') -Wait -PassThru -WindowStyle Hidden -ErrorAction Stop } catch { "RESULT error installer launch: $($_.Exception.Message)"; Summary 'failed' @{ error = 'launch' }; exit 2 }
|
||||
$code = $p.ExitCode
|
||||
$word = if ($meaning.ContainsKey([int]$code)) { $meaning[[int]$code] } else { 'unknown code' }
|
||||
"RESULT install end $(Stamp) exit=$code meaning=`"$word`" wall_s=$([int]((Get-Date) - $t1).TotalSeconds)"
|
||||
# the installer's own logs: the bootstrapper's under GFXInstaller\Bootstrapper, the inner installer's beside it (read 7 October 2026)
|
||||
$logRoot = Join-Path $env:ProgramData 'Intel\GFXInstaller'
|
||||
if (Test-Path -LiteralPath $logRoot) {
|
||||
foreach ($lf in @(Get-ChildItem -LiteralPath $logRoot -Recurse -Filter '*.log' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt $t1.AddMinutes(-1) } | Sort-Object LastWriteTime)) {
|
||||
"RESULT log $($lf.FullName) bytes=$($lf.Length)"
|
||||
Get-Content -LiteralPath $lf.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'exit|error|fail|device|not supported|reboot|restart|install' } | Select-Object -Last 25 | ForEach-Object { "RESULT log $_" }
|
||||
}
|
||||
} else { "RESULT log none under $logRoot" }
|
||||
|
||||
# ---- what Windows sees after: the Intel device by name and version, its memory, the OpenCL platform ----
|
||||
Start-Sleep -Seconds 10
|
||||
Adapters 'after'
|
||||
$clAfter = OpenCLList 'after'
|
||||
$intelAfter = @($clAfter | Where-Object { $_ -match 'Intel' })
|
||||
"RESULT opencl_after intel_lines=$($intelAfter.Count)"
|
||||
$found = @()
|
||||
try { $found = @(Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'Intel' } | ForEach-Object { "$($_.Name) driver $($_.DriverVersion) status $($_.Status) code $($_.ConfigManagerErrorCode)" }) } catch { }
|
||||
foreach ($f in $found) { "RESULT intel_device $f" }
|
||||
$versionOk = @($found | Where-Object { $_ -match [regex]::Escape($version) }).Count -gt 0
|
||||
$clinfo = Get-Command clinfo -ErrorAction SilentlyContinue
|
||||
if ($clinfo) { & $clinfo.Source -l 2>&1 | ForEach-Object { "RESULT clinfo $_" } } else { "RESULT clinfo not installed (the worker's --list above is the enumeration)" }
|
||||
$reboot = ($code -eq 14 -or $code -eq 13 -or $code -eq 1014)
|
||||
$status = if ($code -eq 0 -or $code -eq 14 -or $code -eq 1014) { 'done' } else { 'failed' }
|
||||
"RESULT end $(Stamp) version_installed=$versionOk reboot_required=$reboot intel_opencl_platform=$($intelAfter.Count -gt 0)"
|
||||
Summary $status @{ version = $version; installer_exit = $code; meaning = $word; reboot_required = $reboot; sha512 = $sha; sha256 = $sha256; intel_devices = $found; intel_opencl_lines = $intelAfter.Count; opencl_intel_before = $intelBefore }
|
||||
if ($status -eq 'failed') { exit 1 }
|
||||
exit 0
|
||||
58
tools/intel-arc/pc1-intel-readback.ps1
Normal file
58
tools/intel-arc/pc1-intel-readback.ps1
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Igneum run job: read back PC 1 (machine ae432dc7) after the restart that applied the Intel Arc driver 32.0.101.9034
|
||||
# (7 October 2026, branch intel-arc). Read only, NOT elevated, beside the miners: no api/* request other than one GET
|
||||
# of api/state for the card row; never quits, pauses, resumes or switches anything. Lines: the Intel adapter row (driver
|
||||
# version, status, Code), the PnP display devices, the installed worker's --list (the "Intel(R) OpenCL Graphics" platform
|
||||
# line with the device's global MiB), clinfo when present, and the app's own card row for the Arc after hotplug.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
$want = '32.0.101.9034'
|
||||
"RESULT start $(Stamp) job=pc1-intel-readback machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION uptime_s=$([int]((Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime).TotalSeconds)"
|
||||
$found = @()
|
||||
try {
|
||||
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) {
|
||||
"RESULT adapter name=`"$($a.Name)`" driver=$($a.DriverVersion) date=$($a.DriverDate) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID)"
|
||||
if ($a.Name -match 'Intel') { $found += "$($a.Name) driver $($a.DriverVersion) status $($a.Status) code $($a.ConfigManagerErrorCode)" }
|
||||
}
|
||||
} catch { "RESULT adapter error=$($_.Exception.Message)" }
|
||||
try { foreach ($d in @(Get-PnpDevice -Class Display -ErrorAction Stop)) { "RESULT pnp name=`"$($d.FriendlyName)`" status=$($d.Status) present=$($d.Present) id=$($d.InstanceId)" } } catch { "RESULT pnp error=$($_.Exception.Message)" }
|
||||
try {
|
||||
foreach ($k in @(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' })) {
|
||||
$p = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
|
||||
if ($p -and $p.DriverDesc -match 'Intel') { $mem = '-'; if ($null -ne $p.'HardwareInformation.qwMemorySize') { $mem = [string]([math]::Round([double]$p.'HardwareInformation.qwMemorySize' / 1MB)) + ' MiB' }; "RESULT class key=$($k.PSChildName) desc=`"$($p.DriverDesc)`" driver=$($p.DriverVersion) memory=$mem" }
|
||||
}
|
||||
} catch { "RESULT class not_readable=$($_.Exception.Message)" }
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$worker = $null
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $worker = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
$intelLines = @()
|
||||
if ($worker) {
|
||||
$list = @(& $worker --list 2>&1 | ForEach-Object { "$_" })
|
||||
foreach ($l in $list) { "RESULT list $l" }
|
||||
$intelLines = @($list | Where-Object { $_ -match 'Intel' })
|
||||
} else { "RESULT list error=no installed igneum-worker-opencl.exe" }
|
||||
"RESULT opencl intel_lines=$($intelLines.Count)"
|
||||
$clinfo = Get-Command clinfo -ErrorAction SilentlyContinue
|
||||
if ($clinfo) { & $clinfo.Source -l 2>&1 | ForEach-Object { "RESULT clinfo $_" } }
|
||||
# the app's own row for the Arc (read only)
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$row = ''
|
||||
try {
|
||||
$urlFile = Join-Path $appDir 'app.url'
|
||||
if (Test-Path $urlFile) {
|
||||
$url = (Get-Content -LiteralPath $urlFile -Raw).Trim()
|
||||
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
|
||||
$all = $st.mining.cards; if (-not $all) { $all = $st.cards }
|
||||
foreach ($c in @($all)) { $line = "key=$($c.key) name=`"$($c.name)`" vendor=$($c.vendor) kind=$($c.kind) worker=$($c.worker) enabled=$($c.enabled) identities=$($c.identities) state=$($c.state) pid=$($c.pid) hash_now=$($c.hash_now) vram_mb=$($c.vram_mb) problem=`"$($c.problem)`" message=`"$($c.message)`""; "RESULT app_card $line"; if ($c.name -match 'Intel') { $row = $line } }
|
||||
} else { "RESULT app_card error=no app.url (the app is not up yet)" }
|
||||
} catch { "RESULT app_card error=$($_.Exception.Message)" }
|
||||
# first light, read only: the app's own log lines for the Intel worker (ready, exchange, sub-group, or its error)
|
||||
try {
|
||||
$logDir = $env:IGNEUM_LOG_DIR; if (-not $logDir) { $logDir = Join-Path $appDir 'logs' }
|
||||
$newest = Get-ChildItem -Path $logDir -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($newest) { "RESULT firstlight log=$($newest.FullName)"; Get-Content -LiteralPath $newest.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'worker|exchange|ready|prepare|pack|miner' -and $_ -notmatch 'job run-ia|job collect-ia|RESULT ' } | Select-Object -Last 40 | ForEach-Object { "RESULT firstlight $_" } } else { "RESULT firstlight error=no app-*.log under $logDir" }
|
||||
} catch { "RESULT firstlight error=$($_.Exception.Message)" }
|
||||
foreach ($f in $found) { "RESULT intel_device $f" }
|
||||
$versionOk = @($found | Where-Object { $_ -match [regex]::Escape($want) -and $_ -match 'code 0' }).Count -gt 0
|
||||
"RESULT end $(Stamp) version_ok=$versionOk intel_opencl_platform=$($intelLines.Count -gt 0) app_row=$(if ($row) { 'yes' } else { 'no' })"
|
||||
'SUMMARY ' + ([ordered]@{ job = 'pc1-intel-readback'; status = $(if ($versionOk -and $intelLines.Count -gt 0) { 'done' } else { 'partial' }); finished_at = (Stamp); version_ok = $versionOk; intel_opencl_lines = $intelLines.Count; intel_devices = $found; app_row = $row } | ConvertTo-Json -Compress -Depth 3)
|
||||
exit 0
|
||||
48
tools/intel-arc/pc1-node-wait.ps1
Normal file
48
tools/intel-arc/pc1-node-wait.ps1
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Igneum run job: wait for PC 1's node after a restart (machine ae432dc7), 7 October 2026 (branch intel-arc). READ ONLY,
|
||||
# beside whatever runs: GET api/state every 10 s until node.synced is true, and the node's own eth JSON-RPC
|
||||
# (127.0.0.1:26790, the default RPC port 26610 plus 180, config.rs evm_port) answers igneum_getExecStatus with an
|
||||
# executed tip; cap 10 minutes; the timeline printed as RESULT lines. It never posts anything: the miners are restarted
|
||||
# by the signed `restart --what miners` job the runner executes AFTER this one (the runner is serial), which is the
|
||||
# app's own path for clearing a faulted worker. No api/* request other than GET api/state.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
$started = Get-Date
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$urlFile = Join-Path $appDir 'app.url'
|
||||
"RESULT start $(Stamp) job=pc1-node-wait machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION uptime_s=$([int]((Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime).TotalSeconds)"
|
||||
$evmPort = 26790
|
||||
if ($env:IGNEUM_APP_RPC_PORT) { try { $evmPort = [int]$env:IGNEUM_APP_RPC_PORT + 180 } catch { } }
|
||||
function ExecTip {
|
||||
try {
|
||||
$body = '{"jsonrpc":"2.0","id":1,"method":"igneum_getExecStatus","params":[]}'
|
||||
$r = Invoke-RestMethod -Uri "http://127.0.0.1:$evmPort" -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 5
|
||||
if ($r.result) { return "$($r.result.executedTipHash)|$($r.result.executedTipNumber)|$($r.result.startedFrom)" }
|
||||
if ($r.error) { return "error:$($r.error.message)" }
|
||||
return 'no result'
|
||||
} catch { return "unreachable:$($_.Exception.Message)" }
|
||||
}
|
||||
$synced = $false; $tip = ''; $t = 0; $last = ''
|
||||
while ($t -le 600) {
|
||||
$line = ''
|
||||
try {
|
||||
if (Test-Path $urlFile) {
|
||||
$url = (Get-Content -LiteralPath $urlFile -Raw).Trim()
|
||||
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
|
||||
$n = $st.node
|
||||
$cards = @($st.mining.cards); if (-not $cards) { $cards = @($st.cards) }
|
||||
$cs = ($cards | Where-Object { $_.kind -ne 'integrated' } | ForEach-Object { "$($_.name -replace 'NVIDIA GeForce |AMD Radeon |Intel\(R\) Arc\(TM\) ', ''):$($_.state)" }) -join ' '
|
||||
$line = "node=$($n.state) synced=$($n.synced) blocks=$($n.blocks) headers=$($n.headers) daa=$($n.daa) peers=$($n.peers) cards=[$cs]"
|
||||
$synced = [bool]$n.synced
|
||||
} else { $line = 'app.url absent (the app is not up yet)' }
|
||||
} catch { $line = "api/state: $($_.Exception.Message)" }
|
||||
$e = ExecTip
|
||||
$full = "$line exec=$e"
|
||||
if ($full -ne $last) { "RESULT t+${t}s $(Stamp) $full"; $last = $full }
|
||||
$tip = $e
|
||||
if ($synced -and $e -notmatch '^(unreachable|error|no result)' -and $e -notmatch '^\|' ) { break }
|
||||
Start-Sleep -Seconds 10; $t += 10
|
||||
}
|
||||
$ok = $synced -and $tip -notmatch '^(unreachable|error|no result)' -and $tip -notmatch '^\|'
|
||||
"RESULT end $(Stamp) waited_s=$t synced=$synced exec=$tip ready=$ok"
|
||||
'SUMMARY ' + ([ordered]@{ job = 'pc1-node-wait'; status = $(if ($ok) { 'done' } else { 'partial' }); duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp); synced = $synced; exec = $tip; waited_s = $t } | ConvertTo-Json -Compress)
|
||||
exit 0
|
||||
21
tools/intel-arc/pc1-reboot.ps1
Normal file
21
tools/intel-arc/pc1-reboot.ps1
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Igneum run job: restart PC 1 (machine ae432dc7) to apply the Intel Arc driver 32.0.101.9034 (7 October 2026, branch
|
||||
# intel-arc). the project lead's word, relayed by main at 14:5x UK: "if the installer reports reboot required (exit 14) you have his
|
||||
# word to reboot PC 1". Run b of the driver job exited 1014 (the bootstrapper's wrap of the inner installer's 14).
|
||||
# A plain `run` job, NOT elevated: shutdown.exe needs only SeShutdownPrivilege, which the signed-in user holds, so no
|
||||
# prompt. Windows closes the installed app with the session (it is never quit, paused or resumed by this script: no
|
||||
# api/* request here) and the app's autostart brings it back after the restart. The read-back job follows.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
"RESULT start $(Stamp) job=pc1-reboot machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
try {
|
||||
$arc = @(Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'Intel' } | ForEach-Object { "$($_.Name) driver $($_.DriverVersion) status $($_.Status) code $($_.ConfigManagerErrorCode)" })
|
||||
foreach ($a in $arc) { "RESULT before $a" }
|
||||
} catch { "RESULT before error=$($_.Exception.Message)" }
|
||||
$pending = Test-Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
|
||||
try { $p = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -Name PendingFileRenameOperations -ErrorAction SilentlyContinue; "RESULT pending_file_renames=$([bool]($p -and $p.PendingFileRenameOperations))" } catch { }
|
||||
"RESULT restart requested $(Stamp): shutdown /r /t 20 (the app closes with the session and autostarts after)"
|
||||
& "$env:SystemRoot\System32\shutdown.exe" /r /t 20 /c "Igneum: restart to apply the Intel Arc driver 32.0.101.9034 (the project lead's word, 7 October 2026)" 2>&1 | ForEach-Object { "RESULT shutdown $_" }
|
||||
"RESULT shutdown exit=$LASTEXITCODE"
|
||||
'SUMMARY ' + ([ordered]@{ job = 'pc1-reboot'; status = $(if ($LASTEXITCODE -eq 0) { 'done' } else { 'failed' }); finished_at = (Stamp); shutdown_exit = $LASTEXITCODE } | ConvertTo-Json -Compress)
|
||||
if ($LASTEXITCODE -ne 0) { exit 1 }
|
||||
exit 0
|
||||
54
tools/intel-arc/pc2-freeze-events.ps1
Normal file
54
tools/intel-arc/pc2-freeze-events.ps1
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Igneum run job: why PC 2 (machine 1ccfe586) went silent at 16:27:14Z on 7 October 2026 during the Intel Arc driver
|
||||
# install (run-ia-pc2-intel-driver-20261007-c), and why 32.0.101.9034 did not bind. Read only, NOT elevated, beside the
|
||||
# miners: no api/* request at all; never quits, pauses, resumes or switches anything. Lines: the System and Application
|
||||
# event logs 16:20Z to 17:40Z (power, shutdown and restart, display and driver events, the Intel installer's own events),
|
||||
# the Intel installer's logs under ProgramData, the run c job's local log (every RESULT line the app could not upload),
|
||||
# pnputil's view of the Intel display driver packages (staged 9034 or not), LastBootUpTime and the Arc row again.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
$from = [datetime]::Parse('2026-10-07T16:20:00Z').ToUniversalTime(); $to = [datetime]::Parse('2026-10-07T17:40:00Z').ToUniversalTime()
|
||||
$os = Get-CimInstance Win32_OperatingSystem
|
||||
"RESULT start $(Stamp) job=pc2-freeze-events machine=$env:COMPUTERNAME last_boot=$($os.LastBootUpTime.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')) uptime_s=$([int]((Get-Date) - $os.LastBootUpTime).TotalSeconds)"
|
||||
foreach ($log in @('System', 'Application')) {
|
||||
try {
|
||||
$ev = @(Get-WinEvent -FilterHashtable @{ LogName = $log; StartTime = $from.ToLocalTime(); EndTime = $to.ToLocalTime() } -ErrorAction Stop | Sort-Object TimeCreated)
|
||||
"RESULT events log=$log count=$($ev.Count) window=$($from.ToString('HH:mm'))Z..$($to.ToString('HH:mm'))Z"
|
||||
foreach ($e in $ev) {
|
||||
$keep = ($e.Id -in @(41, 1074, 1076, 6005, 6006, 6008, 6013, 1001, 4101, 219, 7000, 7001, 7026, 7031, 7034, 7045, 20001, 20003, 10010, 10016, 1000, 1002, 1033, 1034, 11707, 11708, 11724, 1040, 1042, 1035, 12, 13)) -or ($e.ProviderName -match 'Kernel-Power|Kernel-General|EventLog|BugCheck|Display|igfx|Intel|Thunderbolt|USB4|PnP|UserPnp|MsiInstaller|Windows Error Reporting|Application Error|Application Hang|igneum|WHEA|Kernel-PnP|Kernel-Boot')
|
||||
if (-not $keep) { continue }
|
||||
$msg = ($e.Message -replace '\s+', ' '); if ($msg.Length -gt 260) { $msg = $msg.Substring(0, 260) }
|
||||
"RESULT ev $($e.TimeCreated.ToUniversalTime().ToString('HH:mm:ss'))Z log=$log id=$($e.Id) level=$($e.LevelDisplayName) provider=`"$($e.ProviderName)`" msg=`"$msg`""
|
||||
}
|
||||
} catch { "RESULT events log=$log error=$($_.Exception.Message)" }
|
||||
}
|
||||
# the Intel installer's own logs (the bootstrapper and the inner installer write under ProgramData)
|
||||
foreach ($dir in @('C:\ProgramData\Intel\GFXInstaller', 'C:\ProgramData\Intel\Logs', "$env:ProgramData\Intel\GFXInstaller\Logs")) {
|
||||
if (-not (Test-Path -LiteralPath $dir)) { continue }
|
||||
foreach ($f in @(Get-ChildItem -LiteralPath $dir -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime.ToUniversalTime() -ge $from.AddHours(-2) } | Sort-Object LastWriteTime)) {
|
||||
"RESULT intel_log file=`"$($f.FullName)`" bytes=$($f.Length) written=$($f.LastWriteTime.ToUniversalTime().ToString('HH:mm:ss'))Z"
|
||||
try { Get-Content -LiteralPath $f.FullName -ErrorAction Stop | Where-Object { $_ -match 'exit|error|fail|restart|reboot|result|return|install|version|Code|cancel|abort|complete' } | Select-Object -Last 40 | ForEach-Object { $t = $_ -replace '\s+', ' '; if ($t.Length -gt 240) { $t = $t.Substring(0, 240) }; "RESULT intel_log_line `"$t`"" } } catch { "RESULT intel_log_line error=$($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
# the run c job's local log: every line the runner wrote after the uploads stopped
|
||||
$appDir = $env:IGNEUM_APP_DIR; if (-not $appDir) { $appDir = Join-Path $env:LOCALAPPDATA 'igneum' }
|
||||
try {
|
||||
$hits = @(Get-ChildItem -LiteralPath $appDir -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'run-ia-pc2-intel-driver-20261007-c' -or ($_.Length -lt 4000000 -and $_.LastWriteTime.ToUniversalTime() -ge $from -and $_.LastWriteTime.ToUniversalTime() -le $to -and $_.Extension -in @('.log', '.txt', '.json')) })
|
||||
"RESULT runc_files count=$($hits.Count)"
|
||||
foreach ($f in $hits) {
|
||||
"RESULT runc_file file=`"$($f.FullName)`" bytes=$($f.Length) written=$($f.LastWriteTime.ToUniversalTime().ToString('HH:mm:ss'))Z"
|
||||
if ($f.Name -match 'run-ia-pc2-intel-driver-20261007-c' -or $f.Name -match 'job') {
|
||||
try { Get-Content -LiteralPath $f.FullName -ErrorAction Stop | Where-Object { $_ -match 'RESULT|SUMMARY|exit|restart|error|install' } | Select-Object -Last 30 | ForEach-Object { $t = $_ -replace '\s+', ' '; if ($t.Length -gt 240) { $t = $t.Substring(0, 240) }; "RESULT runc_line `"$t`"" } } catch { "RESULT runc_line error=$($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
} catch { "RESULT runc_files error=$($_.Exception.Message)" }
|
||||
# the driver store: is 9034 staged (pnputil lists every package; the Intel display ones name igfx or iigd)
|
||||
try {
|
||||
$out = & pnputil.exe /enum-drivers 2>&1 | Out-String
|
||||
$blocks = $out -split '(?m)^\s*$' | Where-Object { $_ -match 'Intel' -and $_ -match 'Display' }
|
||||
"RESULT pnputil intel_display_packages=$($blocks.Count)"
|
||||
foreach ($b in $blocks) { $t = ($b -replace '\s+', ' ').Trim(); if ($t.Length -gt 300) { $t = $t.Substring(0, 300) }; "RESULT pnputil_pkg `"$t`"" }
|
||||
} catch { "RESULT pnputil error=$($_.Exception.Message)" }
|
||||
try { foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'Intel' })) { "RESULT intel_device $($a.Name) driver $($a.DriverVersion) date $($a.DriverDate) status $($a.Status) code $($a.ConfigManagerErrorCode)" } } catch { "RESULT intel_device error=$($_.Exception.Message)" }
|
||||
"RESULT end $(Stamp)"
|
||||
'SUMMARY ' + ([ordered]@{ job = 'pc2-freeze-events'; status = 'done'; finished_at = (Stamp) } | ConvertTo-Json -Compress)
|
||||
exit 0
|
||||
168
tools/intel-arc/pc2-intel-driver-b.ps1
Normal file
168
tools/intel-arc/pc2-intel-driver-b.ps1
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
# Igneum run job (shape b: the minidump copy first, then the driver): the Intel Arc graphics driver on PC 2 (machine 1ccfe586), 7 October 2026 (branch intel-arc). the project lead's word:
|
||||
# "PC 2 needs Arc drivers"; the B580 sits in a Razer Core X V2 on PC 2. PC 1's recipe (pc1-intel-driver.ps1) plus one
|
||||
# step: when the installer answers 14 or 1014 (installed, restart required) this job RESTARTS the PC itself (main's word
|
||||
# for PC 2, not the project lead's desk), with a line before and after; the read-back job runs after the boot.
|
||||
# Published as a signed `run` job with --elevated and WITHOUT --stop-miners: the RUNNER raises the one elevation prompt
|
||||
# (Start-Process -Verb RunAs) at the script's start, the project lead clicks it at the desk, and everything below runs under it:
|
||||
# the download, the hash check and the installer, with no second prompt. The installed app, its node and its miners
|
||||
# are never quit, paused, resumed or switched (no api/* request in this script); the Intel installer touches only
|
||||
# the Intel display device, never the NVIDIA or AMD driver, and runs with -s (silent) and no -b, so it never reboots
|
||||
# on its own; exit 14 = "reboot required", reported and left to the project lead. Lines start with RESULT so
|
||||
# `node tools/jobs.mjs <job id>` shows them; a SUMMARY {json} line ends the job.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc2-intel-driver'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
# Intel's download page 785597 (read 7 October 2026): version 32.0.101.9034, released 2 October 2026 (non-WHQL; 9033
|
||||
# is the WHQL one), file gfx_win_101.9034.exe, 889.4 MB, mirror folder 929959; the SHA512 is the string the page prints.
|
||||
$version = '32.0.101.9034'
|
||||
$url = 'https://downloadmirror.intel.com/929959/gfx_win_101.9034.exe'
|
||||
$expectedBytes = 932631144
|
||||
$expectedSha512 = '3fc6f9b4c8ef40bf066f1ad77f3c6aea55d47bae6dc8ab9575a7a9fe8eee84bf0c79333f81825cd7538ea1bb7ca78b6ddb0b75f96b321b4dcf524dec843cf3f0'
|
||||
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-intel-driver' }
|
||||
New-Item -ItemType Directory -Force -Path $job | Out-Null
|
||||
$exe = Join-Path $job 'gfx_win_101.9034.exe'
|
||||
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$elevated = (New-Object Security.Principal.WindowsPrincipal($id)).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION user=$($id.Name) elevated=$elevated version_wanted=$version"
|
||||
# (B), the shipper's and main's shape of 7 October 2026, 19:1x BST: the 16:27Z crash's kernel minidump rides this one
|
||||
# elevated run (the Minidump folder refuses the unelevated read: run-ia-pc2-minidump-20261007 at 18:14:55Z). It is copied
|
||||
# into the job's own folder as base64 chunks (the intake carries text, 256 KB a file) before the installer starts, so the
|
||||
# chunks survive whatever the install does; the collect job that follows uploads them and the Mac parses the triage dump.
|
||||
try {
|
||||
$mdDir = 'C:\WINDOWS\Minidump'
|
||||
$mdOut = $env:IGNEUM_JOB_DIR; if (-not $mdOut) { $mdOut = Join-Path $env:TEMP 'ia-minidump-20261007' }
|
||||
foreach ($f in @(Get-ChildItem -LiteralPath $mdDir -File -ErrorAction Stop | Sort-Object LastWriteTime)) { "RESULT minidump_dir file=`"$($f.Name)`" bytes=$($f.Length) written=$($f.LastWriteTime.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))" }
|
||||
$mdSrc = Join-Path $mdDir '100726-22234-01.dmp'
|
||||
$mdBytes = [System.IO.File]::ReadAllBytes($mdSrc)
|
||||
$mdSha = (Get-FileHash -LiteralPath $mdSrc -Algorithm SHA256).Hash.ToLower()
|
||||
"RESULT dump file=`"$mdSrc`" bytes=$($mdBytes.Length) sha256=$mdSha"
|
||||
$mdCode = [BitConverter]::ToUInt32($mdBytes, 56); $mdP = @(0..3 | ForEach-Object { [BitConverter]::ToUInt64($mdBytes, 64 + 8 * $_) })
|
||||
"RESULT header signature=`"$([System.Text.Encoding]::ASCII.GetString($mdBytes, 0, 8))`" bugcheck=0x$($mdCode.ToString('x8')) p1=0x$($mdP[0].ToString('x')) p2=0x$($mdP[1].ToString('x')) p3=0x$($mdP[2].ToString('x')) p4=0x$($mdP[3].ToString('x')) dump_type=$([BitConverter]::ToUInt32($mdBytes, 0xf98))"
|
||||
New-Item -ItemType Directory -Force -Path $mdOut | Out-Null
|
||||
$mdChunk = 135000; $mdN = [math]::Ceiling($mdBytes.Length / $mdChunk)
|
||||
for ($mi = 0; $mi -lt $mdN; $mi++) {
|
||||
$mdLen = [math]::Min($mdChunk, $mdBytes.Length - $mi * $mdChunk)
|
||||
$mdB64 = [Convert]::ToBase64String($mdBytes, $mi * $mdChunk, $mdLen, [System.Base64FormattingOptions]::InsertLineBreaks)
|
||||
$mdName = Join-Path $mdOut ('dump-{0:d3}.b64' -f $mi)
|
||||
[System.IO.File]::WriteAllText($mdName, "CHUNK $mi of $mdN bytes=$mdLen`r`n$mdB64`r`nEND $mi`r`n", [System.Text.Encoding]::ASCII)
|
||||
"RESULT chunk i=$mi of=$mdN raw_bytes=$mdLen file_bytes=$((Get-Item -LiteralPath $mdName).Length) sha256=$((Get-FileHash -LiteralPath $mdName -Algorithm SHA256).Hash.ToLower())"
|
||||
}
|
||||
"RESULT minidump chunks=$mdN dir=`"$mdOut`""
|
||||
} catch { "RESULT minidump error=$($_.Exception.Message)" }
|
||||
if (-not $elevated) { "RESULT error not elevated: the installer needs administrator rights (publish with --elevated)"; Summary 'failed' @{ error = 'not elevated' }; exit 2 }
|
||||
|
||||
# ---- what Windows sees before: every display adapter, the Intel ones by PnP, the OpenCL platforms the worker lists ----
|
||||
function Adapters([string] $tag) {
|
||||
try {
|
||||
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) {
|
||||
"RESULT adapter_$tag name=`"$($a.Name)`" driver=$($a.DriverVersion) date=$($a.DriverDate) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID) ram32=$($a.AdapterRAM)"
|
||||
}
|
||||
} catch { "RESULT adapter_$tag error=$($_.Exception.Message)" }
|
||||
# the 32-bit AdapterRAM field caps at 4 GB: the true size is the class key's qwMemorySize
|
||||
try {
|
||||
foreach ($k in @(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' })) {
|
||||
$p = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
|
||||
if ($p -and $p.DriverDesc) { $mem = ''; if ($null -ne $p.'HardwareInformation.qwMemorySize') { $mem = [string]([math]::Round([double]$p.'HardwareInformation.qwMemorySize' / 1MB)) + ' MiB' }; "RESULT class_$tag key=$($k.PSChildName) desc=`"$($p.DriverDesc)`" driver=$($p.DriverVersion) provider=`"$($p.ProviderName)`" memory=$mem" }
|
||||
}
|
||||
} catch { "RESULT class_$tag error=$($_.Exception.Message)" }
|
||||
try {
|
||||
foreach ($d in @(Get-PnpDevice -Class Display -ErrorAction Stop)) { "RESULT pnp_$tag name=`"$($d.FriendlyName)`" status=$($d.Status) present=$($d.Present) id=$($d.InstanceId)" }
|
||||
} catch { "RESULT pnp_$tag error=$($_.Exception.Message)" }
|
||||
}
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$worker = $null
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $worker = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
"RESULT app install=$inst worker=$worker"
|
||||
function OpenCLList([string] $tag) {
|
||||
if (-not $worker) { "RESULT opencl_$tag error=no installed igneum-worker-opencl.exe to list with"; return @() }
|
||||
$lines = @(& $worker --list 2>&1 | ForEach-Object { "$_" })
|
||||
foreach ($l in $lines) { "RESULT opencl_$tag $l" }
|
||||
return $lines
|
||||
}
|
||||
Adapters 'before'
|
||||
$clBefore = OpenCLList 'before'
|
||||
$intelBefore = @($clBefore | Where-Object { $_ -match 'Intel' }).Count
|
||||
"RESULT opencl_before intel_lines=$intelBefore"
|
||||
|
||||
# ---- the download: curl.exe (shipped with Windows 10 1803 and later), the size and the SHA512 Intel publishes ----
|
||||
$dl = $null
|
||||
if (Test-Path -LiteralPath $exe) {
|
||||
$len = (Get-Item -LiteralPath $exe).Length
|
||||
if ($len -eq $expectedBytes) { "RESULT download reuse $exe bytes=$len (already here)"; $dl = $true } else { Remove-Item -LiteralPath $exe -Force -ErrorAction SilentlyContinue }
|
||||
}
|
||||
if (-not $dl) {
|
||||
$curl = Join-Path $env:SystemRoot 'System32\curl.exe'
|
||||
if (-not (Test-Path -LiteralPath $curl)) { "RESULT error no curl.exe at $curl"; Summary 'failed' @{ error = 'no curl' }; exit 2 }
|
||||
$t0 = Get-Date
|
||||
"RESULT download start $(Stamp) url=$url expected_bytes=$expectedBytes"
|
||||
$attempt = 0
|
||||
while ($attempt -lt 3) {
|
||||
$attempt++
|
||||
$out = @(& $curl -sS -L --retry 3 --retry-delay 5 --connect-timeout 30 --max-time 1500 -o $exe $url 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$len = 0; if (Test-Path -LiteralPath $exe) { $len = (Get-Item -LiteralPath $exe).Length }
|
||||
"RESULT download attempt=$attempt curl_exit=$rc bytes=$len wall_s=$([int]((Get-Date) - $t0).TotalSeconds) $($out -join ' ')"
|
||||
if ($rc -eq 0 -and $len -eq $expectedBytes) { $dl = $true; break }
|
||||
Start-Sleep -Seconds 10
|
||||
}
|
||||
if (-not $dl) { "RESULT error download did not reach $expectedBytes bytes in 3 attempts"; Summary 'failed' @{ error = 'download' }; exit 2 }
|
||||
}
|
||||
$sha = (Get-FileHash -Algorithm SHA512 -LiteralPath $exe).Hash.ToLower()
|
||||
$sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $exe).Hash.ToLower()
|
||||
"RESULT file $exe bytes=$((Get-Item -LiteralPath $exe).Length) sha512=$sha sha256=$sha256"
|
||||
if ($sha -ne $expectedSha512) { "RESULT error sha512 mismatch: Intel publishes $expectedSha512; the installer is NOT run"; Summary 'failed' @{ error = 'sha512 mismatch'; sha512 = $sha }; exit 2 }
|
||||
"RESULT sha512 ok (equal to the string on Intel's page 785597)"
|
||||
try { $sig = Get-AuthenticodeSignature -LiteralPath $exe; "RESULT authenticode status=$($sig.Status) signer=`"$($sig.SignerCertificate.Subject)`"" } catch { "RESULT authenticode error=$($_.Exception.Message)" }
|
||||
|
||||
# ---- the installer: -s silent, no -b (no reboot by the installer); its exit code says what happened ----
|
||||
# Intel's codes (support article 000098195, read 7 October 2026): 0 success, 1 failed, 2 the installer rebooted the
|
||||
# system itself (-b only), 8 no driver for this device, 13 reboot required BEFORE setup, 14 reboot required after.
|
||||
$meaning = @{ 0 = 'installed, no reboot asked'; 1 = 'installer failed (general)'; 2 = 'the installer rebooted the system'; 5 = 'the OS or a device on this platform is not supported by the driver (no supported Intel GPU present?)'; 6 = 'the installer was closed before it finished'; 7 = 'unsupported command-line parameter'; 8 = 'no INF files found in the bundle'; 9 = 'driver files without an Intel signature'; 11 = 'no free space'; 13 = 'a reboot is required BEFORE this driver can install'; 14 = 'installed; a reboot is required to apply it (NOT taken: the project lead decides)'; 1007 = 'the bootstrapper wrapped an inner Installer.exe exit 7 (unsupported parameter)'; 1014 = 'installed; a reboot is required to apply it (the bootstrapper wrapping the inner 14; NOT taken: the project lead decides)' }
|
||||
# Run 1 (09:27Z): `-s -report <path>` made the inner Installer.exe exit 7 ("unsupported parameter") and the bootstrapper
|
||||
# answer 1007 before any device check (its log: C:\ProgramData\Intel\GFXInstaller\Bootstrapper\IntelGFX_Bootstrapper_*.log).
|
||||
# So: -s alone. The bootstrapper also installs the .NET Desktop Runtime it needs (it did on run 1, exit 0).
|
||||
$t1 = Get-Date
|
||||
"RESULT install start $(Stamp) args=-s (silent, no -b); the screen may blink while the Intel device resets; NVIDIA and AMD drivers are not touched"
|
||||
$p = $null
|
||||
try { $p = Start-Process -FilePath $exe -ArgumentList @('-s') -Wait -PassThru -WindowStyle Hidden -ErrorAction Stop } catch { "RESULT error installer launch: $($_.Exception.Message)"; Summary 'failed' @{ error = 'launch' }; exit 2 }
|
||||
$code = $p.ExitCode
|
||||
$word = if ($meaning.ContainsKey([int]$code)) { $meaning[[int]$code] } else { 'unknown code' }
|
||||
"RESULT install end $(Stamp) exit=$code meaning=`"$word`" wall_s=$([int]((Get-Date) - $t1).TotalSeconds)"
|
||||
# the installer's own logs: the bootstrapper's under GFXInstaller\Bootstrapper, the inner installer's beside it (read 7 October 2026)
|
||||
$logRoot = Join-Path $env:ProgramData 'Intel\GFXInstaller'
|
||||
if (Test-Path -LiteralPath $logRoot) {
|
||||
foreach ($lf in @(Get-ChildItem -LiteralPath $logRoot -Recurse -Filter '*.log' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt $t1.AddMinutes(-1) } | Sort-Object LastWriteTime)) {
|
||||
"RESULT log $($lf.FullName) bytes=$($lf.Length)"
|
||||
Get-Content -LiteralPath $lf.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'exit|error|fail|device|not supported|reboot|restart|install' } | Select-Object -Last 25 | ForEach-Object { "RESULT log $_" }
|
||||
}
|
||||
} else { "RESULT log none under $logRoot" }
|
||||
|
||||
# ---- what Windows sees after: the Intel device by name and version, its memory, the OpenCL platform ----
|
||||
Start-Sleep -Seconds 10
|
||||
Adapters 'after'
|
||||
$clAfter = OpenCLList 'after'
|
||||
$intelAfter = @($clAfter | Where-Object { $_ -match 'Intel' })
|
||||
"RESULT opencl_after intel_lines=$($intelAfter.Count)"
|
||||
$found = @()
|
||||
try { $found = @(Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'Intel' } | ForEach-Object { "$($_.Name) driver $($_.DriverVersion) status $($_.Status) code $($_.ConfigManagerErrorCode)" }) } catch { }
|
||||
foreach ($f in $found) { "RESULT intel_device $f" }
|
||||
$versionOk = @($found | Where-Object { $_ -match [regex]::Escape($version) }).Count -gt 0
|
||||
$clinfo = Get-Command clinfo -ErrorAction SilentlyContinue
|
||||
if ($clinfo) { & $clinfo.Source -l 2>&1 | ForEach-Object { "RESULT clinfo $_" } } else { "RESULT clinfo not installed (the worker's --list above is the enumeration)" }
|
||||
$reboot = ($code -eq 14 -or $code -eq 13 -or $code -eq 1014)
|
||||
$status = if ($code -eq 0 -or $code -eq 14 -or $code -eq 1014) { 'done' } else { 'failed' }
|
||||
if ($reboot) {
|
||||
"RESULT restart $(Stamp) the installer asked for a restart (exit $code): restarting PC 2 in 20 s on main's word for PC 2; the read-back job runs after the boot"
|
||||
& "$env:SystemRoot\System32\shutdown.exe" /r /t 20 /c "Igneum: restart to apply the Intel Arc driver 32.0.101.9034 (PC 2 driver job)" 2>&1 | ForEach-Object { "RESULT shutdown $_" }
|
||||
"RESULT restart requested $(Stamp) shutdown_exit=$LASTEXITCODE"
|
||||
}
|
||||
"RESULT end $(Stamp) version_installed=$versionOk reboot_required=$reboot intel_opencl_platform=$($intelAfter.Count -gt 0)"
|
||||
Summary $status @{ version = $version; installer_exit = $code; meaning = $word; reboot_required = $reboot; sha512 = $sha; sha256 = $sha256; intel_devices = $found; intel_opencl_lines = $intelAfter.Count; opencl_intel_before = $intelBefore }
|
||||
if ($status -eq 'failed') { exit 1 }
|
||||
exit 0
|
||||
143
tools/intel-arc/pc2-intel-driver.ps1
Normal file
143
tools/intel-arc/pc2-intel-driver.ps1
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
# Igneum run job: the Intel Arc graphics driver on PC 2 (machine 1ccfe586), 7 October 2026 (branch intel-arc). the project lead's word:
|
||||
# "PC 2 needs Arc drivers"; the B580 sits in a Razer Core X V2 on PC 2. PC 1's recipe (pc1-intel-driver.ps1) plus one
|
||||
# step: when the installer answers 14 or 1014 (installed, restart required) this job RESTARTS the PC itself (main's word
|
||||
# for PC 2, not the project lead's desk), with a line before and after; the read-back job runs after the boot.
|
||||
# Published as a signed `run` job with --elevated and WITHOUT --stop-miners: the RUNNER raises the one elevation prompt
|
||||
# (Start-Process -Verb RunAs) at the script's start, the project lead clicks it at the desk, and everything below runs under it:
|
||||
# the download, the hash check and the installer, with no second prompt. The installed app, its node and its miners
|
||||
# are never quit, paused, resumed or switched (no api/* request in this script); the Intel installer touches only
|
||||
# the Intel display device, never the NVIDIA or AMD driver, and runs with -s (silent) and no -b, so it never reboots
|
||||
# on its own; exit 14 = "reboot required", reported and left to the project lead. Lines start with RESULT so
|
||||
# `node tools/jobs.mjs <job id>` shows them; a SUMMARY {json} line ends the job.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc2-intel-driver'
|
||||
$started = Get-Date
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
function Summary([string] $status, [hashtable] $extra) {
|
||||
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
# Intel's download page 785597 (read 7 October 2026): version 32.0.101.9034, released 2 October 2026 (non-WHQL; 9033
|
||||
# is the WHQL one), file gfx_win_101.9034.exe, 889.4 MB, mirror folder 929959; the SHA512 is the string the page prints.
|
||||
$version = '32.0.101.9034'
|
||||
$url = 'https://downloadmirror.intel.com/929959/gfx_win_101.9034.exe'
|
||||
$expectedBytes = 932631144
|
||||
$expectedSha512 = '3fc6f9b4c8ef40bf066f1ad77f3c6aea55d47bae6dc8ab9575a7a9fe8eee84bf0c79333f81825cd7538ea1bb7ca78b6ddb0b75f96b321b4dcf524dec843cf3f0'
|
||||
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-intel-driver' }
|
||||
New-Item -ItemType Directory -Force -Path $job | Out-Null
|
||||
$exe = Join-Path $job 'gfx_win_101.9034.exe'
|
||||
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$elevated = (New-Object Security.Principal.WindowsPrincipal($id)).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION user=$($id.Name) elevated=$elevated version_wanted=$version"
|
||||
if (-not $elevated) { "RESULT error not elevated: the installer needs administrator rights (publish with --elevated)"; Summary 'failed' @{ error = 'not elevated' }; exit 2 }
|
||||
|
||||
# ---- what Windows sees before: every display adapter, the Intel ones by PnP, the OpenCL platforms the worker lists ----
|
||||
function Adapters([string] $tag) {
|
||||
try {
|
||||
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) {
|
||||
"RESULT adapter_$tag name=`"$($a.Name)`" driver=$($a.DriverVersion) date=$($a.DriverDate) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID) ram32=$($a.AdapterRAM)"
|
||||
}
|
||||
} catch { "RESULT adapter_$tag error=$($_.Exception.Message)" }
|
||||
# the 32-bit AdapterRAM field caps at 4 GB: the true size is the class key's qwMemorySize
|
||||
try {
|
||||
foreach ($k in @(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' })) {
|
||||
$p = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
|
||||
if ($p -and $p.DriverDesc) { $mem = ''; if ($null -ne $p.'HardwareInformation.qwMemorySize') { $mem = [string]([math]::Round([double]$p.'HardwareInformation.qwMemorySize' / 1MB)) + ' MiB' }; "RESULT class_$tag key=$($k.PSChildName) desc=`"$($p.DriverDesc)`" driver=$($p.DriverVersion) provider=`"$($p.ProviderName)`" memory=$mem" }
|
||||
}
|
||||
} catch { "RESULT class_$tag error=$($_.Exception.Message)" }
|
||||
try {
|
||||
foreach ($d in @(Get-PnpDevice -Class Display -ErrorAction Stop)) { "RESULT pnp_$tag name=`"$($d.FriendlyName)`" status=$($d.Status) present=$($d.Present) id=$($d.InstanceId)" }
|
||||
} catch { "RESULT pnp_$tag error=$($_.Exception.Message)" }
|
||||
}
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$worker = $null
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $worker = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
"RESULT app install=$inst worker=$worker"
|
||||
function OpenCLList([string] $tag) {
|
||||
if (-not $worker) { "RESULT opencl_$tag error=no installed igneum-worker-opencl.exe to list with"; return @() }
|
||||
$lines = @(& $worker --list 2>&1 | ForEach-Object { "$_" })
|
||||
foreach ($l in $lines) { "RESULT opencl_$tag $l" }
|
||||
return $lines
|
||||
}
|
||||
Adapters 'before'
|
||||
$clBefore = OpenCLList 'before'
|
||||
$intelBefore = @($clBefore | Where-Object { $_ -match 'Intel' }).Count
|
||||
"RESULT opencl_before intel_lines=$intelBefore"
|
||||
|
||||
# ---- the download: curl.exe (shipped with Windows 10 1803 and later), the size and the SHA512 Intel publishes ----
|
||||
$dl = $null
|
||||
if (Test-Path -LiteralPath $exe) {
|
||||
$len = (Get-Item -LiteralPath $exe).Length
|
||||
if ($len -eq $expectedBytes) { "RESULT download reuse $exe bytes=$len (already here)"; $dl = $true } else { Remove-Item -LiteralPath $exe -Force -ErrorAction SilentlyContinue }
|
||||
}
|
||||
if (-not $dl) {
|
||||
$curl = Join-Path $env:SystemRoot 'System32\curl.exe'
|
||||
if (-not (Test-Path -LiteralPath $curl)) { "RESULT error no curl.exe at $curl"; Summary 'failed' @{ error = 'no curl' }; exit 2 }
|
||||
$t0 = Get-Date
|
||||
"RESULT download start $(Stamp) url=$url expected_bytes=$expectedBytes"
|
||||
$attempt = 0
|
||||
while ($attempt -lt 3) {
|
||||
$attempt++
|
||||
$out = @(& $curl -sS -L --retry 3 --retry-delay 5 --connect-timeout 30 --max-time 1500 -o $exe $url 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$len = 0; if (Test-Path -LiteralPath $exe) { $len = (Get-Item -LiteralPath $exe).Length }
|
||||
"RESULT download attempt=$attempt curl_exit=$rc bytes=$len wall_s=$([int]((Get-Date) - $t0).TotalSeconds) $($out -join ' ')"
|
||||
if ($rc -eq 0 -and $len -eq $expectedBytes) { $dl = $true; break }
|
||||
Start-Sleep -Seconds 10
|
||||
}
|
||||
if (-not $dl) { "RESULT error download did not reach $expectedBytes bytes in 3 attempts"; Summary 'failed' @{ error = 'download' }; exit 2 }
|
||||
}
|
||||
$sha = (Get-FileHash -Algorithm SHA512 -LiteralPath $exe).Hash.ToLower()
|
||||
$sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $exe).Hash.ToLower()
|
||||
"RESULT file $exe bytes=$((Get-Item -LiteralPath $exe).Length) sha512=$sha sha256=$sha256"
|
||||
if ($sha -ne $expectedSha512) { "RESULT error sha512 mismatch: Intel publishes $expectedSha512; the installer is NOT run"; Summary 'failed' @{ error = 'sha512 mismatch'; sha512 = $sha }; exit 2 }
|
||||
"RESULT sha512 ok (equal to the string on Intel's page 785597)"
|
||||
try { $sig = Get-AuthenticodeSignature -LiteralPath $exe; "RESULT authenticode status=$($sig.Status) signer=`"$($sig.SignerCertificate.Subject)`"" } catch { "RESULT authenticode error=$($_.Exception.Message)" }
|
||||
|
||||
# ---- the installer: -s silent, no -b (no reboot by the installer); its exit code says what happened ----
|
||||
# Intel's codes (support article 000098195, read 7 October 2026): 0 success, 1 failed, 2 the installer rebooted the
|
||||
# system itself (-b only), 8 no driver for this device, 13 reboot required BEFORE setup, 14 reboot required after.
|
||||
$meaning = @{ 0 = 'installed, no reboot asked'; 1 = 'installer failed (general)'; 2 = 'the installer rebooted the system'; 5 = 'the OS or a device on this platform is not supported by the driver (no supported Intel GPU present?)'; 6 = 'the installer was closed before it finished'; 7 = 'unsupported command-line parameter'; 8 = 'no INF files found in the bundle'; 9 = 'driver files without an Intel signature'; 11 = 'no free space'; 13 = 'a reboot is required BEFORE this driver can install'; 14 = 'installed; a reboot is required to apply it (NOT taken: the project lead decides)'; 1007 = 'the bootstrapper wrapped an inner Installer.exe exit 7 (unsupported parameter)'; 1014 = 'installed; a reboot is required to apply it (the bootstrapper wrapping the inner 14; NOT taken: the project lead decides)' }
|
||||
# Run 1 (09:27Z): `-s -report <path>` made the inner Installer.exe exit 7 ("unsupported parameter") and the bootstrapper
|
||||
# answer 1007 before any device check (its log: C:\ProgramData\Intel\GFXInstaller\Bootstrapper\IntelGFX_Bootstrapper_*.log).
|
||||
# So: -s alone. The bootstrapper also installs the .NET Desktop Runtime it needs (it did on run 1, exit 0).
|
||||
$t1 = Get-Date
|
||||
"RESULT install start $(Stamp) args=-s (silent, no -b); the screen may blink while the Intel device resets; NVIDIA and AMD drivers are not touched"
|
||||
$p = $null
|
||||
try { $p = Start-Process -FilePath $exe -ArgumentList @('-s') -Wait -PassThru -WindowStyle Hidden -ErrorAction Stop } catch { "RESULT error installer launch: $($_.Exception.Message)"; Summary 'failed' @{ error = 'launch' }; exit 2 }
|
||||
$code = $p.ExitCode
|
||||
$word = if ($meaning.ContainsKey([int]$code)) { $meaning[[int]$code] } else { 'unknown code' }
|
||||
"RESULT install end $(Stamp) exit=$code meaning=`"$word`" wall_s=$([int]((Get-Date) - $t1).TotalSeconds)"
|
||||
# the installer's own logs: the bootstrapper's under GFXInstaller\Bootstrapper, the inner installer's beside it (read 7 October 2026)
|
||||
$logRoot = Join-Path $env:ProgramData 'Intel\GFXInstaller'
|
||||
if (Test-Path -LiteralPath $logRoot) {
|
||||
foreach ($lf in @(Get-ChildItem -LiteralPath $logRoot -Recurse -Filter '*.log' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt $t1.AddMinutes(-1) } | Sort-Object LastWriteTime)) {
|
||||
"RESULT log $($lf.FullName) bytes=$($lf.Length)"
|
||||
Get-Content -LiteralPath $lf.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'exit|error|fail|device|not supported|reboot|restart|install' } | Select-Object -Last 25 | ForEach-Object { "RESULT log $_" }
|
||||
}
|
||||
} else { "RESULT log none under $logRoot" }
|
||||
|
||||
# ---- what Windows sees after: the Intel device by name and version, its memory, the OpenCL platform ----
|
||||
Start-Sleep -Seconds 10
|
||||
Adapters 'after'
|
||||
$clAfter = OpenCLList 'after'
|
||||
$intelAfter = @($clAfter | Where-Object { $_ -match 'Intel' })
|
||||
"RESULT opencl_after intel_lines=$($intelAfter.Count)"
|
||||
$found = @()
|
||||
try { $found = @(Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'Intel' } | ForEach-Object { "$($_.Name) driver $($_.DriverVersion) status $($_.Status) code $($_.ConfigManagerErrorCode)" }) } catch { }
|
||||
foreach ($f in $found) { "RESULT intel_device $f" }
|
||||
$versionOk = @($found | Where-Object { $_ -match [regex]::Escape($version) }).Count -gt 0
|
||||
$clinfo = Get-Command clinfo -ErrorAction SilentlyContinue
|
||||
if ($clinfo) { & $clinfo.Source -l 2>&1 | ForEach-Object { "RESULT clinfo $_" } } else { "RESULT clinfo not installed (the worker's --list above is the enumeration)" }
|
||||
$reboot = ($code -eq 14 -or $code -eq 13 -or $code -eq 1014)
|
||||
$status = if ($code -eq 0 -or $code -eq 14 -or $code -eq 1014) { 'done' } else { 'failed' }
|
||||
if ($reboot) {
|
||||
"RESULT restart $(Stamp) the installer asked for a restart (exit $code): restarting PC 2 in 20 s on main's word for PC 2; the read-back job runs after the boot"
|
||||
& "$env:SystemRoot\System32\shutdown.exe" /r /t 20 /c "Igneum: restart to apply the Intel Arc driver 32.0.101.9034 (PC 2 driver job)" 2>&1 | ForEach-Object { "RESULT shutdown $_" }
|
||||
"RESULT restart requested $(Stamp) shutdown_exit=$LASTEXITCODE"
|
||||
}
|
||||
"RESULT end $(Stamp) version_installed=$versionOk reboot_required=$reboot intel_opencl_platform=$($intelAfter.Count -gt 0)"
|
||||
Summary $status @{ version = $version; installer_exit = $code; meaning = $word; reboot_required = $reboot; sha512 = $sha; sha256 = $sha256; intel_devices = $found; intel_opencl_lines = $intelAfter.Count; opencl_intel_before = $intelBefore }
|
||||
if ($status -eq 'failed') { exit 1 }
|
||||
exit 0
|
||||
71
tools/intel-arc/pc2-intel-readback.ps1
Normal file
71
tools/intel-arc/pc2-intel-readback.ps1
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Igneum run job: read back PC 2 (machine 1ccfe586) after the restart that applied the Intel Arc driver 32.0.101.9034
|
||||
# (the B580 in a Razer Core X V2 over Thunderbolt: the PCIe link width and speed of the device are read too)
|
||||
# (7 October 2026, branch intel-arc). Read only, NOT elevated, beside the miners: no api/* request other than one GET
|
||||
# of api/state for the card row; never quits, pauses, resumes or switches anything. Lines: the Intel adapter row (driver
|
||||
# version, status, Code), the PnP display devices, the installed worker's --list (the "Intel(R) OpenCL Graphics" platform
|
||||
# line with the device's global MiB), clinfo when present, and the app's own card row for the Arc after hotplug.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
$want = '32.0.101.9034'
|
||||
"RESULT start $(Stamp) job=pc2-intel-readback machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION uptime_s=$([int]((Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime).TotalSeconds)"
|
||||
$found = @()
|
||||
try {
|
||||
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) {
|
||||
"RESULT adapter name=`"$($a.Name)`" driver=$($a.DriverVersion) date=$($a.DriverDate) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID)"
|
||||
if ($a.Name -match 'Intel') { $found += "$($a.Name) driver $($a.DriverVersion) status $($a.Status) code $($a.ConfigManagerErrorCode)" }
|
||||
}
|
||||
} catch { "RESULT adapter error=$($_.Exception.Message)" }
|
||||
try {
|
||||
foreach ($d in @(Get-PnpDevice -Class Display -ErrorAction Stop)) {
|
||||
"RESULT pnp name=`"$($d.FriendlyName)`" status=$($d.Status) present=$($d.Present) problem=$($d.Problem) id=$($d.InstanceId)"
|
||||
if ($d.FriendlyName -match 'Intel' -and $d.Present) {
|
||||
# the eGPU link: PCIe width and speed as negotiated (DEVPKEY_PciDevice_CurrentLinkWidth/Speed) against the device's maximum
|
||||
try {
|
||||
$props = Get-PnpDeviceProperty -InstanceId $d.InstanceId -KeyName 'DEVPKEY_PciDevice_CurrentLinkWidth', 'DEVPKEY_PciDevice_CurrentLinkSpeed', 'DEVPKEY_PciDevice_MaxLinkWidth', 'DEVPKEY_PciDevice_MaxLinkSpeed', 'DEVPKEY_Device_LocationInfo' -ErrorAction Stop
|
||||
$line = ($props | ForEach-Object { ($_.KeyName -replace 'DEVPKEY_(PciDevice_|Device_)', '') + '=' + $_.Data }) -join ' '
|
||||
"RESULT link $line (speed 1=2.5 GT/s gen1, 2=5 gen2, 3=8 gen3, 4=16 gen4)"
|
||||
} catch { "RESULT link error=$($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
} catch { "RESULT pnp error=$($_.Exception.Message)" }
|
||||
try {
|
||||
foreach ($k in @(Get-ChildItem 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' -ErrorAction Stop | Where-Object { $_.PSChildName -match '^\d{4}$' })) {
|
||||
$p = Get-ItemProperty -Path $k.PSPath -ErrorAction SilentlyContinue
|
||||
if ($p -and $p.DriverDesc -match 'Intel') { $mem = '-'; if ($null -ne $p.'HardwareInformation.qwMemorySize') { $mem = [string]([math]::Round([double]$p.'HardwareInformation.qwMemorySize' / 1MB)) + ' MiB' }; "RESULT class key=$($k.PSChildName) desc=`"$($p.DriverDesc)`" driver=$($p.DriverVersion) memory=$mem" }
|
||||
}
|
||||
} catch { "RESULT class not_readable=$($_.Exception.Message)" }
|
||||
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-app.exe') } | Select-Object -First 1
|
||||
$worker = $null
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $worker = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
$intelLines = @()
|
||||
if ($worker) {
|
||||
$list = @(& $worker --list 2>&1 | ForEach-Object { "$_" })
|
||||
foreach ($l in $list) { "RESULT list $l" }
|
||||
$intelLines = @($list | Where-Object { $_ -match 'Intel' })
|
||||
} else { "RESULT list error=no installed igneum-worker-opencl.exe" }
|
||||
"RESULT opencl intel_lines=$($intelLines.Count)"
|
||||
$clinfo = Get-Command clinfo -ErrorAction SilentlyContinue
|
||||
if ($clinfo) { & $clinfo.Source -l 2>&1 | ForEach-Object { "RESULT clinfo $_" } }
|
||||
# the app's own row for the Arc (read only)
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$row = ''
|
||||
try {
|
||||
$urlFile = Join-Path $appDir 'app.url'
|
||||
if (Test-Path $urlFile) {
|
||||
$url = (Get-Content -LiteralPath $urlFile -Raw).Trim()
|
||||
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
|
||||
$all = $st.mining.cards; if (-not $all) { $all = $st.cards }
|
||||
foreach ($c in @($all)) { $line = "key=$($c.key) name=`"$($c.name)`" vendor=$($c.vendor) kind=$($c.kind) worker=$($c.worker) enabled=$($c.enabled) identities=$($c.identities) state=$($c.state) pid=$($c.pid) hash_now=$($c.hash_now) vram_mb=$($c.vram_mb) problem=`"$($c.problem)`" message=`"$($c.message)`""; "RESULT app_card $line"; if ($c.name -match 'Intel') { $row = $line } }
|
||||
} else { "RESULT app_card error=no app.url (the app is not up yet)" }
|
||||
} catch { "RESULT app_card error=$($_.Exception.Message)" }
|
||||
# first light, read only: the app's own log lines for the Intel worker (ready, exchange, sub-group, or its error)
|
||||
try {
|
||||
$logDir = $env:IGNEUM_LOG_DIR; if (-not $logDir) { $logDir = Join-Path $appDir 'logs' }
|
||||
$newest = Get-ChildItem -Path $logDir -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if ($newest) { "RESULT firstlight log=$($newest.FullName)"; Get-Content -LiteralPath $newest.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match 'worker|exchange|ready|prepare|pack|miner' -and $_ -notmatch 'job run-ia|job collect-ia|RESULT ' } | Select-Object -Last 40 | ForEach-Object { "RESULT firstlight $_" } } else { "RESULT firstlight error=no app-*.log under $logDir" }
|
||||
} catch { "RESULT firstlight error=$($_.Exception.Message)" }
|
||||
foreach ($f in $found) { "RESULT intel_device $f" }
|
||||
$versionOk = @($found | Where-Object { $_ -match [regex]::Escape($want) -and $_ -match 'code 0' }).Count -gt 0
|
||||
"RESULT end $(Stamp) version_ok=$versionOk intel_opencl_platform=$($intelLines.Count -gt 0) app_row=$(if ($row) { 'yes' } else { 'no' })"
|
||||
'SUMMARY ' + ([ordered]@{ job = 'pc2-intel-readback'; status = $(if ($versionOk -and $intelLines.Count -gt 0) { 'done' } else { 'partial' }); finished_at = (Stamp); version_ok = $versionOk; intel_opencl_lines = $intelLines.Count; intel_devices = $found; app_row = $row } | ConvertTo-Json -Compress -Depth 3)
|
||||
exit 0
|
||||
53
tools/intel-arc/pc2-minidump.ps1
Normal file
53
tools/intel-arc/pc2-minidump.ps1
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Igneum run job: the kernel minidump of PC 2's 16:27Z crash (7 October 2026, bugcheck 0x3B during the Intel Arc driver
|
||||
# install) out through the signed channel, read only, NOT elevated, beside the miners: no api/* request, nothing switched.
|
||||
# The log intake carries text (the last 256 KB of a file), so the 2 MB dump is copied into the app's jobs folder as
|
||||
# base64 chunks under 180,000 bytes each with a sha256 per chunk in the job's own folder; the collect job that follows uploads them by glob and
|
||||
# the Mac reassembles and parses the triage dump (bugcheck, exception record, driver list, the faulting module).
|
||||
# The header's own facts are printed here as well (DUMP_HEADER64: bugcheck code and parameters, TRIAGE_DUMP64 counts).
|
||||
$ErrorActionPreference = 'Continue'
|
||||
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||
"RESULT start $(Stamp) job=pc2-minidump machine=$env:COMPUTERNAME user=$env:USERNAME"
|
||||
$dir = 'C:\WINDOWS\Minidump'
|
||||
try { foreach ($f in @(Get-ChildItem -LiteralPath $dir -File -ErrorAction Stop | Sort-Object LastWriteTime)) { "RESULT minidump_dir file=`"$($f.Name)`" bytes=$($f.Length) written=$($f.LastWriteTime.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ'))" } } catch { "RESULT minidump_dir error=$($_.Exception.Message)" }
|
||||
$src = Join-Path $dir '100726-22234-01.dmp'
|
||||
# the chunks land in the job's own folder (made by the app for this run), never in another job's kit
|
||||
$out = $env:IGNEUM_JOB_DIR; if (-not $out) { $out = Join-Path $env:TEMP 'ia-minidump-20261007' }
|
||||
try {
|
||||
$bytes = [System.IO.File]::ReadAllBytes($src)
|
||||
$sha = (Get-FileHash -LiteralPath $src -Algorithm SHA256).Hash.ToLower()
|
||||
"RESULT dump file=`"$src`" bytes=$($bytes.Length) sha256=$sha"
|
||||
# DUMP_HEADER64: Signature 'PAGE' ValidDump 'DU64'; BugCheckCode at 56, parameters at 64..95; the triage block at 0x2000
|
||||
$sig = [System.Text.Encoding]::ASCII.GetString($bytes, 0, 8)
|
||||
$code = [BitConverter]::ToUInt32($bytes, 56)
|
||||
$p = @(0..3 | ForEach-Object { [BitConverter]::ToUInt64($bytes, 64 + 8 * $_) })
|
||||
$machine = [BitConverter]::ToUInt32($bytes, 48); $cpus = [BitConverter]::ToUInt32($bytes, 52)
|
||||
$major = [BitConverter]::ToUInt32($bytes, 8); $minor = [BitConverter]::ToUInt32($bytes, 12)
|
||||
$dumpType = [BitConverter]::ToUInt32($bytes, 0xf98)
|
||||
"RESULT header signature=`"$sig`" version=$major.$minor machine=0x$($machine.ToString('x')) cpus=$cpus dump_type=$dumpType bugcheck=0x$($code.ToString('x8')) p1=0x$($p[0].ToString('x')) p2=0x$($p[1].ToString('x')) p3=0x$($p[2].ToString('x')) p4=0x$($p[3].ToString('x'))"
|
||||
if ($bytes.Length -gt 0x2000 + 80) {
|
||||
$t = 0x2000
|
||||
$names = 'ServicePackBuild', 'SizeOfDump', 'ValidOffset', 'ContextOffset', 'ExceptionOffset', 'MmOffset', 'UnloadedDriversOffset', 'PrcbOffset', 'ProcessOffset', 'ThreadOffset', 'CallStackOffset', 'SizeOfCallStack', 'DriverListOffset', 'DriverCount', 'StringPoolOffset', 'StringPoolSize', 'BrokenDriverOffset', 'TriageOptions'
|
||||
$line = @(); for ($i = 0; $i -lt $names.Count; $i++) { $line += "$($names[$i])=$([BitConverter]::ToUInt32($bytes, $t + 4 * $i))" }
|
||||
"RESULT triage $($line -join ' ') TopOfStack=0x$([BitConverter]::ToUInt64($bytes, $t + 72).ToString('x'))"
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $out | Out-Null
|
||||
Get-ChildItem -LiteralPath $out -File -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue
|
||||
$chunk = 135000 # raw bytes per chunk: 180,000 base64 bytes, under the intake's 256 KB tail
|
||||
$n = [math]::Ceiling($bytes.Length / $chunk)
|
||||
for ($i = 0; $i -lt $n; $i++) {
|
||||
$len = [math]::Min($chunk, $bytes.Length - $i * $chunk)
|
||||
$b64 = [Convert]::ToBase64String($bytes, $i * $chunk, $len, [System.Base64FormattingOptions]::InsertLineBreaks)
|
||||
$name = Join-Path $out ('dump-{0:d3}.b64' -f $i)
|
||||
[System.IO.File]::WriteAllText($name, "CHUNK $i of $n bytes=$len`r`n$b64`r`nEND $i`r`n", [System.Text.Encoding]::ASCII)
|
||||
$h = (Get-FileHash -LiteralPath $name -Algorithm SHA256).Hash.ToLower()
|
||||
"RESULT chunk i=$i of=$n raw_bytes=$len file_bytes=$((Get-Item -LiteralPath $name).Length) sha256=$h"
|
||||
}
|
||||
"RESULT end $(Stamp) chunks=$n dir=`"$out`""
|
||||
'SUMMARY ' + ([ordered]@{ job = 'pc2-minidump'; status = 'done'; finished_at = (Stamp); bytes = $bytes.Length; sha256 = $sha; chunks = $n } | ConvertTo-Json -Compress)
|
||||
exit 0
|
||||
} catch {
|
||||
"RESULT dump error=$($_.Exception.Message)"
|
||||
"RESULT end $(Stamp) chunks=0"
|
||||
'SUMMARY ' + ([ordered]@{ job = 'pc2-minidump'; status = 'partial'; finished_at = (Stamp); error = $_.Exception.Message } | ConvertTo-Json -Compress)
|
||||
exit 0
|
||||
}
|
||||
|
|
@ -124,6 +124,13 @@ function stateNow() {
|
|||
if (scenario === 'clock') { s.clock = { ...s.clock, severity: 'block', skew_s: -412, message: 'This machine\'s clock is 6 min 52 s behind the network. Blocks it finds are rejected until the clock is right.', hint: 'System Settings > General > Date & Time: turn on "Set time and date automatically"' }; s.mining.state = 'waiting'; s.mining.hash_total = 0; }
|
||||
if (scenario === 'fresh') { s.setup_done = false; s.phase = 'welcome'; s.address = { display: '', key_saved: false, source: '', value: '', wallet_file: '' }; }
|
||||
if (scenario === 'job') { s.jobs = { ...s.jobs, active: true, id: 'job-2026-10-04-17', kind: 'shard-benchmark', title: 'shard benchmark', stage: 'proving shard 3 of 8', started_at: t - 400, results: ['RESULT shard=1 prove_s=41.2', 'RESULT shard=2 prove_s=39.8'], message: '' }; }
|
||||
if (scenario === 'intel') {
|
||||
// PC 1 after the B580 swap (7 October 2026, docs/plans/intel-arc.md): the 5090 and the first Intel card; the Arc's
|
||||
// numbers are the plan's expectation, not a measurement, until the PC 1 job's row lands
|
||||
s.mining.cards = [{ ...recState.mining.cards[0], key: 'nvidia:NVIDIA GeForce RTX 5090', name: 'NVIDIA GeForce RTX 5090', kind: 'discrete', vendor: 'nvidia', worker: 'CUDA', detail: '21760 cores', vram_mb: 32768, device: '0', identities: 8, ids: ['8fafda27', '1b2c3d4e', '5f6a7b8c'], power_default_w: 575, power_pct: 80, power_applied: true, power_limit_w: 460, power_w: 227, temp_gpu: 61, temp_mem: 70, hash_now: 127.7, hash_avg: 127.1, eff_mhw: 0.563, sweep_supported: true, sweep_state: 'idle', sweep_note: '', tune_line: 'Tuned: 127.7 MH/s at 227 W (0.563 MH/W)', tune_source: 'full', tune_control: true, tune_clock_mhz: 1854, sweep_pct: 100, sweep_watts: 227, sweep_at: t - 7200 },
|
||||
{ ...recState.mining.cards[0], key: 'intel:Intel(R) Arc(TM) B580 Graphics', name: 'Intel(R) Arc(TM) B580 Graphics', code: 'Intel(R) Arc(TM) B580 Graphics', platform: 'Intel(R) OpenCL Graphics (OpenCL 3.0 ), driver 32.0.101.9034', kind: 'discrete', vendor: 'intel', worker: 'OpenCL', detail: '160 compute units', vram_mb: 12208, device: '1', bus: '05:00.0', identities: 8, ids: ['c0ffee01', 'c0ffee02'], enabled: true, state: 'mining', hash_now: 15.2, hash_avg: 15.0, accepted: 3, rejected: 0, power_default_w: 0, power_pct: 0, power_applied: false, power_w: 0, temp_gpu: 0, temp_mem: 0, eff_mhw: 0, sweep_supported: false, sweep_state: 'unsupported', sweep_note: 'not available: Intel Arc exposes no power cap or reading through OpenCL (the driver\'s IGCL counters come next)', tune_line: '', tune_source: '', tune_control: false, driver: '32.0.101.9034' }];
|
||||
s.mining.hash_total = 142.9; s.display_name = 'pc-1';
|
||||
}
|
||||
if (scenario === 'nvidia') {
|
||||
s.mining.cards = [{ ...recState.mining.cards[0], key: 'nvidia:0:NVIDIA GeForce RTX 5090', name: 'NVIDIA GeForce RTX 5090', kind: 'discrete', vendor: 'nvidia', worker: 'CUDA', detail: '21760 cores', vram_mb: 32768, device: '0', identities: 8, ids: ['8fafda27', '1b2c3d4e', '5f6a7b8c'], power_default_w: 575, power_pct: 80, power_applied: true, power_limit_w: 460, power_w: 441, temp_gpu: 71, temp_mem: 92, telemetry_at: t, hash_now: 412.5, hash_avg: 405.2 },
|
||||
{ ...recState.mining.cards[0], key: 'nvidia:1:NVIDIA GeForce RTX 4070', name: 'NVIDIA GeForce RTX 4070', kind: 'discrete', vendor: 'nvidia', worker: 'CUDA', detail: '5888 cores', vram_mb: 12288, device: '1', identities: 2, ids: ['9a8b7c6d'], power_default_w: 200, power_pct: 80, power_applied: false, power_note: 'nvidia-smi: wanted 160 W, card reports 200 W', power_limit_w: 200, power_w: 188, temp_gpu: 64, temp_mem: 0, telemetry_at: t, hash_now: 98.1, hash_avg: 97.4, state: 'restarting', restart_in_s: 4 },
|
||||
|
|
|
|||
Loading…
Reference in a new issue