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}