use raw_cpuid::{self, CpuId, CpuIdReaderNative};
pub use raw_cpuid::CacheType;
#[inline]
fn amd_is_zen(cpuid: &CpuId<CpuIdReaderNative>) -> Option<bool> {
let info = cpuid.get_feature_info()?;
match (info.base_family_id(), info.extended_family_id()) {
(0xF, 0x8..=0xA) => Some(true),
_ => Some(false),
}
}
#[inline]
fn generic_cache_size(
cpuid: CpuId<CpuIdReaderNative>,
level: u8,
cache_type: CacheType,
) -> Option<usize> {
cpuid
.get_cache_parameters()?
.filter(|c| c.level() == level && c.cache_type() == cache_type)
.map(|c| c.sets() * c.associativity() * c.coherency_line_size())
.min()
}
#[inline]
fn amd_cache_size(
cpuid: CpuId<CpuIdReaderNative>,
level: u8,
cache_type: CacheType,
) -> Option<usize> {
match (level, cache_type) {
(1, CacheType::Instruction) => cpuid
.get_l1_cache_and_tlb_info()
.map(|i| i.icache_size() as usize * 1024),
(1, CacheType::Data) => cpuid
.get_l1_cache_and_tlb_info()
.map(|i| i.icache_size() as usize * 1024),
(2, CacheType::Unified) => cpuid
.get_l2_l3_cache_and_tlb_info()
.map(|i| i.l2cache_size() as usize * 1024),
(3, CacheType::Unified) => cpuid
.get_l2_l3_cache_and_tlb_info()
.map(|i| i.l3cache_size() as usize * 1024),
_ => None,
}
}
#[inline]
pub fn cache_size(level: u8, cache_type: CacheType) -> Option<usize> {
let cpuid = CpuId::new();
match cpuid.get_vendor_info()?.as_str() {
"AuthenticAMD" if amd_is_zen(&cpuid).unwrap_or(false) => {
amd_cache_size(cpuid, level, cache_type)
}
_ => generic_cache_size(cpuid, level, cache_type),
}
}
#[inline]
fn generic_cache_line_size(
cpuid: CpuId<CpuIdReaderNative>,
level: u8,
cache_type: CacheType,
) -> Option<usize> {
cpuid
.get_cache_parameters()?
.filter(|cparams| cparams.level() == level && cparams.cache_type() == cache_type)
.map(|cparams| cparams.coherency_line_size())
.min()
}
#[inline]
fn amd_cache_line_size(
cpuid: CpuId<CpuIdReaderNative>,
level: u8,
cache_type: CacheType,
) -> Option<usize> {
match (level, cache_type) {
(1, CacheType::Instruction) => cpuid
.get_l1_cache_and_tlb_info()
.map(|i| i.icache_line_size() as usize),
(1, CacheType::Data) => cpuid
.get_l1_cache_and_tlb_info()
.map(|i| i.dcache_line_size() as usize),
(2, CacheType::Unified) => cpuid
.get_l2_l3_cache_and_tlb_info()
.map(|i| i.l2cache_line_size() as usize),
(3, CacheType::Unified) => cpuid
.get_l2_l3_cache_and_tlb_info()
.map(|i| i.l3cache_line_size() as usize),
_ => None,
}
}
#[inline]
pub fn cache_line_size(level: u8, cache_type: CacheType) -> Option<usize> {
let cpuid = CpuId::new();
match cpuid.get_vendor_info()?.as_str() {
"AuthenticAMD" if amd_is_zen(&cpuid).unwrap_or(false) => {
amd_cache_line_size(cpuid, level, cache_type)
}
_ => generic_cache_line_size(cpuid, level, cache_type),
}
}
#[inline]
pub fn l1_cache_size() -> Option<usize> {
cache_size(1, CacheType::Data)
}
#[inline]
pub fn l1_cache_line_size() -> Option<usize> {
cache_line_size(1, CacheType::Data)
}
#[inline]
pub fn l2_cache_size() -> Option<usize> {
cache_size(2, CacheType::Unified)
}
#[inline]
pub fn l2_cache_line_size() -> Option<usize> {
cache_line_size(2, CacheType::Unified)
}
#[inline]
pub fn l3_cache_size() -> Option<usize> {
cache_size(3, CacheType::Unified)
}
#[inline]
pub fn l3_cache_line_size() -> Option<usize> {
cache_line_size(3, CacheType::Unified)
}
#[cfg(test)]
mod tests {}