From d45e9b8c293fa1e04ede47af0007bc720f093413 Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Tue, 9 Jul 2024 18:04:08 -0700 Subject: initial commit ``` [18:04] mba-fcuny:apple_silicon (main +) | cargo run Compiling apple_silicon v0.1.0 (/Users/fcuny/workspace/apple_silicon) Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s Running `target/debug/apple_silicon` our CPU is an Apple M2, and we have 8 cores, and 10 GPU cores ``` --- src/apple_silicon/.gitignore | 1 + src/apple_silicon/Cargo.lock | 65 ++++++++++++++++++++++++++++++++++++ src/apple_silicon/Cargo.toml | 7 ++++ src/apple_silicon/src/error.rs | 23 +++++++++++++ src/apple_silicon/src/main.rs | 10 ++++++ src/apple_silicon/src/soc.rs | 75 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 181 insertions(+) create mode 100644 src/apple_silicon/.gitignore create mode 100644 src/apple_silicon/Cargo.lock create mode 100644 src/apple_silicon/Cargo.toml create mode 100644 src/apple_silicon/src/error.rs create mode 100644 src/apple_silicon/src/main.rs create mode 100644 src/apple_silicon/src/soc.rs (limited to 'src') diff --git a/src/apple_silicon/.gitignore b/src/apple_silicon/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/src/apple_silicon/.gitignore @@ -0,0 +1 @@ +/target diff --git a/src/apple_silicon/Cargo.lock b/src/apple_silicon/Cargo.lock new file mode 100644 index 0000000..25e57f9 --- /dev/null +++ b/src/apple_silicon/Cargo.lock @@ -0,0 +1,65 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "apple_silicon" +version = "0.1.0" +dependencies = [ + "thiserror", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/src/apple_silicon/Cargo.toml b/src/apple_silicon/Cargo.toml new file mode 100644 index 0000000..21b6242 --- /dev/null +++ b/src/apple_silicon/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "apple_silicon" +version = "0.1.0" +edition = "2021" + +[dependencies] +thiserror = "1.0.61" diff --git a/src/apple_silicon/src/error.rs b/src/apple_silicon/src/error.rs new file mode 100644 index 0000000..a70b9b5 --- /dev/null +++ b/src/apple_silicon/src/error.rs @@ -0,0 +1,23 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("socinfo parsing error: `{0}`")] + Parse(String), + + #[error("I/O error: `{source}`")] + Io { + #[from] + source: std::io::Error, + }, + + #[error("utf8 conversion error: `{source}`")] + Utf8Conversion { + #[from] + source: std::string::FromUtf8Error, + }, + + #[error("integer parsing error: `{source}`")] + ParseInt { + #[from] + source: std::num::ParseIntError, + }, +} diff --git a/src/apple_silicon/src/main.rs b/src/apple_silicon/src/main.rs new file mode 100644 index 0000000..b54c3ec --- /dev/null +++ b/src/apple_silicon/src/main.rs @@ -0,0 +1,10 @@ +mod error; +mod soc; + +fn main() { + let cpu_info = soc::SocInfo::new().unwrap(); + println!( + "our CPU is an {}, and we have {} cores, and {} GPU cores", + cpu_info.cpu_brand_name, cpu_info.num_cpu_cores, cpu_info.num_gpu_cores, + ); +} diff --git a/src/apple_silicon/src/soc.rs b/src/apple_silicon/src/soc.rs new file mode 100644 index 0000000..fcf3ed3 --- /dev/null +++ b/src/apple_silicon/src/soc.rs @@ -0,0 +1,75 @@ +use crate::error::Error; + +use std::process::Command; + +pub type Result = std::result::Result; + +pub struct SocInfo { + pub cpu_brand_name: String, + pub num_cpu_cores: u16, + pub num_gpu_cores: u16, +} + +impl SocInfo { + pub fn new() -> Result { + let (cpu_brand_name, num_cpu_cores) = cpu_info()?; + + let num_gpu_cores = gpu_info()?; + + Ok(SocInfo { + cpu_brand_name, + num_cpu_cores, + num_gpu_cores, + }) + } +} + +// https://github.com/tlkh/asitop/blob/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd/asitop/utils.py#L94 +fn cpu_info() -> Result<(String, u16)> { + let binary = "/usr/sbin/sysctl"; + let args = &[ + // don't display the variable name + "-n", + "machdep.cpu.brand_string", + "machdep.cpu.core_count", + ]; + + let output = Command::new(binary).args(args).output()?; + let buffer = String::from_utf8(output.stdout)?; + + let mut iter = buffer.split('\n'); + let cpu_brand_name = match iter.next() { + Some(s) => s.to_string(), + None => return Err(Error::Parse(buffer.to_string())), + }; + + let num_cpu_cores = match iter.next() { + Some(s) => s.parse::()?, + None => return Err(Error::Parse(buffer.to_string())), + }; + + Ok((cpu_brand_name, num_cpu_cores)) +} + +// https://github.com/tlkh/asitop/blob/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd/asitop/utils.py#L120 +fn gpu_info() -> Result { + let binary = "/usr/sbin/system_profiler"; + let args = &["-detailLevel", "basic", "SPDisplaysDataType"]; + + let output = Command::new(binary).args(args).output()?; + let buffer = String::from_utf8(output.stdout)?; + + let num_gpu_cores_line = buffer + .lines() + .find(|&line| line.trim_start().starts_with("Total Number of Cores")); + + let num_gpu_cores = match num_gpu_cores_line { + Some(s) => match s.split(": ").last() { + Some(s) => s.parse::()?, + None => return Err(Error::Parse(buffer.to_string())), + }, + None => return Err(Error::Parse(buffer.to_string())), + }; + + Ok(num_gpu_cores) +} -- cgit v1.2.3 From a8f9b712274bfdb4483b37ac2b919c429af2f38e Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sat, 28 Dec 2024 13:48:07 -0800 Subject: be more explicit in the default output --- src/apple_silicon/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/apple_silicon/src/main.rs b/src/apple_silicon/src/main.rs index b54c3ec..a1b6ba1 100644 --- a/src/apple_silicon/src/main.rs +++ b/src/apple_silicon/src/main.rs @@ -4,7 +4,7 @@ mod soc; fn main() { let cpu_info = soc::SocInfo::new().unwrap(); println!( - "our CPU is an {}, and we have {} cores, and {} GPU cores", + "our CPU is an {}, and we have {} CPU cores, and {} GPU cores", cpu_info.cpu_brand_name, cpu_info.num_cpu_cores, cpu_info.num_gpu_cores, ); } -- cgit v1.2.3 From d6e2fa44ab9e6f7b8644b8e39260c9531b692fbb Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sat, 28 Dec 2024 13:48:28 -0800 Subject: document the public types --- src/apple_silicon/src/soc.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/apple_silicon/src/soc.rs b/src/apple_silicon/src/soc.rs index fcf3ed3..17ff323 100644 --- a/src/apple_silicon/src/soc.rs +++ b/src/apple_silicon/src/soc.rs @@ -4,9 +4,13 @@ use std::process::Command; pub type Result = std::result::Result; +/// Information about the Silicon chip pub struct SocInfo { + /// The CPU brand name string pub cpu_brand_name: String, + /// Number of CPU cores pub num_cpu_cores: u16, + /// Number of GPU cores pub num_gpu_cores: u16, } -- cgit v1.2.3 From 1cf606e5286eaef939fb93c228de40f93d9c8632 Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sat, 28 Dec 2024 13:48:50 -0800 Subject: use a constant value for the path to sysctl --- src/apple_silicon/src/soc.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/apple_silicon/src/soc.rs b/src/apple_silicon/src/soc.rs index 17ff323..43a29ba 100644 --- a/src/apple_silicon/src/soc.rs +++ b/src/apple_silicon/src/soc.rs @@ -29,8 +29,10 @@ impl SocInfo { } // https://github.com/tlkh/asitop/blob/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd/asitop/utils.py#L94 +const SYSCTL_PATH: &str = "/usr/sbin/sysctl"; + fn cpu_info() -> Result<(String, u16)> { - let binary = "/usr/sbin/sysctl"; + let binary = SYSCTL_PATH; let args = &[ // don't display the variable name "-n", -- cgit v1.2.3 From 36761bad994f435f009415caac011f5972001eeb Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sun, 29 Dec 2024 09:42:36 -0800 Subject: report TDP for GPU and CPU Add the TDP for the GPUs and CPUs, for the models we know about. Apple does not publish information about TDP so we're going to have to gather that information by going over various sites. Add some unit tests. --- src/apple_silicon/src/main.rs | 7 +- src/apple_silicon/src/soc.rs | 190 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 186 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/apple_silicon/src/main.rs b/src/apple_silicon/src/main.rs index a1b6ba1..18c9747 100644 --- a/src/apple_silicon/src/main.rs +++ b/src/apple_silicon/src/main.rs @@ -4,7 +4,10 @@ mod soc; fn main() { let cpu_info = soc::SocInfo::new().unwrap(); println!( - "our CPU is an {}, and we have {} CPU cores, and {} GPU cores", - cpu_info.cpu_brand_name, cpu_info.num_cpu_cores, cpu_info.num_gpu_cores, + "our CPU is an {}, and we have {} CPU cores, and {} GPU cores. The TDP is {}.", + cpu_info.cpu_brand_name, + cpu_info.num_cpu_cores, + cpu_info.num_gpu_cores, + cpu_info.cpu_max_power.unwrap(), ); } diff --git a/src/apple_silicon/src/soc.rs b/src/apple_silicon/src/soc.rs index 43a29ba..8db2e1f 100644 --- a/src/apple_silicon/src/soc.rs +++ b/src/apple_silicon/src/soc.rs @@ -1,29 +1,118 @@ use crate::error::Error; -use std::process::Command; +use std::process::{Command, Output}; pub type Result = std::result::Result; +pub type Watts = u32; +pub type Bandwidth = u32; +pub type CoreCount = u16; /// Information about the Silicon chip pub struct SocInfo { /// The CPU brand name string pub cpu_brand_name: String, /// Number of CPU cores - pub num_cpu_cores: u16, + pub num_cpu_cores: CoreCount, /// Number of GPU cores - pub num_gpu_cores: u16, + pub num_gpu_cores: CoreCount, + /// Maximum CPU power in watts (if available) + pub cpu_max_power: Option, + /// Maximum GPU power in watts (if available) + pub gpu_max_power: Option, + /// Maximum CPU bandwidth in GB/s (if available) + pub cpu_max_bw: Option, + /// Maximum GPU bandwidth in GB/s (if available) + pub gpu_max_bw: Option, + /// Number of efficiency cores + pub e_core_count: Option, + /// Number of performance cores + pub p_core_count: Option, +} + +#[derive(Debug, PartialEq)] +enum AppleChip { + M1, + M1Pro, + M1Max, + M1Ultra, + M2, + M2Pro, + M2Max, + M2Ultra, + M3, + M3Pro, + M3Max, + Unknown, +} + +struct ChipSpecs { + cpu_tdp: Watts, + gpu_tdp: Watts, +} + +impl AppleChip { + fn from_brand_string(brand: &str) -> Self { + match brand { + s if s.contains("M1 Pro") => AppleChip::M1Pro, + s if s.contains("M1 Max") => AppleChip::M1Max, + s if s.contains("M1 Ultra") => AppleChip::M1Ultra, + s if s.contains("M1") => AppleChip::M1, + s if s.contains("M2 Pro") => AppleChip::M2Pro, + s if s.contains("M2 Max") => AppleChip::M2Max, + s if s.contains("M2 Ultra") => AppleChip::M2Ultra, + s if s.contains("M2") => AppleChip::M2, + s if s.contains("M3 Pro") => AppleChip::M3Pro, + s if s.contains("M3 Max") => AppleChip::M3Max, + s if s.contains("M3") => AppleChip::M3, + _ => AppleChip::Unknown, + } + } + + fn get_specs(&self) -> ChipSpecs { + match self { + AppleChip::M1 => ChipSpecs { + cpu_tdp: 20, + gpu_tdp: 20, + }, + AppleChip::M2 => ChipSpecs { + cpu_tdp: 20, + gpu_tdp: 22, + }, + AppleChip::M2Pro => ChipSpecs { + cpu_tdp: 30, + gpu_tdp: 35, + }, + AppleChip::M2Max => ChipSpecs { + cpu_tdp: 30, + gpu_tdp: 40, + }, + // Add more variants as needed + _ => ChipSpecs { + cpu_tdp: 0, + gpu_tdp: 0, + }, + } + } } impl SocInfo { pub fn new() -> Result { - let (cpu_brand_name, num_cpu_cores) = cpu_info()?; + let (cpu_brand_name, num_cpu_cores) = cpu_info(&RealCommand)?; + let num_gpu_cores = gpu_info(&RealCommand)?; - let num_gpu_cores = gpu_info()?; + let chip = AppleChip::from_brand_string(&cpu_brand_name); + let specs = chip.get_specs(); Ok(SocInfo { cpu_brand_name, num_cpu_cores, num_gpu_cores, + cpu_max_power: Some(specs.cpu_tdp), + gpu_max_power: Some(specs.gpu_tdp), + cpu_max_bw: None, + gpu_max_bw: None, + e_core_count: None, + p_core_count: None, }) } } @@ -31,7 +120,7 @@ impl SocInfo { // https://github.com/tlkh/asitop/blob/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd/asitop/utils.py#L94 const SYSCTL_PATH: &str = "/usr/sbin/sysctl"; -fn cpu_info() -> Result<(String, u16)> { +fn cpu_info(cmd: &impl SystemCommand) -> Result<(String, u16)> { let binary = SYSCTL_PATH; let args = &[ // don't display the variable name @@ -40,7 +129,7 @@ fn cpu_info() -> Result<(String, u16)> { "machdep.cpu.core_count", ]; - let output = Command::new(binary).args(args).output()?; + let output = cmd.execute(binary, args)?; let buffer = String::from_utf8(output.stdout)?; let mut iter = buffer.split('\n'); @@ -58,11 +147,11 @@ fn cpu_info() -> Result<(String, u16)> { } // https://github.com/tlkh/asitop/blob/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd/asitop/utils.py#L120 -fn gpu_info() -> Result { +fn gpu_info(cmd: &impl SystemCommand) -> Result { let binary = "/usr/sbin/system_profiler"; let args = &["-detailLevel", "basic", "SPDisplaysDataType"]; - let output = Command::new(binary).args(args).output()?; + let output = cmd.execute(binary, args)?; let buffer = String::from_utf8(output.stdout)?; let num_gpu_cores_line = buffer @@ -79,3 +168,86 @@ fn gpu_info() -> Result { Ok(num_gpu_cores) } + +/// Trait for system command execution +pub trait SystemCommand { + fn execute(&self, binary: &str, args: &[&str]) -> Result; +} + +/// Real command executor +pub struct RealCommand; + +impl SystemCommand for RealCommand { + fn execute(&self, binary: &str, args: &[&str]) -> Result { + Ok(Command::new(binary).args(args).output()?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::os::unix::process::ExitStatusExt; + + struct MockCommand { + output: Vec, + } + + impl MockCommand { + fn new(output: &str) -> Self { + Self { + output: output.as_bytes().to_vec(), + } + } + } + + impl SystemCommand for MockCommand { + fn execute(&self, _binary: &str, _args: &[&str]) -> Result { + Ok(Output { + status: std::process::ExitStatus::from_raw(0), + stdout: self.output.clone(), + stderr: Vec::new(), + }) + } + } + + #[test] + fn test_gpu_info() { + let mock_output = r#"Graphics/Displays: + Apple M2: + Total Number of Cores: 10"#; + let cmd = MockCommand::new(mock_output); + + let result = gpu_info(&cmd); + assert_eq!(result.unwrap(), 10); + } + + #[test] + fn test_cpu_info_success() { + let mock_output = "Apple M2\n8\n"; + let cmd = MockCommand::new(mock_output); + + let result = cpu_info(&cmd); + assert!(result.is_ok()); + let (brand, cores) = result.unwrap(); + assert_eq!(brand, "Apple M2"); + assert_eq!(cores, 8); + } + + #[test] + fn test_cpu_info_missing_core_count() { + let mock_output = "Apple M2\n"; + let cmd = MockCommand::new(mock_output); + + let result = cpu_info(&cmd); + assert!(matches!(result, Err(Error::ParseInt { .. }))); + } + + #[test] + fn test_cpu_info_invalid_core_count() { + let mock_output = "Apple M2\ninvalid\n"; + let cmd = MockCommand::new(mock_output); + + let result = cpu_info(&cmd); + assert!(matches!(result, Err(Error::ParseInt { .. }))); + } +} -- cgit v1.2.3 From 4574308d41ce996a020c399a855efed13881f8db Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sun, 29 Dec 2024 13:36:02 -0800 Subject: split main.rs into bin/apple_silicon.rs and lib.rs Create a binary named `apple_silicon`, and add a library named to the project, so it can be re-used by external projects. --- src/apple_silicon/src/bin/apple_silicon.rs | 12 ++++++++++++ src/apple_silicon/src/lib.rs | 2 ++ src/apple_silicon/src/main.rs | 13 ------------- 3 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 src/apple_silicon/src/bin/apple_silicon.rs create mode 100644 src/apple_silicon/src/lib.rs delete mode 100644 src/apple_silicon/src/main.rs (limited to 'src') diff --git a/src/apple_silicon/src/bin/apple_silicon.rs b/src/apple_silicon/src/bin/apple_silicon.rs new file mode 100644 index 0000000..0f71eeb --- /dev/null +++ b/src/apple_silicon/src/bin/apple_silicon.rs @@ -0,0 +1,12 @@ +use apple_silicon::soc::SocInfo; + +fn main() { + let cpu_info = SocInfo::new().unwrap(); + println!( + "our CPU is an {}, and we have {} CPU cores, and {} GPU cores. The TDP is {}.", + cpu_info.cpu_brand_name, + cpu_info.num_cpu_cores, + cpu_info.num_gpu_cores, + cpu_info.cpu_max_power.unwrap(), + ); +} diff --git a/src/apple_silicon/src/lib.rs b/src/apple_silicon/src/lib.rs new file mode 100644 index 0000000..74bd62c --- /dev/null +++ b/src/apple_silicon/src/lib.rs @@ -0,0 +1,2 @@ +pub mod error; +pub mod soc; diff --git a/src/apple_silicon/src/main.rs b/src/apple_silicon/src/main.rs deleted file mode 100644 index 18c9747..0000000 --- a/src/apple_silicon/src/main.rs +++ /dev/null @@ -1,13 +0,0 @@ -mod error; -mod soc; - -fn main() { - let cpu_info = soc::SocInfo::new().unwrap(); - println!( - "our CPU is an {}, and we have {} CPU cores, and {} GPU cores. The TDP is {}.", - cpu_info.cpu_brand_name, - cpu_info.num_cpu_cores, - cpu_info.num_gpu_cores, - cpu_info.cpu_max_power.unwrap(), - ); -} -- cgit v1.2.3 From 6682e86f1cd481bd02f856e2bf97750050a6af70 Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sun, 29 Dec 2024 13:40:14 -0800 Subject: update project's metadata --- src/apple_silicon/Cargo.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/apple_silicon/Cargo.toml b/src/apple_silicon/Cargo.toml index 21b6242..ce33425 100644 --- a/src/apple_silicon/Cargo.toml +++ b/src/apple_silicon/Cargo.toml @@ -2,6 +2,17 @@ name = "apple_silicon" version = "0.1.0" edition = "2021" +description = "A common line tool for Apple Silicon" + +license = "MIT" +authors = ["Franck Cuny "] +repository = "https://github.com/fcuny/apple_silicon" +homepage = "https://github.com/fcuny/apple_silicon" +categories = ["command-line-utilities"] + +[[bin]] +name = "apple_silicon" +path = "src/bin/apple_silicon.rs" [dependencies] thiserror = "1.0.61" -- cgit v1.2.3 From eaa170a24d6e7851c41b17ff60cf727798f59b77 Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sun, 29 Dec 2024 13:50:05 -0800 Subject: add initial github actions --- src/apple_silicon/.github/dependabot.yml | 10 +++++++++ src/apple_silicon/.github/workflows/ci.yml | 35 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/apple_silicon/.github/dependabot.yml create mode 100644 src/apple_silicon/.github/workflows/ci.yml (limited to 'src') diff --git a/src/apple_silicon/.github/dependabot.yml b/src/apple_silicon/.github/dependabot.yml new file mode 100644 index 0000000..d062b44 --- /dev/null +++ b/src/apple_silicon/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" diff --git a/src/apple_silicon/.github/workflows/ci.yml b/src/apple_silicon/.github/workflows/ci.yml new file mode 100644 index 0000000..59ce8e7 --- /dev/null +++ b/src/apple_silicon/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: 🦀 Rust CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - uses: Swatinem/rust-cache@v2 + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Run clippy + run: cargo clippy -- -D warnings + + - name: Run tests + run: cargo test --verbose + + - name: Build + run: cargo build --verbose -- cgit v1.2.3 From ce030fb46f5dd4fd9ec974b1d3a94d808cd53140 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Dec 2024 21:51:29 +0000 Subject: Bump thiserror from 1.0.61 to 2.0.9 Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.61 to 2.0.9. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.61...2.0.9) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- src/apple_silicon/Cargo.lock | 16 ++++++++-------- src/apple_silicon/Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/apple_silicon/Cargo.lock b/src/apple_silicon/Cargo.lock index 25e57f9..222ef9d 100644 --- a/src/apple_silicon/Cargo.lock +++ b/src/apple_silicon/Cargo.lock @@ -11,9 +11,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.69" +version = "2.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" dependencies = [ "proc-macro2", "quote", @@ -40,18 +40,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", diff --git a/src/apple_silicon/Cargo.toml b/src/apple_silicon/Cargo.toml index ce33425..ea7f910 100644 --- a/src/apple_silicon/Cargo.toml +++ b/src/apple_silicon/Cargo.toml @@ -15,4 +15,4 @@ name = "apple_silicon" path = "src/bin/apple_silicon.rs" [dependencies] -thiserror = "1.0.61" +thiserror = "2.0.9" -- cgit v1.2.3 From 77d464373fb0b30d7d0ad268462e0294b855d161 Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sun, 29 Dec 2024 13:58:44 -0800 Subject: add a few more useful files to the repo --- src/apple_silicon/.editorconfig | 23 +++++++++++++++++++++++ src/apple_silicon/README.md | 1 + 2 files changed, 24 insertions(+) create mode 100644 src/apple_silicon/.editorconfig create mode 100644 src/apple_silicon/README.md (limited to 'src') diff --git a/src/apple_silicon/.editorconfig b/src/apple_silicon/.editorconfig new file mode 100644 index 0000000..9d9ad7a --- /dev/null +++ b/src/apple_silicon/.editorconfig @@ -0,0 +1,23 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space + +[*.rs] +indent_size = 4 + +[*.toml] +indent_size = 2 + +[*.{diff,patch}] +end_of_line = unset +insert_final_newline = unset +trim_trailing_whitespace = unset diff --git a/src/apple_silicon/README.md b/src/apple_silicon/README.md new file mode 100644 index 0000000..7d83327 --- /dev/null +++ b/src/apple_silicon/README.md @@ -0,0 +1 @@ +A command line utility to report information about Apple Silicon (inspired by [`asitop`](https://github.com/tlkh/asitop/tree/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd)). -- cgit v1.2.3 From 0e05e5601d069b15c07a29f7ff5371f8c15a0cc9 Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sun, 29 Dec 2024 14:20:05 -0800 Subject: report the number of E cores and P cores Report the CPU and GPU bandwidths for some of the M core models. --- src/apple_silicon/src/soc.rs | 75 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/apple_silicon/src/soc.rs b/src/apple_silicon/src/soc.rs index 8db2e1f..9c761cc 100644 --- a/src/apple_silicon/src/soc.rs +++ b/src/apple_silicon/src/soc.rs @@ -24,9 +24,9 @@ pub struct SocInfo { /// Maximum GPU bandwidth in GB/s (if available) pub gpu_max_bw: Option, /// Number of efficiency cores - pub e_core_count: Option, + pub e_core_count: CoreCount, /// Number of performance cores - pub p_core_count: Option, + pub p_core_count: CoreCount, } #[derive(Debug, PartialEq)] @@ -48,6 +48,8 @@ enum AppleChip { struct ChipSpecs { cpu_tdp: Watts, gpu_tdp: Watts, + cpu_bw: Bandwidth, + gpu_bw: Bandwidth, } impl AppleChip { @@ -73,23 +75,51 @@ impl AppleChip { AppleChip::M1 => ChipSpecs { cpu_tdp: 20, gpu_tdp: 20, + cpu_bw: 70, + gpu_bw: 70, + }, + AppleChip::M1Pro => ChipSpecs { + cpu_tdp: 30, + gpu_tdp: 30, + cpu_bw: 200, + gpu_bw: 200, + }, + AppleChip::M1Max => ChipSpecs { + cpu_tdp: 30, + gpu_tdp: 60, + cpu_bw: 250, + gpu_bw: 400, + }, + AppleChip::M1Ultra => ChipSpecs { + cpu_tdp: 60, + gpu_tdp: 120, + cpu_bw: 500, + gpu_bw: 800, }, AppleChip::M2 => ChipSpecs { - cpu_tdp: 20, - gpu_tdp: 22, + cpu_tdp: 25, + gpu_tdp: 15, + cpu_bw: 100, + gpu_bw: 100, }, AppleChip::M2Pro => ChipSpecs { cpu_tdp: 30, gpu_tdp: 35, + cpu_bw: 0, + gpu_bw: 0, }, AppleChip::M2Max => ChipSpecs { cpu_tdp: 30, gpu_tdp: 40, + cpu_bw: 0, + gpu_bw: 0, }, // Add more variants as needed _ => ChipSpecs { cpu_tdp: 0, gpu_tdp: 0, + cpu_bw: 0, + gpu_bw: 0, }, } } @@ -97,7 +127,7 @@ impl AppleChip { impl SocInfo { pub fn new() -> Result { - let (cpu_brand_name, num_cpu_cores) = cpu_info(&RealCommand)?; + let (cpu_brand_name, num_cpu_cores, e_core_count, p_core_count) = cpu_info(&RealCommand)?; let num_gpu_cores = gpu_info(&RealCommand)?; let chip = AppleChip::from_brand_string(&cpu_brand_name); @@ -109,10 +139,10 @@ impl SocInfo { num_gpu_cores, cpu_max_power: Some(specs.cpu_tdp), gpu_max_power: Some(specs.gpu_tdp), - cpu_max_bw: None, - gpu_max_bw: None, - e_core_count: None, - p_core_count: None, + cpu_max_bw: Some(specs.cpu_bw), + gpu_max_bw: Some(specs.gpu_bw), + e_core_count: e_core_count, + p_core_count: p_core_count, }) } } @@ -120,13 +150,15 @@ impl SocInfo { // https://github.com/tlkh/asitop/blob/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd/asitop/utils.py#L94 const SYSCTL_PATH: &str = "/usr/sbin/sysctl"; -fn cpu_info(cmd: &impl SystemCommand) -> Result<(String, u16)> { +fn cpu_info(cmd: &impl SystemCommand) -> Result<(String, u16, u16, u16)> { let binary = SYSCTL_PATH; let args = &[ // don't display the variable name "-n", "machdep.cpu.brand_string", "machdep.cpu.core_count", + "hw.perflevel0.logicalcpu", + "hw.perflevel1.logicalcpu", ]; let output = cmd.execute(binary, args)?; @@ -143,7 +175,22 @@ fn cpu_info(cmd: &impl SystemCommand) -> Result<(String, u16)> { None => return Err(Error::Parse(buffer.to_string())), }; - Ok((cpu_brand_name, num_cpu_cores)) + let num_performance_cores = match iter.next() { + Some(s) => s.parse::()?, + None => return Err(Error::Parse(buffer.to_string())), + }; + + let num_efficiency_cores = match iter.next() { + Some(s) => s.parse::()?, + None => return Err(Error::Parse(buffer.to_string())), + }; + + Ok(( + cpu_brand_name, + num_cpu_cores, + num_performance_cores, + num_efficiency_cores, + )) } // https://github.com/tlkh/asitop/blob/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd/asitop/utils.py#L120 @@ -223,14 +270,16 @@ mod tests { #[test] fn test_cpu_info_success() { - let mock_output = "Apple M2\n8\n"; + let mock_output = "Apple M2\n8\n4\n4\n"; let cmd = MockCommand::new(mock_output); let result = cpu_info(&cmd); assert!(result.is_ok()); - let (brand, cores) = result.unwrap(); + let (brand, cores, p_cores, e_cores) = result.unwrap(); assert_eq!(brand, "Apple M2"); assert_eq!(cores, 8); + assert_eq!(p_cores, 4); + assert_eq!(e_cores, 4); } #[test] -- cgit v1.2.3 From d7c145968ca6d2d350d065cec3f90d1a76abadd5 Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sun, 29 Dec 2024 14:24:11 -0800 Subject: fix clippy feedback --- src/apple_silicon/src/soc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/apple_silicon/src/soc.rs b/src/apple_silicon/src/soc.rs index 9c761cc..0e2bf5c 100644 --- a/src/apple_silicon/src/soc.rs +++ b/src/apple_silicon/src/soc.rs @@ -141,8 +141,8 @@ impl SocInfo { gpu_max_power: Some(specs.gpu_tdp), cpu_max_bw: Some(specs.cpu_bw), gpu_max_bw: Some(specs.gpu_bw), - e_core_count: e_core_count, - p_core_count: p_core_count, + e_core_count, + p_core_count, }) } } -- cgit v1.2.3 From dc9d77ae7e57a78be0b852cadfc90e676c7f964f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:37:11 +0000 Subject: Bump thiserror from 2.0.9 to 2.0.11 Bumps [thiserror](https://github.com/dtolnay/thiserror) from 2.0.9 to 2.0.11. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/2.0.9...2.0.11) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- src/apple_silicon/Cargo.lock | 8 ++++---- src/apple_silicon/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/apple_silicon/Cargo.lock b/src/apple_silicon/Cargo.lock index 222ef9d..1ee0f1d 100644 --- a/src/apple_silicon/Cargo.lock +++ b/src/apple_silicon/Cargo.lock @@ -40,18 +40,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", diff --git a/src/apple_silicon/Cargo.toml b/src/apple_silicon/Cargo.toml index ea7f910..65dcf5e 100644 --- a/src/apple_silicon/Cargo.toml +++ b/src/apple_silicon/Cargo.toml @@ -15,4 +15,4 @@ name = "apple_silicon" path = "src/bin/apple_silicon.rs" [dependencies] -thiserror = "2.0.9" +thiserror = "2.0.11" -- cgit v1.2.3