code wiki / _hdl_build / nx_energy_model.nx

nx_energy_model.nx source

↩ module page · 48 lines · 2603 B

1// nx_energy_model.nx -- STATE-INDEPENDENT energy estimation, pure NishiLang. 2// 3// The S-class answer to "you can only measure energy when not charging": you do 4// NOT measure the battery at all. Energy is estimated from a CHARGE-INDEPENDENT 5// activity counter -- cycles (rdtsc/rdcycle, available on EVERY target including a 6// PMU-less VM) or retired instructions (perf, where a PMU exists) -- times a 7// per-unit energy coefficient: 8// 9// energy = activity_count * energy_per_unit 10// 11// activity_count does not depend on whether the node is plugged in, so this yields 12// a real energy number on AC, on a battery-less server, on a supercomputer node, 13// or on a discharging garden sensor -- ANY power state, ANY hardware. 14// 15// The coefficient is the only chip-specific part. A real joule sensor (RAPL, an 16// INA219 on the rail, or a discharging fuel-gauge) CALIBRATES it once: 17// energy_per_unit = real_energy_measured / activity_count_for_that_work 18// Where no sensor is present we use a documented PRIOR (clearly labeled, not a 19// measured claim) and refine it the moment a sensor appears. So a sensor IMPROVES 20// the number; it is never REQUIRED to get one. 21// 22// Research (Cardinal #4, real sources): per-operation / per-cycle energy figures -- 23// Horowitz, "Computing's Energy Problem (and what we can do about it)," ISSCC 2014; 24// event-driven accounting -- Bellosa, ACM SIGOPS EW 2000; PMC power models -- Isci 25// & Martonosi, MICRO 2003; Bircher & John, ISPASS 2007. 26 27import "nx_syscalls.nx" 28const EM_MAGIC_1000000: i64 = 1000000 29 30// Prior coefficient in FEMTOJOULES per reference cycle. ~5e6 fJ = 5 nJ/cycle is the 31// order of magnitude for one active modern core (~15 W at ~3 GHz: 15/3e9 = 5 nJ). 32// This is a PRIOR to be calibrated per chip -- NOT a measured value for any 33// specific node. (Horowitz ISSCC 2014 gives the per-op pJ figures this scales from.) 34const EM_PRIOR_FJ_PER_CYCLE: i64 = 5000000 35 36// energy (femtojoules) from a state-independent activity count + coefficient. 37func em_energy_fj(activity: i64, fj_per_unit: i64) -> i64 { return activity * fj_per_unit } 38 39// CALIBRATE the coefficient from a real joule measurement over a known activity 40// count: fj_per_unit = real_energy_fj / activity. -1 on bad input. 41func em_calibrate_fj_per_unit(real_energy_fj: i64, activity: i64) -> i64 { 42 if activity <= 0 { return 0 - 1 } 43 return real_energy_fj / activity 44} 45 46// convenience: femtojoules -> picojoules and -> nanojoules (integer, truncating). 47func em_fj_to_pj(fj: i64) -> i64 { return fj / 1000 } 48func em_fj_to_nj(fj: i64) -> i64 { return fj / EM_MAGIC_1000000 }