commit 125f716c513a6d7be35f175ee34f5aa11e45a20a Author: Patrick Auernig Date: Thu Aug 3 16:15:40 2023 +0200 Create project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1380eb3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ryzen-wattage" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f0f240d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ryzen-wattage" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..3881215 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,154 @@ +#![allow(dead_code)] + +use std::{ + collections::BTreeMap, + fs::{self, File}, + io::{self, Read, Seek, SeekFrom}, + path::PathBuf, + thread, + time::Duration, +}; + +type MsrMap = BTreeMap; + +#[derive(Debug)] +struct Cpu { + smt_enabled: bool, + core_count: u32, + core_msr: MsrMap, +} + +impl Cpu { + pub fn new() -> io::Result { + let smt_status = fs::read_to_string("/sys/devices/system/cpu/smt/control")?; + let smt_enabled = smt_status.trim_end() == "on"; + + let cores_online = fs::read_to_string("/sys/devices/system/cpu/online")?; + let (_, max) = cores_online.trim_end().split_once("-").unwrap(); + let core_count = max.parse::().unwrap() + 1; + + let core_msr = Self::get_msr_info(smt_enabled, core_count); + + Ok(Self { + smt_enabled, + core_count, + core_msr, + }) + } + + fn get_msr_info(smt_enabled: bool, core_count: u32) -> MsrMap { + let mut map = MsrMap::new(); + + for core in 0..core_count { + if smt_enabled && core % 2 == 0 { + let msr = Msr::new(core); + map.insert(core, msr); + } + } + + map + } + + pub fn package_energy(&self) -> f64 { + let (_, energy) = self + .core_msr + .iter() + .map(|(core, msr)| (core, msr.package_energy().unwrap())) + .next() + .unwrap(); + + energy + } + + pub fn core_energy(&self) -> BTreeMap { + self.core_msr + .iter() + .map(|(core, msr)| (*core, msr.core_energy().unwrap())) + .collect() + } + + pub fn power(&self, duration: Duration) -> (f64, BTreeMap) { + let package_energy_before = self.package_energy(); + let core_energy_before = self.core_energy(); + + thread::sleep(duration); + + let package_energy_after = self.package_energy(); + let core_energy_after = self.core_energy(); + + let duration = duration.as_secs() as f64; + + let package_energy = (package_energy_after - package_energy_before) / duration; + + let cores_energy = core_energy_before + .iter() + .zip(&core_energy_after) + .map(|((&core, &before), (_, &after))| (core, (after - before) / duration)) + .collect(); + + (package_energy, cores_energy) + } +} + +#[derive(Debug)] +struct Msr { + path: PathBuf, +} + +impl Msr { + const POWER_UNIT_OFFSET: u64 = 0xC0010299; + const CORE_ENERGY_OFFSET: u64 = 0xC001029A; + const PACKAGE_ENERGY_OFFSET: u64 = 0xC001029B; + const ENERGY_UNIT_MASK: u64 = 0x1F00; + + pub fn new(core: u32) -> Self { + let path = PathBuf::from(format!("/dev/cpu/{}/msr", core)); + Self { path } + } + + pub fn core_energy(&self) -> io::Result { + let core_energy = self.read_register(Self::CORE_ENERGY_OFFSET)?; + let core_energy = core_energy as f64 * self.energy_unit()?; + Ok(core_energy) + } + + pub fn package_energy(&self) -> io::Result { + let energy = self.read_register(Self::PACKAGE_ENERGY_OFFSET)?; + let energy = energy as f64 * self.energy_unit()?; + Ok(energy) + } + + fn energy_unit(&self) -> io::Result { + let units = self.read_register(Self::POWER_UNIT_OFFSET)?; + let unit = (units & Self::ENERGY_UNIT_MASK) >> 8; + Ok((0.5_f64).powf(unit as f64)) + } + + fn read_register(&self, offset: u64) -> io::Result { + let mut msr_file = File::open(&self.path)?; + msr_file.seek(SeekFrom::Start(offset))?; + + let mut data = [0u8; 8]; + msr_file.read_exact(&mut data)?; + + let data = u64::from_ne_bytes(data); + Ok(data) + } +} + +fn main() { + let cpu = Cpu::new().unwrap(); + + let (package_power, cores_power) = cpu.power(Duration::from_secs(1)); + + println!("Package: {:.2}W", package_power); + + let mut core_sum = 0.0; + + for (core, core_power) in cores_power { + core_sum += core_power; + println!("Core {}: {:.2}W", core, core_power); + } + + println!("Cores Total: {:.2}W", core_sum); +}