code wiki / _hdl_build / nx_energy_model_test.nx
nx_energy_model_test.nx source
↩ module page · 79 lines · 3892 B
1// nx_energy_model_test.nx -- prove the state-independent energy model end to end,
2// pure NishiLang, on THIS box (no PMU, no RAPL, on AC -- yet we still get a number).
3//
4// 1. MEASURE a charge-independent activity count (rdtsc cycles over a real workload).
5// 2. ESTIMATE energy from the documented PRIOR coefficient (labeled as a prior).
6// 3. CALIBRATE: given a real joule reading for a known activity, recover the
7// coefficient and show energy(activity, calibrated) round-trips exactly -- this
8// is the math a real RAPL/INA/discharge reading drives on a sensor-equipped node.
9// 4. MONOTONIC: 2x the activity -> 2x the energy (sanity).
10//
11// The point proven: a real energy ESTIMATE with NO power sensor and IN ANY power
12// state, because the input (cycles) is charge-independent. Known answer: exit 0.
13
14import "nx_energy_model.nx"
15
16func _emit(name: *u8, v: i64) -> i64 {
17 var n: i64 = 0; while name[n] != (0 as u8) { n = n + 1 } sys_write(1, name, n)
18 let b: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m }
19 let t: *u8 = sys_mmap(28); var k: i64 = 0
20 if m == 0 { t[0] = 48; k = 1 }
21 while m > 0 { t[k] = 48 + (m % 10); m = m / 10; k = k + 1 }
22 var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 }
23 b[k] = 10; sys_write(1, b, k + 1); return 0
24}
25
26const EMT_ITERS: i64 = 5000000
27
28func emt_work(seed: i64) -> i64 {
29 var acc: i64 = seed | 1
30 var i: i64 = 0
31 while i < EMT_ITERS { acc = acc * 3; acc = acc | 1; i = i + 1 }
32 return acc
33}
34
35func main() -> i64 {
36 sys_write(1, "=== STATE-INDEPENDENT energy model (rdtsc, any power state) ===\n" as *u8, 63)
37
38 // 1. state-independent activity: rdtsc cycles (works on AC, no battery, any VM).
39 let t0: i64 = __rdtsc()
40 let r: i64 = emt_work(7)
41 let t1: i64 = __rdtsc()
42 if r == 0 - 1 { sys_write(1, "" as *u8, 0) }
43 let cycles: i64 = t1 - t0
44 _emit(" measured cycles (charge-independent) : " as *u8, cycles)
45
46 // 2. estimate energy from the documented PRIOR coefficient.
47 let e_prior_fj: i64 = em_energy_fj(cycles, EM_PRIOR_FJ_PER_CYCLE)
48 _emit(" PRIOR coeff (fJ/cycle, to be calibrated): " as *u8, EM_PRIOR_FJ_PER_CYCLE)
49 _emit(" energy estimate, prior (nanojoules) : " as *u8, em_fj_to_nj(e_prior_fj))
50 sys_write(1, " ^ a real estimate with NO power sensor and ON AC -- the input is cycles.\n" as *u8, 75)
51
52 // 3. CALIBRATION math: a real sensor measured (say) 800000000 fJ for these cycles.
53 // Recover the coefficient and show the model round-trips to that exact energy.
54 let real_fj: i64 = 800000000
55 let coeff: i64 = em_calibrate_fj_per_unit(real_fj, cycles)
56 let e_cal: i64 = em_energy_fj(cycles, coeff)
57 _emit(" calibrated coeff (fJ/cycle, from sensor): " as *u8, coeff)
58 _emit(" energy, calibrated (fJ) : " as *u8, e_cal)
59 // round-trip must match within truncation (real_fj - cycles, since integer div)
60 let err: i64 = real_fj - e_cal
61 var aerr: i64 = err; if aerr < 0 { aerr = 0 - aerr }
62
63 // 4. monotonic: double the activity -> double the energy.
64 let e1: i64 = em_energy_fj(cycles, EM_PRIOR_FJ_PER_CYCLE)
65 let e2: i64 = em_energy_fj(cycles * 2, EM_PRIOR_FJ_PER_CYCLE)
66
67 sys_write(1, " a real sensor (RAPL/INA/discharge) only CALIBRATES the coeff; it is\n" as *u8, 68)
68 sys_write(1, " never required to get a number. Charging is irrelevant.\n" as *u8, 56)
69
70 // GATE: real activity measured, energy positive, calibration round-trips
71 // (within one cycle of truncation), and the model is monotonic.
72 if cycles <= 0 { sys_exit(1); return 1 }
73 if e_prior_fj <= 0 { sys_exit(2); return 2 }
74 if coeff <= 0 { sys_exit(3); return 3 }
75 if aerr > cycles { sys_exit(4); return 4 } // round-trip error < 1 unit
76 if e2 <= e1 { sys_exit(5); return 5 } // monotonic
77 sys_exit(0)
78 return 0
79}