AMD and Intel energy on class v6 (GPU-03 AMD and Intel cells, ECO-05 named cause): per 32 bits of bus the AMD and Intel cards burn the 5090s watts but do 0.21x to 0.47x its random reads; the window costs the RX 7600 and the Arc B580 no rate per unit of work; the B580 class v6 rows (11.01 MH/s, fingerprints equal, watts unreadable unelevated); hash-identical kernel candidates ranked (0 to about 10 percent); the RX 7600 metered knee owed to a queued PC 1 job
Documents only: docs/analysis/class-v6/amd-intel-energy.md and docs/analysis/class-v6/amd-intel-energy/ (the three job scripts, the Level Zero sampler source, the offline-compile prelude and ISA summary). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Documents-only replay ofc9e32b256(c9e32b256a) for the box mirror master
This commit is contained in:
parent
0b9cf9be46
commit
c1b7426e73
7 changed files with 622 additions and 0 deletions
280
docs/analysis/class-v6/amd-intel-energy.md
Normal file
280
docs/analysis/class-v6/amd-intel-energy.md
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
# AMD and Intel energy on class v6: why the measured cards pay 3x to 6x a Blackwell card per hash, and what a kernel can change
|
||||
|
||||
8 October 2026, written 19:4x to 20:3x UK, the AMD-and-Intel energy lane. Register rows: GPU-03 (the 64-register
|
||||
window's real cost) in its AMD and Intel cells; ECO-05's named cause (`docs/analysis/class-v6/eco-05-results.md` on
|
||||
counter-asic-4: "the measured RX 9070 XT (7.9 microjoules at its knee), the RX 7600 (6.2 estimated, 8.1 measured at stock)
|
||||
and the Arc B580 (10.4, watts estimated) cost 4x to 6x a Blackwell card per joule"); the P02 cohort's AMD and Intel cells.
|
||||
The one acceptance rule is P03: a change must cost the honest card no more than 5 percent per accepted work against the
|
||||
paired baseline (and no more than 2 percent of accepted throughput) and must not help the adversary.
|
||||
|
||||
**Labels.** Every number carries one: **measured** (an instrument read it; the instrument is named), **modelled**
|
||||
(arithmetic on labelled inputs, or an offline compiler's report standing in for the driver's), **estimated** (no
|
||||
instrument behind it), **team-reported** (read by a person, not a job). Vendor and JEDEC figures are marked
|
||||
approximate. No row here is a wall reading: neither PC has a wall meter, so every watt is the device's own telemetry
|
||||
(ADLX `GPUPower` on AMD, `nvidia-smi power.draw` on NVIDIA, the Level Zero sysman energy counter on Intel, IOReport on
|
||||
Apple), and none meets P02's calibrated 2 percent wall meter. **Efficiency kind** (review B's rule, main's order 8 October
|
||||
2026): every efficiency row says which of two things it is, and the two are never mixed: **KT**, kernel throughput (the
|
||||
worker's `--bench-pack` or `--memprobe`, device event time, no pool, no shares) against device-reported watts; **SV**, the
|
||||
serving hash rate (the installed app's own `hash_now` for the card while it mines) against device-reported watts. No row
|
||||
here is the third kind, **end-to-end accepted work per wall joule in serving mode**: that needs accepted shares and a wall
|
||||
meter, and is owed (section 4.3). Nothing was built or run on the Mac: the offline compiles
|
||||
ran on igneum-build-1 (LLVM 18.1.3), the jobs on PC 1 and PC 2 through the signed jobs queue.
|
||||
|
||||
## 0. The answer in one paragraph
|
||||
|
||||
On this hash every card runs at 87 to 100 percent of its own dependent random-read ceiling divided by 128 (the unit of
|
||||
work is 128 dependent random 4-byte reads), so joules per hash = 128 x watts / random reads per second. Normalised per
|
||||
32 bits of memory bus, the AMD and Intel cards burn about what the RTX 5090 burns (18 to 19 W per 32 bits at their
|
||||
knees; the RX 7600 28 W at stock) but complete 0.21x to 0.47x its random reads per second (the 5090 1.08 G/s per 32
|
||||
bits; the 9070 XT 0.30; the 7600 0.44 to 0.51; the B580 0.23). That ratio is the whole gap: 3.4x for the 9070 XT, 3.1x
|
||||
to 3.5x for the 7600, 4.4x for the B580 against the 5090's floor, and 3.8x to 6.1x against the 5080 and the 5070 Ti,
|
||||
which is ECO-05's "4x to 6x". It sits in the memory system's random-access rate (GDDR7's four channels per device
|
||||
against GDDR6's two, and a per-channel rate that varies 2.2x between the three GDDR6 cards), not in the kernel: the
|
||||
ALU work is about 5 percent of an AMD card's joules, occupancy is 3x to 6x above what saturates the DRAM, the
|
||||
64-register window costs the RX 7600 and the Arc B580 no rate per unit of work (0 and -0.3 percent, measured), and a 16-byte read costs what a 4-byte read costs on every
|
||||
card measured. The kernel changes that keep every hash identical are worth 0 to about 10 percent on AMD, ranked in
|
||||
section 3; none closes the gap, and the one hash change that does (64-byte reads) halves the 5090 and fails P03.
|
||||
The levers that move ECO-05 tonight are the AMD operating point below the driver's floor (a baseline, not a candidate)
|
||||
and the RX 7600's metered knee (a PC 1 job queued at landing; section 4), which ECO-05 says adds sustainable worlds
|
||||
at 0.03 and 0.10 if it reads under 5 microjoules (modelled tonight at 5.0 to 5.4).
|
||||
|
||||
## 1. The measured rows as they stand, with their instruments
|
||||
|
||||
### 1.1 Rate and watts
|
||||
|
||||
| Card | Class / point | MH/s (rate instrument) | Watts (watts instrument) | Microjoules per hash | Label | Kind | Source |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| RX 9070 XT (gfx1201, 16 GB GDDR6, 256-bit, PC 1 eGPU) | class v2, stock, app mining | 17.73 (the app's hash_now, mean of 24) | 198.9 (ADLX GPUPower, 12 samples, 193 to 212) | 11.2 | measured | SV | docs/bench-log.md, 5 Oct, job tele-measure-1 |
|
||||
| RX 9070 XT | class v5, stock (grid point 0/0) | 18.9 (the app's hash_now, median over a 75 s hold) | 202 (ADLX GPUPower, mean after a 30 s settle) | 10.7 | measured | SV | the ADLX grid, 8 Oct 12:13, `relay/playbooks/ca3-pc1-amd-grid.ps1`; floor/denominator.md |
|
||||
| RX 9070 XT | class v5, gmax -500 MHz, plimit -30 percent (the driver's floor; the grid's best of 24) | 18.9 to 19.0 (same) | 149.3 (same) | 7.9 | measured | KT | same |
|
||||
| RX 7600 (gfx1102, 8 GB GDDR6, 128-bit, PC 1) | class v4 sub-version 3, stock | 13.88 (card-in job, worker bench) | 113 (ADLX, card-in job) | 8.14 | measured | KT | floor/denominator.md, 15:50 UK |
|
||||
| RX 7600 | class v6 hl-v6-foldrw (the partner), stock, quiet | 15.79 (worker --bench-pack) | none | | measured rate only | KT | hash lane, 18:3x UK |
|
||||
| RX 7600 | class v6 hl-v6-all (window + fold + rw; 256 loads per hash = two units of work), stock, quiet | 7.92 = **15.84 per unit of work** | none | | measured rate only | KT | hash lane, 18:3x UK |
|
||||
| RX 7600 | class v6 sizes 1 / 2 / 4 / 5.5 GiB | 15.75 / 15.46 / 15.34 / 15.35 | none | | measured rate only | KT | reference-population.md section 6 |
|
||||
| RX 7600 | class v6 at stock with the class v4 run's 113 W | 15.79 | 113 (a different class's reading) | **7.2** | modelled | KT (modelled) | this file |
|
||||
| RX 7600 | knee | 13.9 | 86 (the 9070 XT's 24 percent applied) | 6.19 | estimated | KT (modelled) | reference-population.md |
|
||||
| Arc B580 (12 GB GDDR6, 192-bit, PC 2 eGPU and a PC 1 slot) | class v4, stock | 10.6 to 11.0 (worker bench) | about 110 (the board's class) | 10.4 | measured rate, estimated watts | KT | floor/denominator.md; the Intel lane, 7 Oct |
|
||||
| Arc B580 (PC 2, enclosure) | class v6 hl-v6-foldrw, stock, quiet (SIMD32, sub-group shuffle exchange) | 11.008 (worker --bench-pack, 60 dispatches of 2^24, device time) | not read: the Level Zero energy counter answered ZE_RESULT_ERROR_UNSUPPORTED_FEATURE (0x78000003) on all three power domains, unelevated, driver 32.0.101.6733 | | measured rate only | KT | job run-ae-pc2-b580-energy-20261008, 18:52Z |
|
||||
| Arc B580 | class v6 hl-v6-all, stock, quiet (the compiler drops to SIMD16: sub-group 16, local-memory exchange with barriers) | 5.489 = **10.98 per unit of work** | not read (same) | | measured rate only | KT | same, 18:54Z |
|
||||
| Arc B580 | class v6 at stock with the board-class watts | 11.0 | about 110 | 10.0 | measured rate, estimated watts | KT | this file |
|
||||
| RTX 5090 (32 GB GDDR7, 512-bit, PC 1) | class v5, the 1,300 MHz lock | 134.8 (worker) | 314 (nvidia-smi; 2.33 x 134.8) | 2.33 | measured | KT | reference-population.md (PC 1, tiers file) |
|
||||
| RTX 5080 (16 GB GDDR7, 256-bit) | class v5, 1,100 MHz | 71.2 | 146.6 | 2.06 | measured (rented) | KT | reference-population.md |
|
||||
| RTX 5070 Ti (16 GB GDDR7, 256-bit) | class v5 knee | 77.0 | 131 | 1.70 | stock measured (rented), knee modelled | KT | reference-population.md |
|
||||
| Apple M5 Max (36 GB LPDDR5X) | class v4 | 26.67 | 37.3 (IOReport GPU and DRAM channels) | 1.40 | measured | KT | floor/denominator.md |
|
||||
|
||||
Tonight's jobs (section 4.1) added the B580's class v6 rows; its watts could not be read unelevated. The RX 7600's metered
|
||||
class v6 rows (ADLX at stock and two knob points) are owed to a PC 1 job still queued at landing.
|
||||
|
||||
### 1.2 The random-read ceiling (the instrument that explains the rows)
|
||||
|
||||
`igneum-worker-opencl --memprobe` (proto-opencl/host.c): dependent random 4-byte loads over a buffer, device event
|
||||
time, best over lanes in flight; `chase` is one dependent load per step per lane.
|
||||
|
||||
| Card | Ceiling at 1024 MiB, G loads/s | Hash-implied (MH/s x 128) | Hash / ceiling | 64-byte read against 4-byte | Label | Source |
|
||||
|---|---|---|---|---|---|---|
|
||||
| RTX 5090 | 17.5 to 18.2 (card off in the app, CUDA) | 17.25 | 0.96 | 0.52x to 0.86x (two 32-byte sectors; bandwidth-bound at 584 GB/s) | measured | bench-log, read-width entry, 5 Oct |
|
||||
| RX 9070 XT | 2.42 to 2.66 (4,096 lanes already saturate it; eight independent chains per lane give the same) | 2.42 | 0.87 to 0.95 | 1.0x (2.47 to 2.87: the line is fetched either way) | measured | bench-log, 5 Oct |
|
||||
| RX 7600 | owed (the PC 1 job of section 4.1) | 2.02 (class v6) | | | hash-implied, modelled | |
|
||||
| Arc B580 | 1.407 to 1.409 (4,096 lanes and up; eight independent chains per lane 1.41, the same); at 256 MiB 1.55 to 1.57 | 1.409 (class v6) | 1.00 | not probed | measured | job run-ae-pc2-b580-energy-20261008 (tonight); the Intel lane 7 Oct read 1.41 |
|
||||
| Apple M5 Max | 3.50 | 3.47 | 1.0 | 1.0x | measured | bench-log, 5 Oct |
|
||||
|
||||
## 2. The decomposition
|
||||
|
||||
### 2.1 The identity
|
||||
|
||||
The unit of work is 128 dependent random 4-byte dataset reads (program.json of both class v6 packs:
|
||||
`loads_per_hash` 128, `load_width_counts_4_16_64` [16, 0, 0], `bytes_per_hash` 512; hl-v6-all does two units per hash).
|
||||
Every card above runs at 0.87 to 1.0 of its probe ceiling / 128 (table 1.2), so:
|
||||
|
||||
microjoules per unit = 128 x P / A, with P the card's watts at its operating point and A its random reads per second.
|
||||
|
||||
Splitting both by the memory bus (32-bit units: the 5090 16, the 9070 XT 8, the 7600 4, the B580 6, the 5080 and 5070 Ti
|
||||
8; vendor figures, approximate):
|
||||
|
||||
| Card | A per 32 bits (G reads/s) | P per 32 bits (W) | Nanojoules per random read (P / A) | Microjoules per hash | Against the 5090: watts ratio x reads ratio = joules ratio | Label |
|
||||
|---|---|---|---|---|---|---|
|
||||
| RTX 5090 at the lock | 1.08 | 19.6 | 18.2 | 2.33 | 1 | A measured, P measured |
|
||||
| RTX 5080 at 1,100 MHz | 1.14 | 18.3 | 16.1 | 2.06 | 0.94 x 0.95 = 0.88 | measured (rented) |
|
||||
| RX 9070 XT at the floor point | 0.30 | 18.7 | 61.7 | 7.9 | 0.95 x 3.6 = 3.4 | measured |
|
||||
| RX 9070 XT at stock | 0.30 | 25.3 | 84 | 10.7 | 1.29 x 3.6 = 4.6 | measured |
|
||||
| RX 7600 at stock, class v4 run | 0.44 | 28.3 | 63.5 | 8.14 | 1.44 x 2.4 = 3.5 | measured |
|
||||
| RX 7600 at stock, class v6 rate | 0.51 | 28.3 | 56 | 7.2 | 1.44 x 2.1 = 3.1 | modelled (watts from the v4 run) |
|
||||
| Arc B580 at stock | 0.23 | 18.3 | 78 to 80 | 10.4 | 0.93 x 4.7 = 4.4 | A measured, P estimated |
|
||||
| Apple M5 Max (for contrast) | 0.22 | 2.3 | 10.7 | 1.40 | 0.12 x 4.9 = 0.60 | measured |
|
||||
|
||||
Reading: at their knees the AMD and Intel cards spend about the same watts per 32 bits of memory bus as the 5090
|
||||
(0.93x to 0.95x; the 7600 at stock and the 9070 XT at stock 1.3x to 1.4x because nothing has been taken off them), and
|
||||
the whole 3x to 4.5x is the random-read rate per 32 bits. Against the 5080 and the 5070 Ti (1.70 to 2.06) the same rows
|
||||
read 3.8x to 6.1x, which is the "4x to 6x" of ECO-05. The Mac shows the other way out: a memory system with a quarter of
|
||||
the 5090's random-read rate per bus width wins on joules because it spends an eighth of the watts per bus width.
|
||||
|
||||
### 2.2 The random-read rate per 32 bits: where the 3.6x lives
|
||||
|
||||
| Factor | 5090 against the 9070 XT | Label |
|
||||
|---|---|---|
|
||||
| Channels per 32-bit device: GDDR7 four, GDDR6 two | 2x | JEDEC device organisation, approximate |
|
||||
| Random reads per channel per second: 5090 about 270 M, 9070 XT about 150 M (the per-channel figure of floor/denominator.md) | 1.8x | modelled from the measured ceilings and the channel counts |
|
||||
| Product | 3.6x | matches the measured 1.08 / 0.30 |
|
||||
|
||||
Within GDDR6 the per-channel rate varies 2.2x on the same DRAM family: the RX 7600 about 220 to 250 M per channel per
|
||||
second (8 channels; hash-implied, modelled), the 9070 XT about 150 M (16 channels; measured ceiling), the B580 about 115
|
||||
M (12 channels; measured ceiling). The 7600's rate says GDDR6 itself allows 1.5x to 2.2x more random reads per channel
|
||||
than the 9070 XT and the B580 obtain, so part of their deficit sits above the DRAM, in the memory controller, the fabric,
|
||||
the last-level cache path or address translation. That part is the only piece of the gap that software might reach
|
||||
(section 3, candidate 2); the GDDR7 against GDDR6 part is hardware. Tonight's B580 probe bounds the translation share
|
||||
on Xe2: its ceiling is 1.55 to 1.57 G/s at 256 MiB and 1.41 at 1024 MiB (measured), both far beyond its 18 MB L2, so
|
||||
the footprint (page reach) costs it about 10 percent and the other 90 percent of its per-channel shortfall is the memory
|
||||
controller and fabric's.
|
||||
|
||||
Bandwidth is not the bound on the AMD and Intel cards: the 9070 XT moves 2.42 G x 64 bytes = 155 GB/s, 24 percent of its
|
||||
640 GB/s rating, and a 64-byte read costs it exactly what a 4-byte read costs (measured); the bound is the rate of random
|
||||
accesses, each opening a DRAM row. The 5090 at 64 bytes is the opposite case (it becomes bandwidth-bound and halves).
|
||||
|
||||
### 2.3 The kernel's memory path on RDNA 3, RDNA 4 and Xe2
|
||||
|
||||
Instrument: the class v6 OpenCL texts (`kernel.cl` of hl-v6-all, id 0x9d40978601a7df2a, and hl-v6-foldrw, id
|
||||
0x605d06cabc489f94, from build-1 `/srv/artefacts/packs/`) compiled offline with clang 18.1.3 for amdgcn-amd-amdhsa,
|
||||
-O3, OpenCL C 1.2, `IGNEUM_EXCHANGE 0` (the local-memory path the driver takes on the 9070 XT), the work-item and
|
||||
rotate built-ins shimmed to the target's own built-ins (no libclc on the box). The driver's compiler is AMD's own LLVM
|
||||
(the PAL,LC stack) of a different version, so these are **modelled** stand-ins for the driver's binary; the driver's own
|
||||
numbers come from the host's kernel line (private memory, local memory, sub-group) and, for the 9070 XT, the 5 October
|
||||
measurement (wave32, private memory 0).
|
||||
|
||||
| Quantity | hl-v6-all, gfx1102 | hl-v6-all, gfx1201 | hl-v6-foldrw, gfx1102 | hl-v6-foldrw, gfx1201 | Label |
|
||||
|---|---|---|---|---|---|
|
||||
| Wave size | 32 | 32 | 32 | 32 | modelled; the driver reports wave32 on gfx1201 (measured, 5 Oct) |
|
||||
| VGPRs | 160 | 160 | 96 | 96 | modelled |
|
||||
| Waves per SIMD (LLVM 18's model) | 6 | 6 (LLVM 18 models 1,024 VGPRs for gfx1201; its gfx1100 model gives 9 at 156) | 10 | 10 | modelled |
|
||||
| Spills (scratch bytes) | 0 | 0 | 0 | 0 | modelled; the driver reports private memory 0 on the 9070 XT (measured, class v2) |
|
||||
| LDS per work-group | 256 B (the exchange's two buffers) | 256 B | 256 B | 256 B | modelled |
|
||||
| Barriers emitted | 0 (work-group = one wave32: elided) | 0 | 0 | 0 | modelled; matches WAVEFRONT.md's measured "barriers elided" |
|
||||
| Global loads in the loop body | 32 x global_load_b32 | 32 | 16 x global_load_b32 | 16 | modelled |
|
||||
| Loads with a full wait right after them | all 32 (51 vmcnt(0) waits) | all 32 | 13 of 16 (three overlap) | | modelled |
|
||||
| Hash-kernel code size | 33.6 KB | 34.0 KB | 6.3 KB | 6.6 KB | modelled |
|
||||
| Integer multiplies (mul_lo, mad, mul_hi) per body | 99 | 99 | 67 | 67 | modelled |
|
||||
|
||||
The five questions the brief asks, answered on these numbers:
|
||||
|
||||
1. **Wave size.** Wave32 on RDNA 3 and 4 (the compiler's choice and the driver's report); a work-group of 32 is one wave,
|
||||
so the local-memory exchange compiles to LDS writes and reads with no barrier. The exchange path and the work-group
|
||||
shape cost nothing on the 9070 XT (`--group-warps 1, 2, 4, 8` = 18.02 to 18.07 MH/s, measured 5 Oct). Xe2: the
|
||||
kernel takes the khr or Intel sub-group shuffle only at a queried sub-group of exactly 32; the Arc's sub-group and
|
||||
SIMD width under the 64-register window, measured tonight on the B580 (the host's kernel line, job
|
||||
run-ae-pc2-b580-energy-20261008): the partner hl-v6-foldrw compiles at sub-group 32 and takes `sub_group_shuffle_xor`;
|
||||
the window pack hl-v6-all drops to sub-group 16 (SIMD16, the Intel compiler's answer to 64 live registers), so the
|
||||
host takes the local-memory exchange with barriers (256 bytes); private memory 0 in both (no spill). The cost of that
|
||||
drop: none in rate (10.98 MH/s per unit of work against 11.01, -0.3 percent, measured), because the card is at 100
|
||||
percent of its random-read ceiling either way.
|
||||
2. **The 16-byte read's coalescing.** Class v6 reads 4 bytes per load (program.json above); there is no 16-byte read in
|
||||
the frozen object. The read-width experiment's 16-byte loads (w16) cost every card what 4 bytes cost (5090 139.8
|
||||
against 136.1 MH/s, 9070 XT 17.90 against 18.15, measured 5 Oct): a lane's random read fetches a whole line (64 bytes
|
||||
on AMD and Apple, a 32-byte sector on NVIDIA) and the 32 lanes of a wave hit 32 different lines (a 1 GiB dataset has
|
||||
16.8 M 64-byte lines; two lanes of a wave share one with probability about 3 x 10^-5), so there is nothing to
|
||||
coalesce.
|
||||
3. **LDS use.** 256 bytes per wave, only the exchange: 8 exchanges per iteration in the body of hl-v6-all, 4 in
|
||||
hl-v6-foldrw, plus 13 per shadow pass x 27 passes; about 2,840 LDS write-read pairs per unit of work. At a few
|
||||
picojoules per lane-access that is a few nanojoules per hash against 7,900 (modelled, under 0.1 percent).
|
||||
4. **The dependent-load chain's latency hiding.** Under the full chain every load's address mixes all 64 registers,
|
||||
including the previous load's result, so each wave has one load in flight (all 32 loads wait vmcnt(0)). It does not
|
||||
matter on these cards: 4,096 lanes in flight already saturate the 9070 XT's DRAM (2.64 G/s at 4,096 lanes, measured),
|
||||
and occupancy 6 on 64 CUs holds 24,576 lanes (the 7600 at 32 CUs: 12,288). The latency is hidden by waves, not by
|
||||
loads in flight within a wave, with 3x to 6x to spare.
|
||||
5. **Occupancy under the 64-register window.** 160 VGPRs against 96: 6 waves per SIMD against 10 (modelled), no spill.
|
||||
Measured consequence on the 7600: none in rate (15.84 per unit of work against 15.79). On NVIDIA the hash lane
|
||||
measured the window at 96/88 registers (5090) and 104/87 (4090), occupancy 67 to 83 percent, within 5 percent per
|
||||
load. **GPU-03's AMD cell, rate: 0 percent per unit of work on the RX 7600 (measured, quiet, rate only); energy:
|
||||
section 4. GPU-03's Intel cell, rate: -0.3 percent per unit of work on the Arc B580 (measured, quiet, rate only),
|
||||
with the class v6 fingerprints equal to the CUDA and Metal references on both packs (5a6ad122a71a888f and
|
||||
59e6708e46f1e87c, self-test PASS): the B580 computes class v6 bit-exact.**
|
||||
|
||||
### 2.4 Where an AMD card's joules go (modelled split at the 9070 XT's floor point, 149.3 W, 18.9 MH/s)
|
||||
|
||||
| Term | Watts | Basis | Label |
|
||||
|---|---|---|---|
|
||||
| ALU work | 6 to 12 | 55.3 k lane-ops per unit of work for the partner (8 iterations x (64 + 27 x 256 shadow ops)), 42 k for the window pack (its shadow covers two units); at 5 to 10 pJ per lane-op (the M5 Max's measured marginal 6.9 pJ per counted op as the scale); 17 percent of the card's measured 6.2 T int op/s chain throughput in use | modelled |
|
||||
| DRAM array and I/O for 2.42 G random 64-byte reads per second | 10 to 20 | 4 to 8 nJ per activate-read-transfer of a 64-byte burst (public GDDR6 figures, approximate) | modelled |
|
||||
| LDS exchange | under 0.2 | section 2.3 | modelled |
|
||||
| The rest: the die and board kept awake at full memory data rate (clock trees, fabric and last-level cache, memory PHY, VRM losses, fans) | about 120 | the remainder | modelled |
|
||||
|
||||
The kernel's own work (ALU, LDS, the loads it issues) is 15 to 30 W of 149; the remainder is the price of a whole
|
||||
graphics card serving 8 or 16 GDDR6 channels' worth of random reads. That is why the knobs that take the core down
|
||||
without touching memory (the 9070 XT's grid: 24 points, the rate flat at 18.9 MH/s, measured) are worth more than any
|
||||
kernel text, and why they stop at the driver's floor (-500 MHz, -30 percent), not at the hash's.
|
||||
|
||||
## 3. Candidate kernel changes that keep every hash identical, ranked
|
||||
|
||||
Common to all: none changes the hash function, so none changes the adversary's cost (the adversary's chip computes the
|
||||
function, not our kernel), and none can help the adversary; each moves only the GPU side. The equality proof for each:
|
||||
(a) the pack's self-test on the changed kernel (cache head and FNV, dataset samples, 96 of 96 vector lanes), (b) the 2^24
|
||||
batch fingerprint at base nonce 0 equal to the unchanged kernel's and to the CUDA and Metal references on the same pack
|
||||
(hl-v6-foldrw 5a6ad122a71a888f, hl-v6-all 59e6708e46f1e87c on the pre-review export; the re-export's pair when it lands),
|
||||
on every platform the change ships to, (c) the P01 vector file (`igneum-pow hash-bound --count 1000000`, staged at
|
||||
build-1 `/srv/artefacts/packs/p01-vectors/`) through the changed kernel. P03 is then a paired energy and rate run on
|
||||
every mandatory cell the change touches (an OpenCL-only change touches no CUDA card). No candidate reduces the
|
||||
specialist's advantage, so none is a G2 upgrade: they are efficiency work on the baseline.
|
||||
|
||||
| Rank | Change | Where | Expected gain (all modelled) | The test that proves equality | Risk under P03 | Clock |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 0 (a baseline, not a candidate) | The AMD operating point below the driver's floor: a lower absolute core clock or a voltage offset where ADLX exposes one (RDNA 3 and 4 Adrenalin carry a voltage offset; ADLX's manual tuning interface for it is unverified here) | the Ember tune's AMD path, not the kernel | the ALU needs about 17 percent of the 9070 XT's shader throughput and about 30 percent of the 7600's (muls at a quarter rate), so the core can lose half its clock before the rate moves: 149 W to 110 to 125 W on the 9070 XT, 7.9 to 5.8 to 6.6 microjoules; the 7600 similar in proportion | clocks never change outputs; the grid's own per-point self-test and fingerprint | none on correctness; P03 places clock savings in the baseline, so it lowers the AMD baseline (ECO-05's input) and scores nothing as a candidate | the update-return lane's AMD knob; a read of ADLX's absolute and voltage interfaces by 12:00 tomorrow |
|
||||
| 1 | Occupancy throttle: launch only the lanes that saturate the DRAM (about 4,096 to 8,192 on the 9070 XT, measured) on a fraction of the CUs, so idle CUs clock-gate and the driver's power manager sees a partly idle die | host (a `--max-groups` cap on the dispatch, about 20 lines in host.c; the persistent-warp loop already exists for variant-5 packs) | 0 to 10 percent of the card's watts, unknown until measured; the ALU budget bounds the throttle at about a quarter of the 9070 XT's CUs and half the 7600's | per-nonce outputs do not depend on the launch shape (WAVEFRONT.md: work-groups of 32 to 256 bit-exact; the 2^24 fingerprint at every cap) | a cap below the ALU need loses rate; the sweep finds the knee | host flag and a sweep job: tomorrow 15:00 |
|
||||
| 2 | Find the GDDR6 per-channel shortfall above the DRAM (the 9070 XT and the B580 at 0.45x to 0.65x the 7600's per-channel rate) and remove it if it is software's: page size and TLB reach of the 1 GiB dataset buffer, the buffer's placement, the driver's allocation flags | host allocation and driver flags | on the B580 at most about 10 percent (its probe falls 10 percent from 256 to 1024 MiB, measured tonight: that is the translation share); the 9070 XT unprobed at two sizes, the same bound expected (modelled) | allocation never changes outputs; the fingerprint | none on correctness; a different allocation could cost the NVIDIA path nothing because it would be vendor-gated | B580 read tonight (10 percent); the 9070 XT's two-size probe when it is back in the housing |
|
||||
| 3 | Non-temporal dataset loads on AMD (`__builtin_nontemporal_load` in the OpenCL text, which AMD's LLVM lowers to the non-temporal bit on RDNA 3 and the TH_LOAD_NT hint on RDNA 4): no last-level-cache or L2 allocation for lines that are not reused | OpenCL text, AMD only | 0 to 3 percent of the watts (no 64-byte fill into the 32 or 64 MB cache per read); risk of losing the 3 to 6 percent of reads that hit that cache (0 to -3 percent rate) | the hint changes no value; the fingerprint and the vectors | the rate risk; paired run decides | an emitter flag and a PC 1 pair: tomorrow 15:00 |
|
||||
| 4 | The window's address mix computed incrementally: m is linear over GF(2) in the 64 registers, so m(src) = A xor T_src xor P xor rotr(P, 1), with T_k = rotl(r_k, 63 - k), A the xor of all T_k kept up to date on each register write (two ops per write) and P the xor of T_k below src; the compiler today emits about 108 ops per load (52 rotates, 54 xors, 2 xor3) | emitter, all three dialects | removes about 20 to 25 percent of the window pack's ALU ops per unit of work, so 1 to 2 percent of an AMD card's joules and less on NVIDIA (its ALU share is smaller still) | the identity is exact; a property test in igneum-pow over random register files and every src, then the fingerprint and vectors | none expected; it must not move the 5090 or 4090 window rows by more than noise | an emitter change on the hash lane's line, after the post-review freeze: days two and three |
|
||||
| 5 | The exchange through DPP or ds_swizzle instead of LDS (RDNA), sub-group shuffles on Intel | OpenCL text, vendor-gated | under 0.1 percent (section 2.3, item 3) | the fingerprint across the exchange paths (WAVEFRONT.md's emulator table already proves path equality) | none | not worth a slot; recorded to close the question |
|
||||
|
||||
Not a candidate (changes the hash; recorded so it is not proposed again): reads of 64 bytes per load (w64) close the
|
||||
5090 against 9070 XT gap from 7.5x to 4.1x in rate, entirely by halving the 5090 (measured, 5 Oct), so the honest NVIDIA
|
||||
cells lose about 50 percent of accepted throughput against P03's 2 percent: out. W = 8 and W = 32 are standing rejected
|
||||
knobs. The hot table and the scratch read-modify-write cost the 9070 XT 13 to 33 percent (measured): out.
|
||||
|
||||
What this means for the vendor question: no kernel change that keeps the hash identical is worth more than about 10
|
||||
percent on AMD (candidate 1) plus whatever candidate 2 finds above the DRAM; the 3x to 4.5x is GDDR7 against GDDR6 and a
|
||||
graphics card's fixed power over few channels. Closing it by changing the hash would cost the honest NVIDIA cards more
|
||||
than P03 allows, which the standing rule forbids ("no ratio is bought with honest GPU energy").
|
||||
|
||||
## 4. Rows for the 21:00 economics landing and the P02 cohort, with clocks
|
||||
|
||||
### 4.1 Tonight's two jobs
|
||||
|
||||
| Job | Machine | What it reads | Instrument | Status and clock |
|
||||
|---|---|---|---|---|
|
||||
| run-ae-pc1-7600-energy-20261008 | PC 1 (ae432dc7), RX 7600 | memprobe at 1024 MiB; hl-v6-foldrw and hl-v6-all as the load at stock, plimit -30, gmax -500 with plimit -30; reset and read-back | the kit worker's bench (rate), ADLX through igneum-gpu-telemetry.exe every 5 s, samples after 20 s (watts) | published 19:41 UK behind the hash lane's ds2g and l8off; at 20:1x UK PC 1's runner was still held by the withdrawn overnight 7600 grid job (its last upload 19:06 UTC), so none of the three had started; the follow-up reset job run-ae-pc1-7600-reset-20261008 is queued behind it |
|
||||
| fetch-ae-ze-power-20261008 + run-ae-pc2-b580-energy-20261008 | PC 2 (1ccfe586), Arc B580 | memprobe at 256 and 1024 MiB; both v6 packs at stock | the kit worker's bench (rate), ze-power.exe (Level Zero sysman energy counters, read only, built on build-1 with mingw, sha256 354abddde13a5341521eb298a5728e65675af7474823faf9b8d5613caf840d8f) | ran 19:50 to 19:54 UK (268 s, done): rates, probes and fingerprints read; the energy counter unsupported unelevated (rc 0x78000003 on all three domains, the device seen as `Intel(R) Graphics [0xe20b]`) |
|
||||
|
||||
|
||||
### 4.2 The rows, each with its clock
|
||||
|
||||
| # | Row | For | Value and label | Clock |
|
||||
|---|---|---|---|---|
|
||||
| 1 | The RX 7600's class v6 rate replaces the class v4 card-in rate in the reference population | the research lane's 21:00 landing | 15.79 MH/s (measured, quiet, rate only); the stock cell 7.2 microjoules (modelled on the v4 run's 113 W) until row 2 | now |
|
||||
| 2 | The RX 7600 metered at stock and at two knob points on class v6 | the 21:00 landing; ECO-05's section 5 condition (a 7600 knee under 5 microjoules adds the worlds at 0.03 and 0.10) | OWED: the PC 1 runner had not reached the job at landing; until it reports, the cells stay 7.2 at stock (modelled on the class v4 run's 113 W) and 5.0 to 5.4 at the floor point (modelled: the 9070 XT's 24 percent, or a flat rate at -30 percent power) | when PC 1's runner reaches it, about 20 minutes after the hash lane's ds2g and l8off; the rows go to the research lane's ECO-05 batch 02 the minute they land |
|
||||
| 3 | The Arc B580 on class v6 | the 21:00 landing; ECO-05's section 5 condition (a B580 under 8 adds worlds) | rate 11.01 MH/s (measured, KT); watts NOT READ: the Level Zero sysman energy counter answers unsupported (0x78000003) unelevated on driver 32.0.101.6733, so the cell stays about 110 W estimated, 10.0 microjoules (estimated); under 8 would need under 88 W | rate landed 19:54 UK; watts owed: an unelevated IGCL telemetry read (unverified) or a wall meter, tomorrow 15:00; an elevated read needs main's exception to the nothing-elevated rule |
|
||||
| 4 | GPU-03's AMD cell: the window's cost per unit of work on the RX 7600 | the register (row 11 and GPU-03) | rate 0 percent (15.84 against 15.79, measured, KT); energy OWED with row 2 (the same job reads both packs at each point) | rate landed; energy with row 2 |
|
||||
| 5 | GPU-03's Intel cell | the register | rate -0.3 percent per unit of work (measured, KT); SIMD16 under the window, no spill; fingerprints equal on both packs; energy not read | landed 19:54 UK |
|
||||
| 6 | The named cause of ECO-05's two-vendor failure | the 21:00 landing's text and eco-05-results.md | section 0 and 2.1 of this file (per 32 bits of bus: the same watts, 0.21x to 0.47x the random reads) | landed with this file |
|
||||
| 7 | The 9070 XT | the cohort | unchanged: 7.9 at the floor point (measured); no v6 row tonight (out of the housing since 14:2x) | when it is back on the bus |
|
||||
|
||||
### 4.3 The P02 cohort, AMD and Intel cells
|
||||
|
||||
| P02 requirement | State tonight | Owed |
|
||||
|---|---|---|
|
||||
| At least 3 AMD retail configurations | 2 measured (RX 9070 XT 16 GB, RX 7600 8 GB); the 9060 XT and 7600 XT are in the card-in queue, not on a PC | a third AMD card on PC 1; the hash lane's card-in job runs it as it arrives |
|
||||
| An advertised 8 GB mining tier | the RX 7600 8 GB (measured; the 5.5 GiB dataset fits at 6,732 MiB of 8,176) | |
|
||||
| Usable, not nominal, memory | the 7600's 8,176 MiB (measured) | the B580's and the 9070 XT's usable figure from the host's device line |
|
||||
| Every advertised vendor in the competitive core | Intel: the Arc B580 only; its class v6 rate and fingerprints measured tonight, its watts not readable unelevated | a second Intel configuration if Intel stays advertised |
|
||||
| Calibrated wall meter, at most 2 percent | none: every AMD and Intel watt is ADLX or Level Zero device telemetry | a wall meter on each PC (a purchase and a hand at the socket); until then every row reads "device-reported" |
|
||||
| 5 paired 30-minute runs per primary cell after 15 minutes of warm-up | none: tonight's points are about 1 minute each after 20 s | the paired protocol on PC 1 and PC 2 once the 2.0 devnet mines there (GPU-02 and GPU-03 under the standard) |
|
||||
| Three unaffiliated operators | none | the served kit (D1) |
|
||||
|
||||
## 5. Unverified and owed
|
||||
|
||||
- The offline compiles are LLVM 18 standing in for AMD's driver compiler; the driver's VGPR count and wave size on the
|
||||
7600 and the B580 come from tonight's host kernel lines.
|
||||
- The channel counts per device and the GDDR6 and GDDR7 energy per access are JEDEC and public figures, approximate.
|
||||
- The split of section 2.4 is modelled; only a per-rail reading (not available through ADLX) would measure it.
|
||||
- The B580's watts: the Level Zero sysman energy counter is unsupported unelevated on driver 32.0.101.6733 (measured
|
||||
tonight); the 110 W stays an estimate until an unelevated IGCL read, a wall meter, or main's exception for one
|
||||
elevated read.
|
||||
- The RX 7600's metered class v6 rows (job run-ae-pc1-7600-energy-20261008, queued on PC 1 behind a held runner).
|
||||
- The ALU energy per op on RDNA is taken at the M5 Max's measured scale, not measured on AMD.
|
||||
- Candidate 0's ADLX absolute-clock and voltage interfaces are unverified on the tool; candidates 1 to 4 are unmeasured.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// shims for an offline AMDGPU compile without libclc (analysis only; never a mining kernel)
|
||||
#define get_global_id(d) ((size_t)(__builtin_amdgcn_workgroup_id_x() * 32u + __builtin_amdgcn_workitem_id_x()))
|
||||
#define get_local_id(d) ((size_t)__builtin_amdgcn_workitem_id_x())
|
||||
#define get_global_size(d) ((size_t)(1u<<20))
|
||||
#define get_sub_group_size() 32u
|
||||
#define rotate(x, n) __builtin_rotateleft32((x), (n))
|
||||
#define mul_hi(a, b) ((uint)(((ulong)(a) * (ulong)(b)) >> 32))
|
||||
#define barrier(f) do { __builtin_amdgcn_fence(__ATOMIC_RELEASE, "workgroup"); __builtin_amdgcn_s_barrier(); __builtin_amdgcn_fence(__ATOMIC_ACQUIRE, "workgroup"); } while (0)
|
||||
#define vload4(o, p) (*(const __global uint4*)((p) + 4u*(o)))
|
||||
#define vstore4(v, o, p) (*(__global uint4*)((p) + 4u*(o)) = (v))
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
== hl-v6-all-gfx1102 (clang 18.1.3, -O3, OpenCL C 1.2, IGNEUM_EXCHANGE 0)
|
||||
; codeLenInByte = 33576
|
||||
; NumSgprs: 18
|
||||
; NumVgprs: 160
|
||||
; ScratchSize: 0
|
||||
; LDSByteSize: 256 bytes/workgroup (compile time only)
|
||||
; Occupancy: 6
|
||||
global_load_b32 32; s_barrier 0; full waits 51; VALU 4080; v_alignbit 1657; v_xor_b32 1724; v_xor3 72; int mul 99; ds_ 42; scratch 0
|
||||
== hl-v6-all-gfx1201 (clang 18.1.3, -O3, OpenCL C 1.2, IGNEUM_EXCHANGE 0)
|
||||
; codeLenInByte = 34004
|
||||
; NumSgprs: 18
|
||||
; NumVgprs: 160
|
||||
; ScratchSize: 0
|
||||
; LDSByteSize: 256 bytes/workgroup (compile time only)
|
||||
; Occupancy: 6
|
||||
global_load_b32 32; s_barrier 0; full waits 49; VALU 4077; v_alignbit 1657; v_xor_b32 1724; v_xor3 72; int mul 99; ds_ 42; scratch 0
|
||||
== hl-v6-foldrw-gfx1102 (clang 18.1.3, -O3, OpenCL C 1.2, IGNEUM_EXCHANGE 0)
|
||||
; codeLenInByte = 6268
|
||||
; NumSgprs: 20
|
||||
; NumVgprs: 96
|
||||
; ScratchSize: 0
|
||||
; LDSByteSize: 256 bytes/workgroup (compile time only)
|
||||
; Occupancy: 10
|
||||
global_load_b32 16; s_barrier 0; full waits 25; VALU 675; v_alignbit 84; v_xor_b32 104; v_xor3 20; int mul 67; ds_ 34; scratch 0
|
||||
== hl-v6-foldrw-gfx1201 (clang 18.1.3, -O3, OpenCL C 1.2, IGNEUM_EXCHANGE 0)
|
||||
; codeLenInByte = 6584
|
||||
; NumSgprs: 20
|
||||
; NumVgprs: 96
|
||||
; ScratchSize: 0
|
||||
; LDSByteSize: 256 bytes/workgroup (compile time only)
|
||||
; Occupancy: 10
|
||||
global_load_b32 16; s_barrier 0; full waits 23; VALU 675; v_alignbit 84; v_xor_b32 104; v_xor3 20; int mul 67; ds_ 34; scratch 0
|
||||
b3314b187c414925e52febb3683120cf7f8db2c8f4b802816b964d705c33c571 ../hl-v6-all/hl-v6-all/kernel.cl
|
||||
b66c22e859244f86f164e05841858ed1d39b42f1ab6611504872c652fb02ee23 ../hl-v6-foldrw/hl-v6-foldrw/kernel.cl
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
# AMD-and-Intel energy lane (Igneum 2.0 register: GPU-03's AMD cells, ECO-05's owed RX 7600 knee), 8 October 2026.
|
||||
# The three-card Windows rig's RX 7600 (OpenCL gfx1102), measure only, unelevated: the class v6 kit's own igneum-worker-opencl.exe is the load
|
||||
# (--bench-pack, no app involvement: the app is never quit, paused or resumed, no api/cards, no app tune), the rebuilt
|
||||
# igneum-gpu-telemetry.exe (ADLX GPUPower, else GPUTotalBoardPower) samples every 5 s beside it, at three knob points:
|
||||
# stock, plimit -30, gmax -500 + plimit -30 (the 9070 XT's best). ADLX manual tuning is reset at the end and a read-back
|
||||
# sample is printed. First: --memprobe at 1024 MiB (the card's dependent random-read ceiling, the 9070 XT's and B580's
|
||||
# instrument). Every result line starts with RESULT; SUMMARY {json} ends it. The sampler is a child process whose pid is
|
||||
# written to this job's folder and stopped by that pid only.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-7600-energy'
|
||||
$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 5)
|
||||
}
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kitId = $env:IGNEUM_V6_KIT_ID; if (-not $kitId) { $kitId = 'fetch-class-v6-kit-20261008' }
|
||||
$kit = Join-Path $jobs $kitId
|
||||
$exe = Join-Path $kit 'bin\windows\igneum-worker-opencl.exe'
|
||||
$packs = Join-Path $kit 'packs'
|
||||
if (-not (Test-Path $exe)) { "RESULT error worker missing at $exe (fetch job $kitId first)"; Summary 'failed' @{ error = 'worker missing' }; exit 2 }
|
||||
"RESULT worker $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) kit=$kitId"
|
||||
# the telemetry tool: the newest job-folder copy that prints a tune line, else the installed one (the grid playbook's rule)
|
||||
$install = Join-Path $env:LOCALAPPDATA 'Programs\Igneum Miner'
|
||||
$tool = $null; $cands = @()
|
||||
$jobsDir = Join-Path $env:LOCALAPPDATA 'igneum\app\jobs'
|
||||
if (Test-Path -LiteralPath $jobsDir) { $cands += @(Get-ChildItem -LiteralPath $jobsDir -Recurse -Filter 'igneum-gpu-telemetry*.exe' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | ForEach-Object { $_.FullName }) }
|
||||
$cands += Join-Path $install 'igneum-gpu-telemetry.exe'
|
||||
foreach ($c in $cands) { if (-not (Test-Path -LiteralPath $c)) { continue }; $pr = @(& $c --tune 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune \d+ ' }; if ($pr) { $tool = $c; break } }
|
||||
if (-not $tool) { "RESULT error no telemetry tool with a tune line"; Summary 'failed' @{ error = 'no tool' }; exit 2 }
|
||||
"RESULT tool $tool sha256 $((Get-FileHash -LiteralPath $tool -Algorithm SHA256).Hash.ToLower())"
|
||||
$tune = @(& $tool --tune 2>&1 | ForEach-Object { "$_" })
|
||||
$tune | ForEach-Object { "RESULT tune $_" }
|
||||
$line = $tune | Where-Object { $_ -match '^tune (\d+) name "([^"]*7600[^"]*)" .* ok\s*$' } | Select-Object -First 1
|
||||
if (-not $line) { "RESULT error no RX 7600 tune line"; Summary 'failed' @{ error = 'no 7600 tune line' }; exit 2 }
|
||||
$ord = [int]([regex]::Match($line, '^tune (\d+)').Groups[1].Value)
|
||||
$gr = [regex]::Match($line, 'gmax_range (-?\d+) (-?\d+)'); $prr = [regex]::Match($line, 'plimit_range (-?\d+) (-?\d+)')
|
||||
"RESULT card ordinal=$ord gmax_range=$($gr.Groups[1].Value)..$($gr.Groups[2].Value) plimit_range=$($prr.Groups[1].Value)..$($prr.Groups[2].Value)"
|
||||
# the OpenCL device of the 7600
|
||||
$list = @(& $exe --list 2>&1 | ForEach-Object { "$_" }); $list | ForEach-Object { "RESULT list $_" }
|
||||
$dev = $null; foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1102' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } }
|
||||
if ($null -eq $dev) { "RESULT error no gfx1102 in --list"; Summary 'failed' @{ error = 'no gfx1102' }; exit 2 }
|
||||
$w = @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" })
|
||||
$loaded = ($w | Where-Object { $_ -match "--device\s+$dev(\s|$)" }).Count -gt 0
|
||||
"RESULT device $dev gfx1102 card_state=$(if ($loaded) { 'loaded (another worker on the card: the rows are labelled loaded)' } else { 'quiet' }) workers=[$($w -join ' ')]"
|
||||
function SampleLine() { @(& $tool 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match ('^amd ' + $ord + ' ') } | Select-Object -First 1 }
|
||||
"RESULT idle_sample $(Stamp) $(SampleLine)"
|
||||
# 1. the random-read ceiling at the dataset size
|
||||
$mp = @(& $exe --memprobe --probe-mib 1024 --device $dev 2>&1 | ForEach-Object { "$_" })
|
||||
$mp | ForEach-Object { "RESULT memprobe $_" }
|
||||
# 2. the three knob points x the two packs, the bench as the load, the sampler beside it
|
||||
$sampler = Join-Path $env:IGNEUM_JOB_DIR 'sampler.ps1'
|
||||
$pidFile = Join-Path $env:IGNEUM_JOB_DIR 'sampler.pid'
|
||||
Set-Content -LiteralPath $sampler -Value @'
|
||||
param([string] $tool, [int] $ord, [string] $out)
|
||||
while ($true) { $l = @(& $tool 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match ('^amd ' + $ord + ' ') } | Select-Object -First 1; Add-Content -LiteralPath $out -Value ((Get-Date).ToUniversalTime().ToString('o') + ' ' + $l); Start-Sleep -Seconds 5 }
|
||||
'@
|
||||
function SetPoint([int] $g, [int] $p) {
|
||||
$a = @(& $tool --card $ord --set-gmax $g 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune ' } | Select-Object -First 1
|
||||
$b = @(& $tool --card $ord --set-plimit $p 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune ' } | Select-Object -First 1
|
||||
return @{ ok = (($a -match ' ok\s*$') -and ($b -match ' ok\s*$')); lines = ($a + ' | ' + $b) }
|
||||
}
|
||||
$rows = @()
|
||||
$points = @(@(0, 0), @(0, -30), @(-500, -30))
|
||||
foreach ($pt in $points) {
|
||||
$g = $pt[0]; $p = $pt[1]
|
||||
if ($g -lt [int]$gr.Groups[1].Value -or $p -lt [int]$prr.Groups[1].Value) { "RESULT point gmax=$g plimit=$p skipped (outside the card's range)"; continue }
|
||||
$set = SetPoint $g $p
|
||||
"RESULT point gmax=$g plimit=$p set ok=$($set.ok) $($set.lines)"
|
||||
if (-not $set.ok) { continue }
|
||||
foreach ($pk in @('hl-v6-foldrw', 'hl-v6-all')) {
|
||||
$d = Join-Path $packs $pk
|
||||
$nb = if ($pk -eq 'hl-v6-all') { 40 } else { 80 }
|
||||
$samp = Join-Path $env:IGNEUM_JOB_DIR ("samples-$pk-g$g-p$p.txt")
|
||||
$sp = Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $sampler, '-tool', $tool, '-ord', $ord, '-out', $samp) -WindowStyle Hidden -PassThru
|
||||
Set-Content -LiteralPath $pidFile -Value $sp.Id
|
||||
$t0 = Get-Date
|
||||
$out = @(& $exe --bench-pack --pack $d --device $dev --batch-log2 24 --batches $nb 2>&1 | ForEach-Object { "$_" })
|
||||
$t1 = Get-Date
|
||||
$spid = [int](Get-Content -LiteralPath $pidFile); Stop-Process -Id $spid -Force -ErrorAction SilentlyContinue; Remove-Item -LiteralPath $pidFile -ErrorAction SilentlyContinue
|
||||
foreach ($l in $out) { if ($l -match '^(pack |class |RESULT |FAIL|error|warm-up|kernel|exchange)') { "RESULT bench $pk g=$g p=$p out $l" } }
|
||||
$res = $out | Where-Object { $_ -match '^RESULT ' } | Select-Object -Last 1
|
||||
$fp = ''; $mhs = 0; $check = ''
|
||||
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -match 'mhs=([0-9.]+)') { $mhs = [double]$Matches[1] }
|
||||
if ($res -match 'check=(\w+)') { $check = $Matches[1] }
|
||||
# the watts: samples from 20 s after the bench started (the dataset build and warm-up excluded) to its end
|
||||
$ws = @(); $gc = @(); $raw = @()
|
||||
if (Test-Path -LiteralPath $samp) { foreach ($s in Get-Content -LiteralPath $samp) { $raw += $s; $ts = [datetime]::Parse(($s -split ' ')[0]).ToUniversalTime(); if ($ts -ge $t0.ToUniversalTime().AddSeconds(20) -and $ts -le $t1.ToUniversalTime()) { $m = [regex]::Match($s, ' watts (-?[\d.]+)'); if ($m.Success -and [double]$m.Groups[1].Value -gt 0) { $ws += [double]$m.Groups[1].Value }; $c = [regex]::Match($s, ' gclk_mhz (-?[\d.]+)'); if ($c.Success) { $gc += [double]$c.Groups[1].Value } } } }
|
||||
$raw | Select-Object -First 3 | ForEach-Object { "RESULT sample $pk g=$g p=$p $_" }
|
||||
$wm = if ($ws.Count) { [math]::Round((($ws | Measure-Object -Average).Average), 1) } else { 0 }
|
||||
$gm = if ($gc.Count) { [math]::Round((($gc | Measure-Object -Average).Average), 0) } else { 0 }
|
||||
$uj = if ($mhs -gt 0 -and $wm -gt 0) { [math]::Round($wm / $mhs, 3) } else { 0 }
|
||||
"RESULT ROW card=RX7600 pack=$pk gmax_off=$g plimit=$p mhs=$mhs watts=$wm uj_per_hash=$uj gclk=$gm samples=$($ws.Count) seconds=$([int]($t1 - $t0).TotalSeconds) fingerprint=$fp check=$check $(Stamp)"
|
||||
$rows += [ordered]@{ pack = $pk; g = $g; p = $p; mhs = $mhs; watts = $wm; uj = $uj; gclk = $gm; samples = $ws.Count; fingerprint = $fp; check = $check }
|
||||
}
|
||||
}
|
||||
$r = @(& $tool --card $ord --reset 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune ' } | Select-Object -First 1
|
||||
"RESULT reset $r"
|
||||
Start-Sleep -Seconds 3
|
||||
"RESULT after_reset_sample $(Stamp) $(SampleLine)"
|
||||
"RESULT after_reset_tune $((@(& $tool --tune 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match ('^tune ' + $ord + ' ') } | Select-Object -First 1))"
|
||||
Summary $(if ($rows.Count) { 'done' } else { 'failed' }) @{ rows = $rows; device = $dev; ordinal = $ord; kit = $kitId }
|
||||
exit $(if ($rows.Count) { 0 } else { 1 })
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# AMD-and-Intel energy lane, 8 October 2026: the guaranteed ADLX reset of the RX 7600 after run-ae-pc1-7600-energy-20261008
|
||||
# (a timeout, a killed job tree or a lost relay must never leave the card capped). Unelevated; the card's ordinal from the
|
||||
# tool's own tune line (the integrated Radeon's all-dash line is not a card); --reset, then a tune-line read-back.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$install = Join-Path $env:LOCALAPPDATA 'Programs\Igneum Miner'
|
||||
$tool = $null; $cands = @()
|
||||
$jobsDir = Join-Path $env:LOCALAPPDATA 'igneum\app\jobs'
|
||||
if (Test-Path -LiteralPath $jobsDir) { $cands += @(Get-ChildItem -LiteralPath $jobsDir -Recurse -Filter 'igneum-gpu-telemetry*.exe' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | ForEach-Object { $_.FullName }) }
|
||||
$cands += Join-Path $install 'igneum-gpu-telemetry.exe'
|
||||
foreach ($c in $cands) { if (-not (Test-Path -LiteralPath $c)) { continue }; $pr = @(& $c --tune 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune \d+ ' }; if ($pr) { $tool = $c; break } }
|
||||
if (-not $tool) { 'RESULT error no telemetry tool with a tune line'; 'SUMMARY {"job":"pc1-7600-reset","status":"failed"}'; exit 2 }
|
||||
$line = @(& $tool --tune 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune (\d+) name "([^"]*7600[^"]*)" .* ok\s*$' } | Select-Object -First 1
|
||||
if (-not $line) { 'RESULT error no RX 7600 tune line'; 'SUMMARY {"job":"pc1-7600-reset","status":"failed"}'; exit 2 }
|
||||
$ord = [int]([regex]::Match($line, '^tune (\d+)').Groups[1].Value)
|
||||
"RESULT before $line"
|
||||
$r = @(& $tool --card $ord --reset 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match '^tune ' } | Select-Object -First 1
|
||||
"RESULT reset $r"
|
||||
Start-Sleep -Seconds 2
|
||||
$after = @(& $tool --tune 2>&1 | ForEach-Object { "$_" }) | Where-Object { $_ -match ('^tune ' + $ord + ' ') } | Select-Object -First 1
|
||||
"RESULT after $after"
|
||||
$ok = ($after -match ' gmax 0 ') -and ($after -match ' plimit 0 ')
|
||||
"SUMMARY {""job"":""pc1-7600-reset"",""status"":""$(if ($ok) { 'done' } else { 'check' })"",""ordinal"":$ord}"
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
# AMD-and-Intel energy lane (Igneum 2.0 register: GPU-03's Intel cell, ECO-05's owed B580 watts), 8 October 2026.
|
||||
# The second Windows rig's Intel Arc B580, measure only, unelevated, no knob written (Intel has none in our tools): the class v6 kit's own
|
||||
# igneum-worker-opencl.exe is the load (--bench-pack; the app is never quit, paused or resumed, no api/cards), and
|
||||
# ze-power.exe (fetch job fetch-ae-ze-power-20261008, sha256 354abddd...; Level Zero sysman energy counters through the
|
||||
# driver's ze_loader.dll, read only) samples every 5 s beside it. First: --memprobe at 256 and 1024 MiB (the dependent
|
||||
# random-read ceiling; two sizes to see whether the rate falls with the footprint beyond the caches, a TLB-reach sign).
|
||||
# The sampler is a child process whose pid is written to this job's folder and stopped by that pid only.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$env:IGNEUM_V6_KIT_ID = 'fetch-class-v6-kit-20261008'
|
||||
$jobName = 'pc2-b580-energy'
|
||||
$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 5)
|
||||
}
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$ze = Join-Path (Join-Path $jobs 'fetch-ae-ze-power-20261008') 'ze-power.exe'
|
||||
if (-not (Test-Path $ze)) { $ze = @(Get-ChildItem -LiteralPath $jobs -Recurse -Filter 'ze-power.exe' -ErrorAction SilentlyContinue | Select-Object -First 1 | ForEach-Object { $_.FullName }) | Select-Object -First 1 }
|
||||
if (-not $ze -or -not (Test-Path $ze)) { "RESULT error ze-power.exe missing (fetch job fetch-ae-ze-power-20261008 first)"; Summary 'failed' @{ error = 'no sampler' }; exit 2 }
|
||||
"RESULT sampler $ze sha256 $((Get-FileHash -Algorithm SHA256 $ze).Hash.ToLower())"
|
||||
@(& $ze 2>&1 | ForEach-Object { "$_" }) | ForEach-Object { "RESULT ze_idle $_" }
|
||||
# the kit: IGNEUM_V6_KIT_ID, else the newest job folder holding the worker and packs\hl-v6-foldrw
|
||||
$kit = $null
|
||||
if ($env:IGNEUM_V6_KIT_ID) { $kit = Join-Path $jobs $env:IGNEUM_V6_KIT_ID }
|
||||
if (-not $kit -or -not (Test-Path $kit)) { $kit = @(Get-ChildItem -LiteralPath $jobs -Directory -ErrorAction SilentlyContinue | Where-Object { (Test-Path (Join-Path $_.FullName 'packs\hl-v6-foldrw\kernel_bound.cl')) -and (Test-Path (Join-Path $_.FullName 'bin\windows\igneum-worker-opencl.exe')) } | Sort-Object LastWriteTime -Descending | ForEach-Object { $_.FullName }) | Select-Object -First 1 }
|
||||
if (-not $kit) { "RESULT error no class v6 kit on this PC"; Summary 'failed' @{ error = 'no kit' }; exit 2 }
|
||||
$exe = Join-Path $kit 'bin\windows\igneum-worker-opencl.exe'; $packs = Join-Path $kit 'packs'
|
||||
"RESULT worker $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) kit=$(Split-Path $kit -Leaf)"
|
||||
$list = @(& $exe --list 2>&1 | ForEach-Object { "$_" }); $list | ForEach-Object { "RESULT list $_" }
|
||||
$dev = $null; foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*(B580|Arc|Battlemage)' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } }
|
||||
if ($null -eq $dev) { "RESULT error no Arc B580 in --list"; Summary 'failed' @{ error = 'no B580' }; exit 2 }
|
||||
$w = @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" })
|
||||
$loaded = ($w | Where-Object { $_ -match "--device\s+$dev(\s|$)" }).Count -gt 0
|
||||
"RESULT device $dev card_state=$(if ($loaded) { 'loaded' } else { 'quiet' }) workers=[$($w -join ' ')]"
|
||||
foreach ($mib in @(256, 1024)) { @(& $exe --memprobe --probe-mib $mib --device $dev 2>&1 | ForEach-Object { "$_" }) | ForEach-Object { "RESULT memprobe $mib $_" } }
|
||||
$pidFile = Join-Path $env:IGNEUM_JOB_DIR 'ze.pid'
|
||||
$rows = @()
|
||||
foreach ($pk in @('hl-v6-foldrw', 'hl-v6-all')) {
|
||||
$d = Join-Path $packs $pk
|
||||
if (-not (Test-Path (Join-Path $d 'kernel_bound.cl'))) { "RESULT error pack $pk missing"; continue }
|
||||
$nb = if ($pk -eq 'hl-v6-all') { 30 } else { 60 }
|
||||
$samp = Join-Path $env:IGNEUM_JOB_DIR ("ze-$pk.txt")
|
||||
$sp = Start-Process -FilePath $ze -ArgumentList @('-l', '5') -RedirectStandardOutput $samp -WindowStyle Hidden -PassThru
|
||||
Set-Content -LiteralPath $pidFile -Value $sp.Id
|
||||
$t0 = Get-Date
|
||||
$out = @(& $exe --bench-pack --pack $d --device $dev --batch-log2 24 --batches $nb 2>&1 | ForEach-Object { "$_" })
|
||||
$t1 = Get-Date
|
||||
$zpid = [int](Get-Content -LiteralPath $pidFile); Stop-Process -Id $zpid -Force -ErrorAction SilentlyContinue; Remove-Item -LiteralPath $pidFile -ErrorAction SilentlyContinue
|
||||
foreach ($l in $out) { if ($l -match '^(pack |class |RESULT |FAIL|error|warm-up|kernel|exchange)') { "RESULT bench $pk out $l" } }
|
||||
$res = $out | Where-Object { $_ -match '^RESULT ' } | Select-Object -Last 1
|
||||
$fp = ''; $mhs = 0; $check = ''
|
||||
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -match 'mhs=([0-9.]+)') { $mhs = [double]$Matches[1] }
|
||||
if ($res -match 'check=(\w+)') { $check = $Matches[1] }
|
||||
# ze lines arrive every 5 s; the lines after the first 20 s of the bench (warm-up and dataset build excluded): the sampler
|
||||
# started with the bench, so the first four sample rounds are dropped
|
||||
$lines = @(); if (Test-Path -LiteralPath $samp) { $lines = @(Get-Content -LiteralPath $samp) }
|
||||
$lines | Select-Object -First 6 | ForEach-Object { "RESULT ze $pk $_" }
|
||||
$dom = @{}
|
||||
$round = @{}
|
||||
foreach ($l in $lines) { if ($l -match '^intel (\d+) dom (\d+) card (\d) watts ([\d.]+)') { $k = "$($Matches[1])/$($Matches[2])/card$($Matches[3])"; if (-not $round.ContainsKey($k)) { $round[$k] = 0 }; $round[$k]++; if ($round[$k] -gt 4) { if (-not $dom.ContainsKey($k)) { $dom[$k] = @() }; $dom[$k] += [double]$Matches[4] } } }
|
||||
$dm = [ordered]@{}
|
||||
foreach ($k in $dom.Keys) { $dm[$k] = [math]::Round((($dom[$k] | Measure-Object -Average).Average), 1); "RESULT ROW card=ArcB580 pack=$pk domain=$k mhs=$mhs watts=$($dm[$k]) uj_per_hash=$(if ($mhs -gt 0) { [math]::Round($dm[$k] / $mhs, 3) } else { 0 }) samples=$($dom[$k].Count) seconds=$([int]($t1 - $t0).TotalSeconds) fingerprint=$fp check=$check $(Stamp)" }
|
||||
$rows += [ordered]@{ pack = $pk; mhs = $mhs; watts = $dm; fingerprint = $fp; check = $check }
|
||||
}
|
||||
Summary $(if ($rows.Count) { 'done' } else { 'failed' }) @{ rows = $rows; device = $dev }
|
||||
exit $(if ($rows.Count) { 0 } else { 1 })
|
||||
98
docs/analysis/class-v6/amd-intel-energy/ze-power.c
Normal file
98
docs/analysis/class-v6/amd-intel-energy/ze-power.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/* ze-power: Intel GPU power from the Level Zero sysman energy counters (AMD-and-Intel energy lane, 8 October 2026).
|
||||
* Windows: loads ze_loader.dll (installed by the Intel graphics driver) at run time; no SDK, no headers.
|
||||
* ze-power.exe one reading: two energy-counter reads 1 s apart per power domain
|
||||
* ze-power.exe -l N a line every N seconds until killed
|
||||
* Line: intel <dev> dom <k> card <0|1> watts <W> energy_uj <E> ts_us <T> name "<first printable strings>"
|
||||
* The counter is the device's own (package or card domain, as the driver exposes it), not the wall. Read only:
|
||||
* no set call exists in this program. */
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
typedef int32_t zr;
|
||||
typedef void *H;
|
||||
typedef struct { uint64_t energy; uint64_t timestamp; } ecnt;
|
||||
typedef zr (__cdecl *f_init)(uint32_t);
|
||||
typedef zr (__cdecl *f_get)(uint32_t *, H *);
|
||||
typedef zr (__cdecl *f_get2)(H, uint32_t *, H *);
|
||||
typedef zr (__cdecl *f_card)(H, H *);
|
||||
typedef zr (__cdecl *f_cnt)(H, ecnt *);
|
||||
typedef zr (__cdecl *f_props)(H, void *);
|
||||
#define MAXD 8
|
||||
#define MAXP 8
|
||||
static void names(H dev, f_props gp, char *out, size_t cap) {
|
||||
out[0] = 0;
|
||||
if (!gp) return;
|
||||
static unsigned char buf[8192];
|
||||
memset(buf, 0, sizeof buf);
|
||||
*(uint32_t *)(buf + 0) = 0x1; /* ZES_STRUCTURE_TYPE_DEVICE_PROPERTIES */
|
||||
*(uint32_t *)(buf + 16) = 0x3; /* core: ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES */
|
||||
if (gp(dev, buf) != 0) return;
|
||||
size_t o = 0; int run = 0; size_t start = 0;
|
||||
for (size_t i = 0; i < sizeof buf && o + 2 < cap; i++) {
|
||||
unsigned char c = buf[i];
|
||||
if (c >= 32 && c < 127) { if (!run) { start = i; run = 1; } }
|
||||
else { if (run && i - start >= 4) { size_t n = i - start; if (o + n + 2 >= cap) break; if (o) out[o++] = '|'; memcpy(out + o, buf + start, n); o += n; } run = 0; }
|
||||
}
|
||||
out[o] = 0;
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
int loop = 0;
|
||||
if (argc >= 3 && strcmp(argv[1], "-l") == 0) loop = atoi(argv[2]);
|
||||
HMODULE m = LoadLibraryA("ze_loader.dll");
|
||||
if (!m) { printf("error no ze_loader.dll (%lu)\n", GetLastError()); return 2; }
|
||||
f_init zesInit = (f_init)GetProcAddress(m, "zesInit");
|
||||
f_get zesDriverGet = (f_get)GetProcAddress(m, "zesDriverGet");
|
||||
f_get2 zesDeviceGet = (f_get2)GetProcAddress(m, "zesDeviceGet");
|
||||
f_get2 enumPwr = (f_get2)GetProcAddress(m, "zesDeviceEnumPowerDomains");
|
||||
f_card cardPwr = (f_card)GetProcAddress(m, "zesDeviceGetCardPowerDomain");
|
||||
f_cnt getE = (f_cnt)GetProcAddress(m, "zesPowerGetEnergyCounter");
|
||||
f_props gp = (f_props)GetProcAddress(m, "zesDeviceGetProperties");
|
||||
f_init zeInit = (f_init)GetProcAddress(m, "zeInit");
|
||||
f_get zeDriverGet = (f_get)GetProcAddress(m, "zeDriverGet");
|
||||
f_get2 zeDeviceGet = (f_get2)GetProcAddress(m, "zeDeviceGet");
|
||||
if (!enumPwr || !getE) { printf("error the loader has no sysman power entry points\n"); return 2; }
|
||||
H drv[MAXD], dev[MAXD * 4]; uint32_t nd = 0, ndev = 0; const char *path = "zesInit";
|
||||
zr r = zesInit ? zesInit(0) : -1;
|
||||
if (r == 0 && zesDriverGet && zesDeviceGet) {
|
||||
uint32_t c = MAXD; if (zesDriverGet(&c, drv) == 0) nd = c;
|
||||
for (uint32_t i = 0; i < nd; i++) { uint32_t k = MAXD; if (zesDeviceGet(drv[i], &k, dev + ndev) == 0) ndev += k; }
|
||||
}
|
||||
if (ndev == 0 && zeInit && zeDriverGet && zeDeviceGet) {
|
||||
path = "zeInit+ZES_ENABLE_SYSMAN"; SetEnvironmentVariableA("ZES_ENABLE_SYSMAN", "1");
|
||||
r = zeInit(0);
|
||||
uint32_t c = MAXD; if (r == 0 && zeDriverGet(&c, drv) == 0) nd = c;
|
||||
for (uint32_t i = 0; i < nd; i++) { uint32_t k = MAXD; if (zeDeviceGet(drv[i], &k, dev + ndev) == 0) ndev += k; }
|
||||
}
|
||||
printf("info path %s init %d drivers %u devices %u\n", path, (int)r, nd, ndev);
|
||||
if (ndev == 0) { printf("error no sysman device\n"); return 3; }
|
||||
H pw[MAXD * 4][MAXP + 1]; uint32_t np[MAXD * 4]; int iscard[MAXD * 4][MAXP + 1]; char nm[MAXD * 4][256];
|
||||
for (uint32_t d = 0; d < ndev; d++) {
|
||||
names(dev[d], gp, nm[d], sizeof nm[d]);
|
||||
uint32_t k = MAXP; np[d] = 0;
|
||||
if (enumPwr(dev[d], &k, pw[d]) == 0) np[d] = k;
|
||||
for (uint32_t j = 0; j < np[d]; j++) iscard[d][j] = 0;
|
||||
H c = 0;
|
||||
if (cardPwr && cardPwr(dev[d], &c) == 0 && c) { pw[d][np[d]] = c; iscard[d][np[d]] = 1; np[d]++; }
|
||||
printf("info dev %u domains %u name \"%s\"\n", d, np[d], nm[d]);
|
||||
}
|
||||
ecnt prev[MAXD * 4][MAXP + 1];
|
||||
for (uint32_t d = 0; d < ndev; d++) for (uint32_t j = 0; j < np[d]; j++) { memset(&prev[d][j], 0, sizeof(ecnt)); getE(pw[d][j], &prev[d][j]); }
|
||||
int every = loop > 0 ? loop : 1;
|
||||
for (;;) {
|
||||
Sleep(every * 1000);
|
||||
for (uint32_t d = 0; d < ndev; d++) for (uint32_t j = 0; j < np[d]; j++) {
|
||||
ecnt e; memset(&e, 0, sizeof e);
|
||||
zr q = getE(pw[d][j], &e);
|
||||
double dt = (double)(e.timestamp - prev[d][j].timestamp) * 1e-6, de = (double)(e.energy - prev[d][j].energy) * 1e-6;
|
||||
double w = dt > 0 ? de / dt : 0;
|
||||
printf("intel %u dom %u card %d watts %.2f energy_uj %llu ts_us %llu rc %d name \"%s\"\n", d, j, iscard[d][j], w,
|
||||
(unsigned long long)e.energy, (unsigned long long)e.timestamp, (int)q, nm[d]);
|
||||
prev[d][j] = e;
|
||||
}
|
||||
fflush(stdout);
|
||||
if (loop <= 0) break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in a new issue