From 57710ab1c3e5efc444a41ff52dfe0efef2a11f9b Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Thu, 3 Apr 2025 18:56:12 +0900 Subject: [PATCH 01/17] feat(dvfs): update Dvfs trait Signed-off-by: Yuuki Takano --- awkernel_lib/src/arch/x86_64/dvfs.rs | 14 +++-- .../src/arch/x86_64/dvfs/hwpstate_intel.rs | 59 +++++++++++++++++- awkernel_lib/src/dvfs.rs | 62 +++++++++++++++++-- userland/Cargo.toml | 3 +- 4 files changed, 125 insertions(+), 13 deletions(-) diff --git a/awkernel_lib/src/arch/x86_64/dvfs.rs b/awkernel_lib/src/arch/x86_64/dvfs.rs index 4c93c0895..96340a6e6 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs.rs @@ -17,7 +17,7 @@ const IA32_MISC_ENABLE: u32 = 0x1A0; impl Dvfs for X86 { /// Fix the frequency of the current CPU. - fn fix_freq(freq_mhz: u64) { + fn fix_freq(freq_mhz: u64) -> bool { unsafe { let mut misc_enable = Msr::new(IA32_MISC_ENABLE); let mut value = misc_enable.read(); @@ -43,26 +43,28 @@ impl Dvfs for X86 { value |= target_pstate; perf_ctl.write(value); } + + true } /// Get the maximum frequency of the current CPU. - fn get_max_freq() -> u64 { + fn get_max_freq() -> Option { unsafe { let platform_info = Msr::new(MSR_PLATFORM_INFO); let max_ratio = (platform_info.read() >> 8) & 0xFF; let bus_freq_mhz = (__cpuid(0x16).ecx & 0xffff) as u64; - max_ratio * bus_freq_mhz + Some(max_ratio * bus_freq_mhz) } } /// Get the current frequency of the current CPU. - fn get_curr_freq() -> u64 { + fn get_curr_freq() -> Option { // Check if the CPU supports the IA32_PERF_MPERF and IA32_PERF_APERF MSRs. let cpuid = unsafe { __cpuid(0x6) }; if (cpuid.ecx & 0x1) == 0 { log::warn!("The CPU does not support IA32_PERF_MPERF and IA32_PERF_APERF MSRs."); - return 0; + return None; } unsafe { @@ -76,7 +78,7 @@ impl Dvfs for X86 { let mperf_delta = mperf.read(); let aperf_delta = aperf.read(); - aperf_delta * Self::get_max_freq() / mperf_delta + Some(aperf_delta * Self::get_max_freq()? / mperf_delta) } } } diff --git a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs index 70435e0cc..54a4dec64 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs @@ -1,8 +1,14 @@ use core::arch::x86_64::__cpuid; +use array_macro::array; +use awkernel_sync::{mcs::MCSNode, mutex::Mutex}; use x86_64::registers::model_specific::Msr; -use crate::{arch::x86_64::msr::*, cpu::cpu_id}; +use crate::{ + arch::x86_64::msr::*, + cpu::{cpu_id, NUM_MAX_CPU}, + dvfs::Dvfs, +}; const CPUTPM1_HWP_NOTIFICATION: u32 = 0x00000100; const CPUTPM1_HWP_ACTIVITY_WINDOW: u32 = 0x00000200; @@ -310,3 +316,54 @@ fn percent_to_raw_perf_bias(x: u64) -> u64 { assert!(x <= 100); ((0xf * x) + 50) / 100 } + +static HWPSTATE_INTEL: [Mutex>; NUM_MAX_CPU] = + array![_ => Mutex::new(None); NUM_MAX_CPU]; + +struct HwPstateIntelImpl; + +impl Dvfs for HwPstateIntelImpl { + fn set_min_performance(min: u8) -> bool { + let cpu_id = cpu_id(); + + let mut node = MCSNode::new(); + let mut hwps = HWPSTATE_INTEL[cpu_id].lock(&mut node); + + if let Some(hwps) = hwps.as_mut() { + hwps.minimum_performance_select(min) + } else { + false + } + } + + fn set_max_performance(max: u8) -> bool { + let cpu_id = cpu_id(); + + let mut node = MCSNode::new(); + let mut hwps = HWPSTATE_INTEL[cpu_id].lock(&mut node); + + if let Some(hwps) = hwps.as_mut() { + hwps.maximum_performance_select(max) + } else { + false + } + } +} + +/// Initialize Intel Hardware-controlled Performance States +/// This function should be called before the main loop on each CPU core. +/// +/// # Safety +/// +/// This function must be called once by each CPU core. +pub(super) unsafe fn init() { + let cpu_id = cpu_id(); + + let hwps = &HWPSTATE_INTEL[cpu_id]; + let mut node = MCSNode::new(); + let mut hwps = hwps.lock(&mut node); + + if hwps.is_none() { + *hwps = HwPstateIntel::new(); + } +} diff --git a/awkernel_lib/src/dvfs.rs b/awkernel_lib/src/dvfs.rs index 8b26212b0..2cd5809a8 100644 --- a/awkernel_lib/src/dvfs.rs +++ b/awkernel_lib/src/dvfs.rs @@ -1,12 +1,64 @@ +#[allow(unused_variables)] pub trait Dvfs { /// Fix the frequency of the current CPU. - fn fix_freq(freq: u64); + /// + /// If current driver does not support this operation, + /// it will return `false`. + fn fix_freq(freq: u64) -> bool { + false + } /// Get the maximum frequency of the current CPU. - fn get_max_freq() -> u64; + /// + /// If current driver does not support this operation, + /// it will return `None`. + fn get_max_freq() -> Option { + None + } /// Get the frequency of the current CPU. - fn get_curr_freq() -> u64; + /// + /// If current driver does not support this operation, + /// it will return `None`. + fn get_curr_freq() -> Option { + None + } + + /// Select the Minimum Performance. + /// (range from 0, lowest performant, through 100, highest performance) + /// + /// If current driver does not support this operation, + /// it will return `false`. + fn set_min_performance(min: u8) -> bool { + false + } + + /// Get the Minimum Performance. + /// (range from 0, lowest performant, through 100, highest performance) + /// + /// If current driver does not support this operation, + /// it will return `None`. + fn get_min_performance() -> Option { + None + } + + /// Set the Maximum Performance. + /// (range from 0, lowest performant, through 100, highest performance) + /// + /// If current driver does not support this operation, + /// it will return `false`. + fn set_max_performance(max: u8) -> bool { + false + } + + /// Get the Maximum Performance. + /// (range from 0, lowest performant, through 100, highest performance) + /// + /// If current driver does not support this operation, + /// it will return `None`. + fn get_max_performance() -> Option { + None + } } /// Fix the frequency of the current CPU. @@ -17,12 +69,12 @@ pub fn fix_freq(freq: u64) { /// Get the maximum frequency of the current CPU. #[inline(always)] -pub fn get_max_freq() -> u64 { +pub fn get_max_freq() -> Option { crate::arch::ArchImpl::get_max_freq() } /// Get the frequency of the current CPU. #[inline(always)] -pub fn get_curr_freq() -> u64 { +pub fn get_curr_freq() -> Option { crate::arch::ArchImpl::get_curr_freq() } diff --git a/userland/Cargo.toml b/userland/Cargo.toml index af012dd65..864a81101 100644 --- a/userland/Cargo.toml +++ b/userland/Cargo.toml @@ -75,7 +75,7 @@ path = "../applications/tests/test_dvfs" optional = true [features] -default = [] +default = ["test_dvfs"] perf = ["awkernel_services/perf"] # Test applications @@ -93,3 +93,4 @@ test_measure_channel = ["dep:test_measure_channel"] test_measure_channel_heavy = ["dep:test_measure_channel_heavy"] test_sched_preempt = ["dep:test_sched_preempt"] test_dag = ["dep:test_dag"] +test_dvfs = ["dep:test_dvfs"] From 0cc6369056df5502fb84891d198211c7b8959c30 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Fri, 4 Apr 2025 11:18:46 +0900 Subject: [PATCH 02/17] feat(dvfs): initialize hwpstate_intel Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/src/lib.rs | 32 +++------ awkernel_lib/src/arch/aarch64/dvfs.rs | 19 +----- awkernel_lib/src/arch/rv32/dvfs.rs | 19 +----- awkernel_lib/src/arch/rv64/dvfs.rs | 19 +----- awkernel_lib/src/arch/std_common/dvfs.rs | 19 +----- awkernel_lib/src/arch/x86_64.rs | 2 +- awkernel_lib/src/arch/x86_64/cpu.rs | 66 +++++++++++++++++++ awkernel_lib/src/arch/x86_64/dvfs.rs | 18 ++++- .../src/arch/x86_64/dvfs/hwpstate_intel.rs | 10 +-- kernel/src/arch/x86_64/kernel_main.rs | 10 ++- 10 files changed, 111 insertions(+), 103 deletions(-) diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index 74beface1..39f2b29f0 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -19,42 +19,28 @@ pub async fn run() { async fn test_dvfs() { loop { - let max = awkernel_lib::dvfs::get_max_freq(); - let cpuid = awkernel_lib::cpu::cpu_id(); - - // Maximum frequency. - awkernel_lib::dvfs::fix_freq(max); - - let start = awkernel_async_lib::time::Time::now(); + // TODO: update CPU freq + let t = awkernel_async_lib::time::Time::now(); for _ in 0..NUM_LOOP { core::hint::black_box(()); } - let t = start.elapsed(); - - let current = awkernel_lib::dvfs::get_curr_freq(); - - log::debug!( - "cpuid = {cpuid}, max = {max}, current = {current}, expected = {max}, time = {t:?}" - ); + let elapsed1 = t.elapsed(); - // Maximum / 2 frequency. - awkernel_lib::dvfs::fix_freq(max / 2); + // TODO: update CPU freq - let start = awkernel_async_lib::time::Time::now(); + let t = awkernel_async_lib::time::Time::now(); for _ in 0..NUM_LOOP { core::hint::black_box(()); } - - let t = start.elapsed(); - - let current = awkernel_lib::dvfs::get_curr_freq(); + let elapsed2 = t.elapsed(); log::debug!( - "cpuid = {cpuid}, max = {max}, current = {current}, expected = {}, time = {t:?}", - max / 2 + "first loop = {} [us], second loop = {} [us]", + elapsed1.as_micros(), + elapsed2.as_micros() ); awkernel_async_lib::sleep(Duration::from_secs(1)).await; diff --git a/awkernel_lib/src/arch/aarch64/dvfs.rs b/awkernel_lib/src/arch/aarch64/dvfs.rs index 63332b7d8..05710d5b4 100644 --- a/awkernel_lib/src/arch/aarch64/dvfs.rs +++ b/awkernel_lib/src/arch/aarch64/dvfs.rs @@ -2,21 +2,4 @@ use crate::dvfs::Dvfs; use super::AArch64; -impl Dvfs for AArch64 { - /// Fix the frequency of the current CPU. - fn fix_freq(_freq: u64) { - // TODO: Implement this. - } - - /// Get the maximum frequency of the current CPU. - fn get_max_freq() -> u64 { - // TODO: Implement this. - 0 - } - - /// Get the current frequency of the current CPU. - fn get_curr_freq() -> u64 { - // TODO: Implement this. - 0 - } -} +impl Dvfs for AArch64 {} diff --git a/awkernel_lib/src/arch/rv32/dvfs.rs b/awkernel_lib/src/arch/rv32/dvfs.rs index 42059b9bb..7c57905a1 100644 --- a/awkernel_lib/src/arch/rv32/dvfs.rs +++ b/awkernel_lib/src/arch/rv32/dvfs.rs @@ -2,21 +2,4 @@ use crate::dvfs::Dvfs; use super::RV32; -impl Dvfs for RV32 { - /// Fix the frequency of the current CPU. - fn fix_freq(_freq: u64) { - // TODO: Implement this. - } - - /// Get the maximum frequency of the current CPU. - fn get_max_freq() -> u64 { - // TODO: Implement this. - 0 - } - - /// Get the current frequency of the current CPU. - fn get_curr_freq() -> u64 { - // TODO: Implement this. - 0 - } -} +impl Dvfs for RV32 {} diff --git a/awkernel_lib/src/arch/rv64/dvfs.rs b/awkernel_lib/src/arch/rv64/dvfs.rs index 15c7906a8..ad2b9c6f4 100644 --- a/awkernel_lib/src/arch/rv64/dvfs.rs +++ b/awkernel_lib/src/arch/rv64/dvfs.rs @@ -2,21 +2,4 @@ use crate::dvfs::Dvfs; use super::RV64; -impl Dvfs for RV64 { - /// Fix the frequency of the current CPU. - fn fix_freq(_freq: u64) { - // TODO: Implement this. - } - - /// Get the maximum frequency of the current CPU. - fn get_max_freq() -> u64 { - // TODO: Implement this. - 0 - } - - /// Get the current frequency of the current CPU. - fn get_curr_freq() -> u64 { - // TODO: Implement this. - 0 - } -} +impl Dvfs for RV64 {} diff --git a/awkernel_lib/src/arch/std_common/dvfs.rs b/awkernel_lib/src/arch/std_common/dvfs.rs index 5e5098d59..39db1df81 100644 --- a/awkernel_lib/src/arch/std_common/dvfs.rs +++ b/awkernel_lib/src/arch/std_common/dvfs.rs @@ -2,21 +2,4 @@ use crate::dvfs::Dvfs; use super::StdCommon; -impl Dvfs for StdCommon { - /// Fix the frequency of the current CPU. - fn fix_freq(_freq: u64) { - // no operation - } - - /// Get the maximum frequency of the current CPU. - fn get_max_freq() -> u64 { - // no operation1 - 1 - } - - /// Get the current frequency of the current CPU. - fn get_curr_freq() -> u64 { - // no operation - 1 - } -} +impl Dvfs for StdCommon {} diff --git a/awkernel_lib/src/arch/x86_64.rs b/awkernel_lib/src/arch/x86_64.rs index 5b9ebf032..56907a169 100644 --- a/awkernel_lib/src/arch/x86_64.rs +++ b/awkernel_lib/src/arch/x86_64.rs @@ -4,7 +4,7 @@ use ::acpi::AcpiTables; pub mod acpi; pub mod cpu; pub mod delay; -pub(super) mod dvfs; +pub mod dvfs; pub mod fault; pub(super) mod interrupt; pub mod interrupt_remap; diff --git a/awkernel_lib/src/arch/x86_64/cpu.rs b/awkernel_lib/src/arch/x86_64/cpu.rs index 34e4f6bc6..9d402988b 100644 --- a/awkernel_lib/src/arch/x86_64/cpu.rs +++ b/awkernel_lib/src/arch/x86_64/cpu.rs @@ -7,6 +7,72 @@ struct RawCpuIdAndCpuId { cpu_id: usize, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CPUVendor { + Intel, + AMD, + Hygon, + Centaur, +} + +#[derive(Debug)] +struct CPUVendorStr { + vendor_id: CPUVendor, + vendor: &'static str, +} + +const CPU_VENDORS: [CPUVendorStr; 4] = [ + CPUVendorStr { + vendor_id: CPUVendor::Intel, + vendor: "GenuineIntel", + }, + CPUVendorStr { + vendor_id: CPUVendor::AMD, + vendor: "AuthenticAMD", + }, + CPUVendorStr { + vendor_id: CPUVendor::Hygon, + vendor: "HygonGenuine", + }, + CPUVendorStr { + vendor_id: CPUVendor::Centaur, + vendor: "CentaurHauls", + }, +]; + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +struct CPUId { + ebx: u32, + edx: u32, + ecx: u32, +} + +union CPUVendorData { + cpuid: CPUId, + vendor_string: [u8; 12], +} + +pub fn get_cpu_vendor() -> Option { + let cpuid = unsafe { core::arch::x86_64::__cpuid(0) }; + let cpuid = CPUId { + ebx: cpuid.ebx, + edx: cpuid.edx, + ecx: cpuid.ecx, + }; + + let vendor_data = CPUVendorData { cpuid }; + let vendor_str = unsafe { core::str::from_utf8(&vendor_data.vendor_string).unwrap() }; + + for vendor in CPU_VENDORS.iter() { + if vendor.vendor == vendor_str { + return Some(vendor.vendor_id); + } + } + + None +} + static mut CPU_ID_NUMA_ID: [u8; NUM_MAX_CPU] = [0; NUM_MAX_CPU]; static mut RAW_CPU_ID_AND_CPU_ID: [RawCpuIdAndCpuId; NUM_MAX_CPU] = [RawCpuIdAndCpuId { diff --git a/awkernel_lib/src/arch/x86_64/dvfs.rs b/awkernel_lib/src/arch/x86_64/dvfs.rs index 96340a6e6..627a0739e 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs.rs @@ -4,7 +4,10 @@ use x86_64::registers::model_specific::Msr; use crate::{delay::wait_millisec, dvfs::Dvfs}; -use super::X86; +use super::{ + cpu::{self, CPUVendor}, + X86, +}; #[allow(dead_code)] // TODO: remove this later mod hwpstate_intel; @@ -82,3 +85,16 @@ impl Dvfs for X86 { } } } + +/// Initialize DVFS. +/// +/// # Safety +/// +/// This function must be called once by each CPU core. +pub unsafe fn init() { + if let Some(CPUVendor::Intel) = cpu::get_cpu_vendor() { + if !hwpstate_intel::init() { + log::warn!("Failed to initialize Intel Hardware-controlled Performance States."); + } + } +} diff --git a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs index 54a4dec64..398892c61 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs @@ -93,18 +93,18 @@ impl HwPstateIntel { } if let Some(result) = rdmsr_safe(&hwp_req) { - log::error!("Failed to read HWP request MSR for cpu{}", cpu_id()); self.req = result; } else { + log::error!("Failed to read HWP request MSR for cpu{}", cpu_id()); return false; } let hwp_caps = Msr::new(MSR_IA32_HWP_CAPABILITIES); if let Some(result) = rdmsr_safe(&hwp_caps) { - log::error!("Failed to read HWP capabilities MSR for cpu{}", cpu_id()); caps = result; } else { + log::error!("Failed to read HWP capabilities MSR for cpu{}", cpu_id()); return false; } } @@ -320,7 +320,7 @@ fn percent_to_raw_perf_bias(x: u64) -> u64 { static HWPSTATE_INTEL: [Mutex>; NUM_MAX_CPU] = array![_ => Mutex::new(None); NUM_MAX_CPU]; -struct HwPstateIntelImpl; +pub(super) struct HwPstateIntelImpl; impl Dvfs for HwPstateIntelImpl { fn set_min_performance(min: u8) -> bool { @@ -356,7 +356,7 @@ impl Dvfs for HwPstateIntelImpl { /// # Safety /// /// This function must be called once by each CPU core. -pub(super) unsafe fn init() { +pub(super) unsafe fn init() -> bool { let cpu_id = cpu_id(); let hwps = &HWPSTATE_INTEL[cpu_id]; @@ -366,4 +366,6 @@ pub(super) unsafe fn init() { if hwps.is_none() { *hwps = HwPstateIntel::new(); } + + hwps.is_some() } diff --git a/kernel/src/arch/x86_64/kernel_main.rs b/kernel/src/arch/x86_64/kernel_main.rs index 252cbfa27..3901f5f49 100644 --- a/kernel/src/arch/x86_64/kernel_main.rs +++ b/kernel/src/arch/x86_64/kernel_main.rs @@ -96,7 +96,8 @@ const MPBOOT_REGION_END: u64 = 1024 * 1024; /// 16. Initialize PCIe devices. /// 17. Initialize interrupt handlers. /// 18. Synchronize TSC. -/// 19. Call `crate::main()`. +/// 19. Initialize DVFS. +/// 20. Call `crate::main()`. fn kernel_main(boot_info: &'static mut BootInfo) -> ! { unsafe { crate::config::init() }; // 0. Initialize the configuration. @@ -341,7 +342,10 @@ fn kernel_main2( num_cpu: non_primary_cpus.len() + 1, }; - // 19. Call `crate::main()`. + // 19. Initialize DVFS. + unsafe { awkernel_lib::arch::x86_64::dvfs::init() }; + + // 20. Call `crate::main()`. crate::main(kernel_info); } @@ -539,6 +543,8 @@ fn non_primary_kernel_main() -> ! { num_cpu, }; + unsafe { awkernel_lib::arch::x86_64::dvfs::init() }; + crate::main(kernel_info); // jump to userland wait_forever(); From 4a49bc375da3c6e3d6aeaf012a95d304dc5847cc Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Fri, 4 Apr 2025 18:23:34 +0900 Subject: [PATCH 03/17] feat(dvfs): add `set_[min|max]_performance()` functions Signed-off-by: Yuuki Takano --- awkernel_lib/src/arch/x86_64/dvfs.rs | 8 ++++++++ awkernel_lib/src/dvfs.rs | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/awkernel_lib/src/arch/x86_64/dvfs.rs b/awkernel_lib/src/arch/x86_64/dvfs.rs index 627a0739e..44eca409f 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs.rs @@ -84,6 +84,14 @@ impl Dvfs for X86 { Some(aperf_delta * Self::get_max_freq()? / mperf_delta) } } + + fn set_min_performance(min: u8) -> bool { + hwpstate_intel::HwPstateIntelImpl::set_min_performance(min) + } + + fn set_max_performance(max: u8) -> bool { + hwpstate_intel::HwPstateIntelImpl::set_max_performance(max) + } } /// Initialize DVFS. diff --git a/awkernel_lib/src/dvfs.rs b/awkernel_lib/src/dvfs.rs index 2cd5809a8..d69e23a34 100644 --- a/awkernel_lib/src/dvfs.rs +++ b/awkernel_lib/src/dvfs.rs @@ -78,3 +78,21 @@ pub fn get_max_freq() -> Option { pub fn get_curr_freq() -> Option { crate::arch::ArchImpl::get_curr_freq() } + +/// Set Maximum Performance. +/// (range from 0, lowest performant, through 100, highest performance) +/// +/// If the driver does not support `set_max_performance()`, `false` will be returned. +#[inline(always)] +pub fn set_max_performance(max: u8) -> bool { + crate::arch::ArchImpl::set_max_performance(max) +} + +/// Set Minimum Performance. +/// (range from 0, lowest performant, through 100, highest performance) +/// +/// If the driver does not support `set_min_performance()`, `false` will be returned. +#[inline(always)] +pub fn set_min_performance(min: u8) -> bool { + crate::arch::ArchImpl::set_min_performance(min) +} From a7e27663770f204e36f1b47c0db6af6d20490b8a Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Fri, 4 Apr 2025 19:01:31 +0900 Subject: [PATCH 04/17] feat(dvfs): set_energy_efficiency() Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/src/lib.rs | 32 +++++++++---------- .../src/arch/x86_64/dvfs/hwpstate_intel.rs | 31 ++++++++++++++++-- awkernel_lib/src/dvfs.rs | 27 ++++++++++++---- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index 39f2b29f0..b351a0bb4 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -6,7 +6,7 @@ extern crate alloc; const APP_NAME: &str = "test DVFS"; -const NUM_LOOP: usize = 1000000; +const NUM_LOOP: usize = 100000000; pub async fn run() { awkernel_async_lib::spawn( @@ -19,26 +19,16 @@ pub async fn run() { async fn test_dvfs() { loop { - // TODO: update CPU freq - let t = awkernel_async_lib::time::Time::now(); + let cpu_id = awkernel_lib::cpu::cpu_id(); - for _ in 0..NUM_LOOP { - core::hint::black_box(()); - } + awkernel_lib::dvfs::set_max_performance(100); + let elapsed1 = empty_loop(); - let elapsed1 = t.elapsed(); - - // TODO: update CPU freq - - let t = awkernel_async_lib::time::Time::now(); - - for _ in 0..NUM_LOOP { - core::hint::black_box(()); - } - let elapsed2 = t.elapsed(); + awkernel_lib::dvfs::set_max_performance(50); + let elapsed2 = empty_loop(); log::debug!( - "first loop = {} [us], second loop = {} [us]", + "cpu_id = {cpu_id:02}, first loop = {} [us], second loop = {} [us]", elapsed1.as_micros(), elapsed2.as_micros() ); @@ -46,3 +36,11 @@ async fn test_dvfs() { awkernel_async_lib::sleep(Duration::from_secs(1)).await; } } + +fn empty_loop() -> Duration { + let t = awkernel_async_lib::time::Time::now(); + for _ in 0..NUM_LOOP { + core::hint::black_box(()); + } + t.elapsed() +} diff --git a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs index 398892c61..dc49a6d09 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs @@ -190,7 +190,7 @@ impl HwPstateIntel { } /// Select Efficiency/Performance Preference. - /// (range from 0, most performant, through 100, most efficient) + /// (range from 0, most performance, through 100, most efficient) pub(super) fn epp_select(&mut self, epp: u8) -> bool { let epp = if epp > 100 { 100 } else { epp }; @@ -213,7 +213,7 @@ impl HwPstateIntel { } /// Select Maximum Performance. - /// (range from 0, lowest performant, through 100, highest performance) + /// (range from 0, lowest performance, through 100, highest performance) /// /// If `max` is less than the minimum performance, /// this function sets the maximum performance to the minimum performance. @@ -230,7 +230,7 @@ impl HwPstateIntel { } /// Select Minimum Performance. - /// (range from 0, lowest performant, through 100, highest performance) + /// (range from 0, lowest performance, through 100, highest performance) /// /// If `min` is greater than the maximum performance, /// this function sets the minimum performance to the maximum performance. @@ -285,6 +285,18 @@ impl HwPstateIntel { val as u8 } } + + /// Set Energy_Performance_Preference. + /// (range from 0, highest performance, through 100, highest energy efficient) + fn set_energy_performance_preference(&mut self, percent: u8) -> bool { + let percent = if percent > 100 { 100 } else { percent }; + let raw_val = percent_to_raw(percent as u64); + + self.req &= !IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE; + self.req |= raw_val << 24; + + self.request() + } } /// Given x * 10 in [0, 1000], round to the integer nearest x. @@ -348,6 +360,19 @@ impl Dvfs for HwPstateIntelImpl { false } } + + fn set_energy_efficiency(val: u8) -> bool { + let cpu_id = cpu_id(); + + let mut node = MCSNode::new(); + let mut hwps = HWPSTATE_INTEL[cpu_id].lock(&mut node); + + if let Some(hwps) = hwps.as_mut() { + hwps.set_energy_performance_preference(val) + } else { + false + } + } } /// Initialize Intel Hardware-controlled Performance States diff --git a/awkernel_lib/src/dvfs.rs b/awkernel_lib/src/dvfs.rs index d69e23a34..c7cd6a5d0 100644 --- a/awkernel_lib/src/dvfs.rs +++ b/awkernel_lib/src/dvfs.rs @@ -25,7 +25,7 @@ pub trait Dvfs { } /// Select the Minimum Performance. - /// (range from 0, lowest performant, through 100, highest performance) + /// (range from 0, lowest performance, through 100, highest performance) /// /// If current driver does not support this operation, /// it will return `false`. @@ -34,7 +34,7 @@ pub trait Dvfs { } /// Get the Minimum Performance. - /// (range from 0, lowest performant, through 100, highest performance) + /// (range from 0, lowest performance, through 100, highest performance) /// /// If current driver does not support this operation, /// it will return `None`. @@ -43,7 +43,7 @@ pub trait Dvfs { } /// Set the Maximum Performance. - /// (range from 0, lowest performant, through 100, highest performance) + /// (range from 0, lowest performance, through 100, highest performance) /// /// If current driver does not support this operation, /// it will return `false`. @@ -52,13 +52,19 @@ pub trait Dvfs { } /// Get the Maximum Performance. - /// (range from 0, lowest performant, through 100, highest performance) + /// (range from 0, lowest performance, through 100, highest performance) /// /// If current driver does not support this operation, /// it will return `None`. fn get_max_performance() -> Option { None } + + /// Set the Energy Efficiency Preference. + /// (range from 0, highest performance, through 100, highest energy efficient) + fn set_energy_efficiency(val: u8) -> bool { + false + } } /// Fix the frequency of the current CPU. @@ -80,7 +86,7 @@ pub fn get_curr_freq() -> Option { } /// Set Maximum Performance. -/// (range from 0, lowest performant, through 100, highest performance) +/// (range from 0, lowest performance, through 100, highest performance) /// /// If the driver does not support `set_max_performance()`, `false` will be returned. #[inline(always)] @@ -89,10 +95,19 @@ pub fn set_max_performance(max: u8) -> bool { } /// Set Minimum Performance. -/// (range from 0, lowest performant, through 100, highest performance) +/// (range from 0, lowest performance, through 100, highest performance) /// /// If the driver does not support `set_min_performance()`, `false` will be returned. #[inline(always)] pub fn set_min_performance(min: u8) -> bool { crate::arch::ArchImpl::set_min_performance(min) } + +/// Set the Energy Efficiency Preference. +/// (range from 0, highest performance, through 100, highest energy efficient) +/// +/// If the driver does not support `set_energy_efficiency()`, `false` will be returned. +#[inline(always)] +pub fn set_energy_efficiency(val: u8) -> bool { + crate::arch::ArchImpl::set_energy_efficiency(val) +} From a63c78d279266ae434a37bd1ab98c27ee86bb58b Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Mon, 7 Apr 2025 19:07:30 +0900 Subject: [PATCH 05/17] feat(dvfs): add `set_desired_performance()` method Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/src/lib.rs | 6 ++-- awkernel_lib/src/arch/x86_64/dvfs.rs | 4 +++ .../src/arch/x86_64/dvfs/hwpstate_intel.rs | 34 ++++++++++++++++++- awkernel_lib/src/dvfs.rs | 27 +++++++++++++++ 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index b351a0bb4..b1364dcdc 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -2,6 +2,8 @@ use core::time::Duration; +use awkernel_lib::dvfs::DesiredPerformance; + extern crate alloc; const APP_NAME: &str = "test DVFS"; @@ -21,10 +23,10 @@ async fn test_dvfs() { loop { let cpu_id = awkernel_lib::cpu::cpu_id(); - awkernel_lib::dvfs::set_max_performance(100); + awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Desired(100)); let elapsed1 = empty_loop(); - awkernel_lib::dvfs::set_max_performance(50); + awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Desired(50)); let elapsed2 = empty_loop(); log::debug!( diff --git a/awkernel_lib/src/arch/x86_64/dvfs.rs b/awkernel_lib/src/arch/x86_64/dvfs.rs index 44eca409f..41182b1cd 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs.rs @@ -92,6 +92,10 @@ impl Dvfs for X86 { fn set_max_performance(max: u8) -> bool { hwpstate_intel::HwPstateIntelImpl::set_max_performance(max) } + + fn set_desired_performance(val: crate::dvfs::DesiredPerformance) -> bool { + hwpstate_intel::HwPstateIntelImpl::set_desired_performance(val) + } } /// Initialize DVFS. diff --git a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs index dc49a6d09..83e4f295e 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs @@ -7,7 +7,7 @@ use x86_64::registers::model_specific::Msr; use crate::{ arch::x86_64::msr::*, cpu::{cpu_id, NUM_MAX_CPU}, - dvfs::Dvfs, + dvfs::{DesiredPerformance, Dvfs}, }; const CPUTPM1_HWP_NOTIFICATION: u32 = 0x00000100; @@ -212,6 +212,22 @@ impl HwPstateIntel { } } + /// Select Desired Preference. + /// (range from 0, most performance, through 100, most efficient) + pub(super) fn desired_select(&mut self, percent: u8) -> bool { + let raw_max = ((self.req & IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE) >> 8) as u8; + let raw_min = (self.req & IA32_HWP_MINIMUM_PERFORMANCE) as u8; + + let percent = if percent > 100 { 100 } else { percent }; + + let val = self.percent_to_raw_performance(percent); + let val = if val > raw_max { raw_max } else { val }; + let val = (if val < raw_min { raw_min } else { val }) as u64; + + self.req = (self.req & !IA32_HWP_DESIRED_PERFORMANCE) | (val << 16); + self.request() + } + /// Select Maximum Performance. /// (range from 0, lowest performance, through 100, highest performance) /// @@ -373,6 +389,22 @@ impl Dvfs for HwPstateIntelImpl { false } } + + fn set_desired_performance(val: DesiredPerformance) -> bool { + let cpu_id = cpu_id(); + + let mut node = MCSNode::new(); + let mut hwps = HWPSTATE_INTEL[cpu_id].lock(&mut node); + + if let Some(hwps) = hwps.as_mut() { + match val { + DesiredPerformance::Desired(val) => hwps.desired_select(val), + DesiredPerformance::Auto => hwps.desired_select(0), + } + } else { + false + } + } } /// Initialize Intel Hardware-controlled Performance States diff --git a/awkernel_lib/src/dvfs.rs b/awkernel_lib/src/dvfs.rs index c7cd6a5d0..2501e7e27 100644 --- a/awkernel_lib/src/dvfs.rs +++ b/awkernel_lib/src/dvfs.rs @@ -1,3 +1,9 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DesiredPerformance { + Desired(u8), + Auto, +} + #[allow(unused_variables)] pub trait Dvfs { /// Fix the frequency of the current CPU. @@ -62,9 +68,21 @@ pub trait Dvfs { /// Set the Energy Efficiency Preference. /// (range from 0, highest performance, through 100, highest energy efficient) + /// + /// If current driver does not support this operation, + /// it will return `false`. fn set_energy_efficiency(val: u8) -> bool { false } + + /// Set the Desired Performance. + /// (range from 0, lowest performance, through 100, highest performance) + /// + /// If current driver does not support this operation, + /// it will return `false`. + fn set_desired_performance(val: DesiredPerformance) -> bool { + false + } } /// Fix the frequency of the current CPU. @@ -111,3 +129,12 @@ pub fn set_min_performance(min: u8) -> bool { pub fn set_energy_efficiency(val: u8) -> bool { crate::arch::ArchImpl::set_energy_efficiency(val) } + +/// Set the Desired Performance. +/// (range from 0, lowest performance, through 100, highest performance) +/// +/// If the driver does not support `set_desired_performance()`, `false` will be returned. +#[inline(always)] +pub fn set_desired_performance(val: DesiredPerformance) -> bool { + crate::arch::ArchImpl::set_desired_performance(val) +} From 62c043d81842ef7381a01207f54dfb99c4a4deb5 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Mon, 16 Jun 2025 18:22:46 +0900 Subject: [PATCH 06/17] fix(dvfs): fix tes_dvfs to measure DVFS Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/Cargo.toml | 2 + applications/tests/test_dvfs/src/lib.rs | 106 +++++++++++++++++++----- 2 files changed, 88 insertions(+), 20 deletions(-) diff --git a/applications/tests/test_dvfs/Cargo.toml b/applications/tests/test_dvfs/Cargo.toml index 048f5ebb3..1db274fd4 100644 --- a/applications/tests/test_dvfs/Cargo.toml +++ b/applications/tests/test_dvfs/Cargo.toml @@ -5,6 +5,8 @@ edition = "2024" [dependencies] log = "0.4" +array-macro = "2.1" +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } [dependencies.awkernel_async_lib] path = "../../../awkernel_async_lib" diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index b1364dcdc..70daea417 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -1,48 +1,114 @@ #![no_std] -use core::time::Duration; +use core::{ + sync::atomic::{AtomicU64, AtomicUsize, Ordering}, + time::Duration, +}; +use alloc::format; +use array_macro::array; use awkernel_lib::dvfs::DesiredPerformance; extern crate alloc; const APP_NAME: &str = "test DVFS"; +const NUM_CPU: usize = 14; +const NUM_TRIALS: usize = 100; +const NUM_BUSY_LOOP: usize = 1000000000; -const NUM_LOOP: usize = 100000000; +static LATENCY: [[[AtomicU64; NUM_TRIALS]; 10]; NUM_CPU] = + array![_ => array![_ => array![_ => AtomicU64::new(0); NUM_TRIALS]; 10]; NUM_CPU]; + +static COUNT: [[AtomicUsize; 10]; NUM_CPU] = + array![_ => array![_ => AtomicUsize::new(0); 10]; NUM_CPU]; +static TOTAL_COUNT: AtomicUsize = AtomicUsize::new(0); pub async fn run() { - awkernel_async_lib::spawn( - APP_NAME.into(), - test_dvfs(), - awkernel_async_lib::scheduler::SchedulerType::FIFO, - ) - .await; + for _ in 0..(awkernel_lib::cpu::num_cpu() - 2) { + awkernel_async_lib::spawn( + APP_NAME.into(), + test_dvfs(), + awkernel_async_lib::scheduler::SchedulerType::FIFO, + ) + .await; + } } async fn test_dvfs() { - loop { + let end_count = (awkernel_lib::cpu::num_cpu() - 1) * NUM_TRIALS * 10; + + while TOTAL_COUNT.load(Ordering::Relaxed) + 1 < end_count { let cpu_id = awkernel_lib::cpu::cpu_id(); - awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Desired(100)); - let elapsed1 = empty_loop(); + for i in 0..10 { + awkernel_lib::dvfs::set_min_performance(10 * i); + awkernel_lib::dvfs::set_max_performance(10 * i); + awkernel_lib::dvfs::set_energy_efficiency(100); + awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Auto); + + let elapsed = empty_loop(); - awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Desired(50)); - let elapsed2 = empty_loop(); + let count = + COUNT[cpu_id][i as usize].fetch_add(1, core::sync::atomic::Ordering::Relaxed); + if count < NUM_TRIALS { + LATENCY[cpu_id][i as usize][count].store( + elapsed.as_micros() as u64, + core::sync::atomic::Ordering::Relaxed, + ); - log::debug!( - "cpu_id = {cpu_id:02}, first loop = {} [us], second loop = {} [us]", - elapsed1.as_micros(), - elapsed2.as_micros() - ); + let total_count = TOTAL_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed); - awkernel_async_lib::sleep(Duration::from_secs(1)).await; + log::debug!("progress: {total_count} / {end_count}"); + + if total_count + 1 == end_count { + print_latency(); + } + } + } + + awkernel_async_lib::r#yield().await; } } fn empty_loop() -> Duration { let t = awkernel_async_lib::time::Time::now(); - for _ in 0..NUM_LOOP { + for _ in 0..NUM_BUSY_LOOP { core::hint::black_box(()); } t.elapsed() } + +fn print_latency() { + let mut result: [[[u64; NUM_TRIALS]; 10]; NUM_CPU] = [[[0; NUM_TRIALS]; 10]; NUM_CPU]; + + for (j, latency_cpu) in LATENCY.iter().enumerate() { + for (k, latency) in latency_cpu.iter().enumerate() { + let mut sum = 0; + let mut min = u64::MAX; + let mut max = 0; + for (l, usec) in latency.iter().enumerate() { + let val = usec.load(core::sync::atomic::Ordering::Relaxed); + if min > val { + min = val; + } + if max < val { + max = val; + } + sum += val; + + result[j][k][l] = val; + } + let avg = sum / NUM_TRIALS as u64; + + let msg = format!( + "CPU {j}: Performance {}: Average: {avg} us, Min: {min} us, Max: {max} us\r\n", + k * 10 + ); + awkernel_lib::console::print(&msg); + } + } + + let result_json = serde_json::to_string(&result).unwrap(); + let result_str = format!("{result_json}\r\n"); + awkernel_lib::console::print(&result_str); +} From b7488b49ccbe01de1821612cc7c53afa2991a054 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Mon, 16 Jun 2025 18:30:03 +0900 Subject: [PATCH 07/17] fix(dvfs): use Vec Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index 70daea417..4d861cbfc 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -5,7 +5,7 @@ use core::{ time::Duration, }; -use alloc::format; +use alloc::{format, vec::Vec}; use array_macro::array; use awkernel_lib::dvfs::DesiredPerformance; @@ -79,14 +79,15 @@ fn empty_loop() -> Duration { } fn print_latency() { - let mut result: [[[u64; NUM_TRIALS]; 10]; NUM_CPU] = [[[0; NUM_TRIALS]; 10]; NUM_CPU]; + let mut result: [[Vec; 10]; NUM_CPU] = + array![_ => array![_ => Vec::with_capacity(NUM_TRIALS); 10]; NUM_CPU]; for (j, latency_cpu) in LATENCY.iter().enumerate() { for (k, latency) in latency_cpu.iter().enumerate() { let mut sum = 0; let mut min = u64::MAX; let mut max = 0; - for (l, usec) in latency.iter().enumerate() { + for usec in latency.iter() { let val = usec.load(core::sync::atomic::Ordering::Relaxed); if min > val { min = val; @@ -96,7 +97,7 @@ fn print_latency() { } sum += val; - result[j][k][l] = val; + result[j][k].push(val); } let avg = sum / NUM_TRIALS as u64; From 8b15d4e6efd8b9411c045d51accaf50e57c4b59d Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Mon, 16 Jun 2025 20:46:17 +0900 Subject: [PATCH 08/17] fix(dvfs): add warm_up() when evaluating Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/src/lib.rs | 26 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index 4d861cbfc..8f98eed71 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -14,13 +14,13 @@ extern crate alloc; const APP_NAME: &str = "test DVFS"; const NUM_CPU: usize = 14; const NUM_TRIALS: usize = 100; -const NUM_BUSY_LOOP: usize = 1000000000; +const NUM_BUSY_LOOP: usize = 10000000000; -static LATENCY: [[[AtomicU64; NUM_TRIALS]; 10]; NUM_CPU] = - array![_ => array![_ => array![_ => AtomicU64::new(0); NUM_TRIALS]; 10]; NUM_CPU]; +static LATENCY: [[[AtomicU64; NUM_TRIALS]; 11]; NUM_CPU] = + array![_ => array![_ => array![_ => AtomicU64::new(0); NUM_TRIALS]; 11]; NUM_CPU]; -static COUNT: [[AtomicUsize; 10]; NUM_CPU] = - array![_ => array![_ => AtomicUsize::new(0); 10]; NUM_CPU]; +static COUNT: [[AtomicUsize; 11]; NUM_CPU] = + array![_ => array![_ => AtomicUsize::new(0); 11]; NUM_CPU]; static TOTAL_COUNT: AtomicUsize = AtomicUsize::new(0); pub async fn run() { @@ -35,17 +35,19 @@ pub async fn run() { } async fn test_dvfs() { - let end_count = (awkernel_lib::cpu::num_cpu() - 1) * NUM_TRIALS * 10; + let end_count = (awkernel_lib::cpu::num_cpu() - 1) * NUM_TRIALS * 11; while TOTAL_COUNT.load(Ordering::Relaxed) + 1 < end_count { let cpu_id = awkernel_lib::cpu::cpu_id(); - for i in 0..10 { + for i in 0..=10 { awkernel_lib::dvfs::set_min_performance(10 * i); awkernel_lib::dvfs::set_max_performance(10 * i); awkernel_lib::dvfs::set_energy_efficiency(100); awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Auto); + warm_up(); + let elapsed = empty_loop(); let count = @@ -70,6 +72,12 @@ async fn test_dvfs() { } } +fn warm_up() { + for _ in 0..(NUM_BUSY_LOOP / 10) { + core::hint::black_box(()); + } +} + fn empty_loop() -> Duration { let t = awkernel_async_lib::time::Time::now(); for _ in 0..NUM_BUSY_LOOP { @@ -79,8 +87,8 @@ fn empty_loop() -> Duration { } fn print_latency() { - let mut result: [[Vec; 10]; NUM_CPU] = - array![_ => array![_ => Vec::with_capacity(NUM_TRIALS); 10]; NUM_CPU]; + let mut result: [[Vec; 11]; NUM_CPU] = + array![_ => array![_ => Vec::with_capacity(NUM_TRIALS); 11]; NUM_CPU]; for (j, latency_cpu) in LATENCY.iter().enumerate() { for (k, latency) in latency_cpu.iter().enumerate() { From 8035c49907fea4812bd8a8b8f24239b353008032 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 17 Jun 2025 11:03:36 +0900 Subject: [PATCH 09/17] fix(dvfs): add `set_min_max_performance()` Signed-off-by: Yuuki Takano --- .../awkernel_services/src/buffered_logger.rs | 2 +- applications/tests/test_dvfs/src/lib.rs | 3 +-- .../src/arch/x86_64/dvfs/hwpstate_intel.rs | 26 +++++++++++++++++++ awkernel_lib/src/dvfs.rs | 18 +++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/applications/awkernel_services/src/buffered_logger.rs b/applications/awkernel_services/src/buffered_logger.rs index f0803ae34..1a4d3a04c 100644 --- a/applications/awkernel_services/src/buffered_logger.rs +++ b/applications/awkernel_services/src/buffered_logger.rs @@ -1,5 +1,5 @@ use alloc::string::String; -use awkernel_async_lib::channel::bounded; +use awkernel_async_lib::channel::bounded::{self, Attribute}; use awkernel_lib::sync::{mcs::MCSNode, mutex::Mutex}; static BUFFERED_LOGGER: Mutex> = Mutex::new(None); diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index 8f98eed71..e7b444012 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -41,8 +41,7 @@ async fn test_dvfs() { let cpu_id = awkernel_lib::cpu::cpu_id(); for i in 0..=10 { - awkernel_lib::dvfs::set_min_performance(10 * i); - awkernel_lib::dvfs::set_max_performance(10 * i); + awkernel_lib::dvfs::set_min_max_performance(10 * i); awkernel_lib::dvfs::set_energy_efficiency(100); awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Auto); diff --git a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs index 83e4f295e..1f4f0d71e 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs @@ -262,6 +262,19 @@ impl HwPstateIntel { self.request() } + /// Select Minimum and Maximum Performance. + /// (range from 0, lowest performance, through 100, highest performance) + fn min_max_peformance_select(&mut self, val: u8) -> bool { + let val = self.percent_to_raw_performance(val) as u64; + + self.req = (self.req & !IA32_HWP_MINIMUM_PERFORMANCE) | val; + + self.req = (self.req & !IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE) | (val << 8); + self.req &= !IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE; + + self.request() + } + #[inline] fn request(&self) -> bool { if self.hwp_pkg_ctrl_en { @@ -405,6 +418,19 @@ impl Dvfs for HwPstateIntelImpl { false } } + + fn set_min_max_performance(val: u8) -> bool { + let cpu_id = cpu_id(); + + let mut node = MCSNode::new(); + let mut hwps = HWPSTATE_INTEL[cpu_id].lock(&mut node); + + if let Some(hwps) = hwps.as_mut() { + hwps.min_max_peformance_select(val) + } else { + false + } + } } /// Initialize Intel Hardware-controlled Performance States diff --git a/awkernel_lib/src/dvfs.rs b/awkernel_lib/src/dvfs.rs index 2501e7e27..927b439e2 100644 --- a/awkernel_lib/src/dvfs.rs +++ b/awkernel_lib/src/dvfs.rs @@ -66,6 +66,15 @@ pub trait Dvfs { None } + /// Select the Minimum and Maximum Performance. + /// (range from 0, lowest performance, through 100, highest performance) + /// + /// If current driver does not support this operation, + /// it will return `false`. + fn set_min_max_performance(min: u8) -> bool { + false + } + /// Set the Energy Efficiency Preference. /// (range from 0, highest performance, through 100, highest energy efficient) /// @@ -138,3 +147,12 @@ pub fn set_energy_efficiency(val: u8) -> bool { pub fn set_desired_performance(val: DesiredPerformance) -> bool { crate::arch::ArchImpl::set_desired_performance(val) } + +/// Set Minimum and Maximum Performance. +/// (range from 0, lowest performance, through 100, highest performance) +/// +/// If the driver does not support `set_min_max_performance()`, `false` will be returned. +#[inline(always)] +pub fn set_min_max_performance(percent: u8) -> bool { + crate::arch::ArchImpl::set_min_max_performance(percent) +} From 4e0befc88b3019ac7f13b011a3e12962b9964ec6 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 17 Jun 2025 11:19:21 +0900 Subject: [PATCH 10/17] revert buffered_logger.rs Signed-off-by: Yuuki Takano --- applications/awkernel_services/src/buffered_logger.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/awkernel_services/src/buffered_logger.rs b/applications/awkernel_services/src/buffered_logger.rs index 1a4d3a04c..f0803ae34 100644 --- a/applications/awkernel_services/src/buffered_logger.rs +++ b/applications/awkernel_services/src/buffered_logger.rs @@ -1,5 +1,5 @@ use alloc::string::String; -use awkernel_async_lib::channel::bounded::{self, Attribute}; +use awkernel_async_lib::channel::bounded; use awkernel_lib::sync::{mcs::MCSNode, mutex::Mutex}; static BUFFERED_LOGGER: Mutex> = Mutex::new(None); From 648341448f57838aa31d9783f7f2b2a148dd4125 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 17 Jun 2025 11:28:17 +0900 Subject: [PATCH 11/17] fix(dvfs): update `min_max_peformance_select()` Signed-off-by: Yuuki Takano --- awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs index 1f4f0d71e..6dcd76d91 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs/hwpstate_intel.rs @@ -267,10 +267,10 @@ impl HwPstateIntel { fn min_max_peformance_select(&mut self, val: u8) -> bool { let val = self.percent_to_raw_performance(val) as u64; - self.req = (self.req & !IA32_HWP_MINIMUM_PERFORMANCE) | val; - - self.req = (self.req & !IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE) | (val << 8); - self.req &= !IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE; + self.req = (self.req + & !(IA32_HWP_MINIMUM_PERFORMANCE | IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE)) + | (val << 8) + | val; self.request() } From 7c70d9313afb62c2a388598b57f1234568707896 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 17 Jun 2025 13:40:48 +0900 Subject: [PATCH 12/17] add nbody.rs Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/Cargo.toml | 1 + applications/tests/test_dvfs/src/lib.rs | 16 ++-- applications/tests/test_dvfs/src/nbody.rs | 102 ++++++++++++++++++++++ userland/Cargo.toml | 2 +- 4 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 applications/tests/test_dvfs/src/nbody.rs diff --git a/applications/tests/test_dvfs/Cargo.toml b/applications/tests/test_dvfs/Cargo.toml index 1db274fd4..947b3fc6e 100644 --- a/applications/tests/test_dvfs/Cargo.toml +++ b/applications/tests/test_dvfs/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" log = "0.4" array-macro = "2.1" serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +num-traits = { version = "0.2", default-features = false } [dependencies.awkernel_async_lib] path = "../../../awkernel_async_lib" diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index e7b444012..0e4c79d57 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -11,6 +11,8 @@ use awkernel_lib::dvfs::DesiredPerformance; extern crate alloc; +mod nbody; + const APP_NAME: &str = "test DVFS"; const NUM_CPU: usize = 14; const NUM_TRIALS: usize = 100; @@ -47,7 +49,13 @@ async fn test_dvfs() { warm_up(); - let elapsed = empty_loop(); + let elapsed = workload(); + + log::debug!( + "CPU {cpu_id}: Performance {}: Elapsed: {} [us]", + i * 10, + elapsed.as_micros() + ); let count = COUNT[cpu_id][i as usize].fetch_add(1, core::sync::atomic::Ordering::Relaxed); @@ -77,11 +85,9 @@ fn warm_up() { } } -fn empty_loop() -> Duration { +fn workload() -> Duration { let t = awkernel_async_lib::time::Time::now(); - for _ in 0..NUM_BUSY_LOOP { - core::hint::black_box(()); - } + nbody::simulate(); t.elapsed() } diff --git a/applications/tests/test_dvfs/src/nbody.rs b/applications/tests/test_dvfs/src/nbody.rs new file mode 100644 index 000000000..ca2c714fe --- /dev/null +++ b/applications/tests/test_dvfs/src/nbody.rs @@ -0,0 +1,102 @@ +use alloc::{vec, vec::Vec}; +use num_traits::float::Float; + +#[derive(Clone, Copy, Debug)] +struct Body { + x: f64, + y: f64, + vx: f64, + vy: f64, + mass: f64, +} + +impl Body { + fn update_velocity(&mut self, fx: f64, fy: f64, dt: f64) { + self.vx += fx / self.mass * dt; + self.vy += fy / self.mass * dt; + } + + fn update_position(&mut self, dt: f64) { + self.x += self.vx * dt; + self.y += self.vy * dt; + } +} + +fn compute_force(a: &Body, b: &Body, g: f64, eps: f64) -> (f64, f64) { + let dx = b.x - a.x; + let dy = b.y - a.y; + let dist_sq = dx * dx + dy * dy + eps * eps; // softening + let dist = dist_sq.sqrt(); + let f = g * a.mass * b.mass / dist_sq; + let fx = f * dx / dist; + let fy = f * dy / dist; + (fx, fy) +} + +fn nbody_step(bodies: &mut [Body], g: f64, dt: f64, eps: f64) { + let n = bodies.len(); + let mut forces = vec![(0.0, 0.0); n]; + + for i in 0..n { + for j in 0..n { + if i != j { + let (fx, fy) = compute_force(&bodies[i], &bodies[j], g, eps); + forces[i].0 += fx; + forces[i].1 += fy; + } + } + } + + for i in 0..n { + bodies[i].update_velocity(forces[i].0, forces[i].1, dt); + bodies[i].update_position(dt); + } +} + +pub fn simulate() { + const N: usize = 2000; + const STEPS: usize = 2; + const G: f64 = 6.67430e-11; + const DT: f64 = 0.1; + const EPS: f64 = 1e-3; + + let mut rnd = XorShift64::new(0x12345678); // 乱数生成器の初期化 + + // 初期化:ランダムにばら撒く(実用では乱数を使ってもよい) + let mut bodies = (0..N) + .map(|_| Body { + x: rnd.next_f64(), + y: rnd.next_f64(), + vx: 0.0, + vy: 0.0, + mass: rnd.next_f64(), + }) + .collect::>(); + + for _ in 0..STEPS { + nbody_step(&mut bodies, G, DT, EPS); + } +} + +pub struct XorShift64 { + state: u64, +} + +impl XorShift64 { + pub fn new(seed: u64) -> Self { + Self { state: seed } + } + + pub fn next(&mut self) -> u64 { + let mut x = self.state; + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; + self.state = x; + x + } + + pub fn next_f64(&mut self) -> f64 { + (self.next() as f64) / (u64::MAX as f64) + } +} diff --git a/userland/Cargo.toml b/userland/Cargo.toml index 009306018..c84e66351 100644 --- a/userland/Cargo.toml +++ b/userland/Cargo.toml @@ -83,7 +83,7 @@ path = "../applications/tests/test_voluntary_preemption" optional = true [features] -default = ["test_dvfs"] +default = [] perf = ["awkernel_services/perf"] # Evaluation applications From 4d4db5fca37eb6fc0ecf7ca6d23efd55e917fbd6 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 17 Jun 2025 16:58:40 +0900 Subject: [PATCH 13/17] fix(dvfs): implment `set_min_max_performance()` Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/src/lib.rs | 2 +- applications/tests/test_dvfs/src/nbody.rs | 2 +- awkernel_lib/src/arch/x86_64/dvfs.rs | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index 0e4c79d57..ee6c454f0 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -44,7 +44,7 @@ async fn test_dvfs() { for i in 0..=10 { awkernel_lib::dvfs::set_min_max_performance(10 * i); - awkernel_lib::dvfs::set_energy_efficiency(100); + awkernel_lib::dvfs::set_energy_efficiency(0); awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Auto); warm_up(); diff --git a/applications/tests/test_dvfs/src/nbody.rs b/applications/tests/test_dvfs/src/nbody.rs index ca2c714fe..836044571 100644 --- a/applications/tests/test_dvfs/src/nbody.rs +++ b/applications/tests/test_dvfs/src/nbody.rs @@ -54,7 +54,7 @@ fn nbody_step(bodies: &mut [Body], g: f64, dt: f64, eps: f64) { } pub fn simulate() { - const N: usize = 2000; + const N: usize = 5000; const STEPS: usize = 2; const G: f64 = 6.67430e-11; const DT: f64 = 0.1; diff --git a/awkernel_lib/src/arch/x86_64/dvfs.rs b/awkernel_lib/src/arch/x86_64/dvfs.rs index 41182b1cd..f22bf30c3 100644 --- a/awkernel_lib/src/arch/x86_64/dvfs.rs +++ b/awkernel_lib/src/arch/x86_64/dvfs.rs @@ -96,6 +96,10 @@ impl Dvfs for X86 { fn set_desired_performance(val: crate::dvfs::DesiredPerformance) -> bool { hwpstate_intel::HwPstateIntelImpl::set_desired_performance(val) } + + fn set_min_max_performance(min: u8) -> bool { + hwpstate_intel::HwPstateIntelImpl::set_min_max_performance(min) + } } /// Initialize DVFS. From 64b2f47196c2d52adae67db1296aa8f083f3ae6b Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Wed, 18 Jun 2025 10:59:43 +0900 Subject: [PATCH 14/17] testing Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/src/lib.rs | 48 ++++++++++++++++++++++--- userland/Cargo.toml | 2 +- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index ee6c454f0..379e33c4c 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -1,13 +1,14 @@ #![no_std] use core::{ - sync::atomic::{AtomicU64, AtomicUsize, Ordering}, + sync::atomic::{AtomicU64, AtomicUsize, Ordering, fence}, time::Duration, }; use alloc::{format, vec::Vec}; use array_macro::array; use awkernel_lib::dvfs::DesiredPerformance; +use num_traits::abs; extern crate alloc; @@ -16,7 +17,7 @@ mod nbody; const APP_NAME: &str = "test DVFS"; const NUM_CPU: usize = 14; const NUM_TRIALS: usize = 100; -const NUM_BUSY_LOOP: usize = 10000000000; +const NUM_BUSY_LOOP: usize = 1000000000; static LATENCY: [[[AtomicU64; NUM_TRIALS]; 11]; NUM_CPU] = array![_ => array![_ => array![_ => AtomicU64::new(0); NUM_TRIALS]; 11]; NUM_CPU]; @@ -29,7 +30,7 @@ pub async fn run() { for _ in 0..(awkernel_lib::cpu::num_cpu() - 2) { awkernel_async_lib::spawn( APP_NAME.into(), - test_dvfs(), + test_freq_latency(), awkernel_async_lib::scheduler::SchedulerType::FIFO, ) .await; @@ -80,7 +81,7 @@ async fn test_dvfs() { } fn warm_up() { - for _ in 0..(NUM_BUSY_LOOP / 10) { + for _ in 0..(NUM_BUSY_LOOP) { core::hint::black_box(()); } } @@ -126,3 +127,42 @@ fn print_latency() { let result_str = format!("{result_json}\r\n"); awkernel_lib::console::print(&result_str); } + +async fn test_freq_latency() { + loop { + awkernel_lib::dvfs::set_min_max_performance(25); + awkernel_lib::dvfs::set_energy_efficiency(0); + awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Auto); + + warm_up(); + + let mut diff = Vec::with_capacity(100000); + + awkernel_lib::dvfs::set_min_max_performance(100); + awkernel_lib::dvfs::set_energy_efficiency(0); + awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Auto); + + let t = awkernel_async_lib::time::Time::now(); + for _ in 0..100000 { + let start = unsafe { core::arch::x86_64::_rdtsc() }; + fence(Ordering::AcqRel); + for _ in 0..1000 { + core::hint::black_box(()); + } + fence(Ordering::AcqRel); + let end = unsafe { core::arch::x86_64::_rdtsc() }; + diff.push((t.elapsed(), (end - start) as i64)); + } + + // let mut result = Vec::new(); + + for i in 0..100 { + // result.push((diff[i].0, diff[i + 1].1 - diff[i].1)); + if abs(diff[i + 1].1 - diff[i].1) > 100 { + log::debug!("{i}: {:?}", diff[i + 1].1 - diff[i].1); + } + } + + // log::debug!("{result:?}"); + } +} diff --git a/userland/Cargo.toml b/userland/Cargo.toml index c84e66351..009306018 100644 --- a/userland/Cargo.toml +++ b/userland/Cargo.toml @@ -83,7 +83,7 @@ path = "../applications/tests/test_voluntary_preemption" optional = true [features] -default = [] +default = ["test_dvfs"] perf = ["awkernel_services/perf"] # Evaluation applications From 53ab58d9d58dbfe83bd3ca896783940123193ba0 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Wed, 25 Jun 2025 18:16:01 +0900 Subject: [PATCH 15/17] update test_dvfs Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/src/lib.rs | 123 +++++++++++++++++++----- 1 file changed, 98 insertions(+), 25 deletions(-) diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index 379e33c4c..a00b0f712 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -7,38 +7,64 @@ use core::{ use alloc::{format, vec::Vec}; use array_macro::array; -use awkernel_lib::dvfs::DesiredPerformance; -use num_traits::abs; +use awkernel_lib::{ + dvfs::DesiredPerformance, + sync::{mcs::MCSNode, mutex::Mutex}, +}; extern crate alloc; mod nbody; -const APP_NAME: &str = "test DVFS"; const NUM_CPU: usize = 14; -const NUM_TRIALS: usize = 100; +const NUM_TRIALS_LATENCY: usize = 100; const NUM_BUSY_LOOP: usize = 1000000000; -static LATENCY: [[[AtomicU64; NUM_TRIALS]; 11]; NUM_CPU] = - array![_ => array![_ => array![_ => AtomicU64::new(0); NUM_TRIALS]; 11]; NUM_CPU]; +static LATENCY: [[[AtomicU64; NUM_TRIALS_LATENCY]; 11]; NUM_CPU] = + array![_ => array![_ => array![_ => AtomicU64::new(0); NUM_TRIALS_LATENCY]; 11]; NUM_CPU]; static COUNT: [[AtomicUsize; 11]; NUM_CPU] = array![_ => array![_ => AtomicUsize::new(0); 11]; NUM_CPU]; static TOTAL_COUNT: AtomicUsize = AtomicUsize::new(0); pub async fn run() { + let mut waiter = Vec::with_capacity(awkernel_lib::cpu::num_cpu() - 2); + for _ in 0..(awkernel_lib::cpu::num_cpu() - 2) { - awkernel_async_lib::spawn( - APP_NAME.into(), - test_freq_latency(), + let w = awkernel_async_lib::spawn( + "test_latency_diff".into(), + test_latency_diff(), awkernel_async_lib::scheduler::SchedulerType::FIFO, ) .await; + + waiter.push(w); + } + + for w in waiter { + let _ = w.join().await; + } + + let mut waiter = Vec::with_capacity(awkernel_lib::cpu::num_cpu() - 2); + + for _ in 0..(awkernel_lib::cpu::num_cpu() - 2) { + let w = awkernel_async_lib::spawn( + "test_latency".into(), + test_latency(), + awkernel_async_lib::scheduler::SchedulerType::FIFO, + ) + .await; + + waiter.push(w); + } + + for w in waiter { + let _ = w.join().await; } } -async fn test_dvfs() { - let end_count = (awkernel_lib::cpu::num_cpu() - 1) * NUM_TRIALS * 11; +async fn test_latency() { + let end_count = (awkernel_lib::cpu::num_cpu() - 1) * NUM_TRIALS_LATENCY * 11; while TOTAL_COUNT.load(Ordering::Relaxed) + 1 < end_count { let cpu_id = awkernel_lib::cpu::cpu_id(); @@ -60,7 +86,7 @@ async fn test_dvfs() { let count = COUNT[cpu_id][i as usize].fetch_add(1, core::sync::atomic::Ordering::Relaxed); - if count < NUM_TRIALS { + if count < NUM_TRIALS_LATENCY { LATENCY[cpu_id][i as usize][count].store( elapsed.as_micros() as u64, core::sync::atomic::Ordering::Relaxed, @@ -94,7 +120,7 @@ fn workload() -> Duration { fn print_latency() { let mut result: [[Vec; 11]; NUM_CPU] = - array![_ => array![_ => Vec::with_capacity(NUM_TRIALS); 11]; NUM_CPU]; + array![_ => array![_ => Vec::with_capacity(NUM_TRIALS_LATENCY); 11]; NUM_CPU]; for (j, latency_cpu) in LATENCY.iter().enumerate() { for (k, latency) in latency_cpu.iter().enumerate() { @@ -113,7 +139,7 @@ fn print_latency() { result[j][k].push(val); } - let avg = sum / NUM_TRIALS as u64; + let avg = sum / NUM_TRIALS_LATENCY as u64; let msg = format!( "CPU {j}: Performance {}: Average: {avg} us, Min: {min} us, Max: {max} us\r\n", @@ -128,22 +154,28 @@ fn print_latency() { awkernel_lib::console::print(&result_str); } -async fn test_freq_latency() { +const NUM_TRIALS_LATENCY_DIFF: usize = 20; +static FREQ_LATENCY: [[Mutex>; NUM_TRIALS_LATENCY_DIFF]; NUM_CPU] = + array![_ => array![_ => Mutex::new(Vec::new()); NUM_TRIALS_LATENCY_DIFF]; NUM_CPU]; +static TOTAL_COUNT_LATENCY_DIFF: AtomicUsize = AtomicUsize::new(0); +static N: usize = 500; + +async fn test_latency_diff() { loop { - awkernel_lib::dvfs::set_min_max_performance(25); + awkernel_lib::dvfs::set_min_max_performance(10); awkernel_lib::dvfs::set_energy_efficiency(0); awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Auto); - warm_up(); + workload(); - let mut diff = Vec::with_capacity(100000); + let mut diff = Vec::with_capacity(N); awkernel_lib::dvfs::set_min_max_performance(100); awkernel_lib::dvfs::set_energy_efficiency(0); awkernel_lib::dvfs::set_desired_performance(DesiredPerformance::Auto); let t = awkernel_async_lib::time::Time::now(); - for _ in 0..100000 { + for _ in 0..N { let start = unsafe { core::arch::x86_64::_rdtsc() }; fence(Ordering::AcqRel); for _ in 0..1000 { @@ -154,15 +186,56 @@ async fn test_freq_latency() { diff.push((t.elapsed(), (end - start) as i64)); } - // let mut result = Vec::new(); + let mut result = Vec::new(); + + for i in 0..(N - 1) { + result.push((diff[i].0.as_nanos() as u64, diff[i + 1].1 - diff[i].1)); + } + + let cpu_id = awkernel_lib::cpu::cpu_id(); + for (i, r) in FREQ_LATENCY[cpu_id].iter().enumerate() { + let mut node = MCSNode::new(); + let mut guard = r.lock(&mut node); + if guard.is_empty() { + *guard = result; + drop(guard); + + let old_total = TOTAL_COUNT_LATENCY_DIFF.fetch_add(1, Ordering::Relaxed); + + log::debug!("{cpu_id}: {i}, {old_total}"); + + if old_total == (NUM_CPU - 1) * NUM_TRIALS_LATENCY_DIFF - 1 { + print_latency_diff(); + } - for i in 0..100 { - // result.push((diff[i].0, diff[i + 1].1 - diff[i].1)); - if abs(diff[i + 1].1 - diff[i].1) > 100 { - log::debug!("{i}: {:?}", diff[i + 1].1 - diff[i].1); + break; } } - // log::debug!("{result:?}"); + let total = TOTAL_COUNT_LATENCY_DIFF.load(Ordering::Relaxed); + + if total == (NUM_CPU - 1) * NUM_TRIALS_LATENCY_DIFF { + break; + } + + awkernel_async_lib::r#yield().await; } } + +fn print_latency_diff() { + let mut result: [[Vec<(u64, i64)>; NUM_TRIALS_LATENCY_DIFF]; NUM_CPU] = + array![_ => array![_ => Vec::new(); NUM_TRIALS_LATENCY_DIFF]; NUM_CPU]; + + for (dst, src) in result.iter_mut().zip(FREQ_LATENCY.iter()) { + for (dst, src) in dst.iter_mut().zip(src.iter()) { + let mut node = MCSNode::new(); + let guard = src.lock(&mut node); + + *dst = guard.clone(); + } + } + + let result_json = serde_json::to_string(&result).unwrap(); + let result_str = format!("{result_json}\r\n"); + awkernel_lib::console::print(&result_str); +} From 10fe38646774e002103c0858fd458aa731d41f49 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Wed, 25 Jun 2025 18:18:44 +0900 Subject: [PATCH 16/17] revert Cargo.toml of userland Signed-off-by: Yuuki Takano --- userland/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userland/Cargo.toml b/userland/Cargo.toml index 009306018..c84e66351 100644 --- a/userland/Cargo.toml +++ b/userland/Cargo.toml @@ -83,7 +83,7 @@ path = "../applications/tests/test_voluntary_preemption" optional = true [features] -default = ["test_dvfs"] +default = [] perf = ["awkernel_services/perf"] # Evaluation applications From 4fa345ddf60e51a26738b1d443f4f2d31df4e590 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Wed, 25 Jun 2025 19:00:13 +0900 Subject: [PATCH 17/17] fix test_latency_diff Signed-off-by: Yuuki Takano --- applications/tests/test_dvfs/src/lib.rs | 6 +++--- userland/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/tests/test_dvfs/src/lib.rs b/applications/tests/test_dvfs/src/lib.rs index a00b0f712..d5811bc02 100644 --- a/applications/tests/test_dvfs/src/lib.rs +++ b/applications/tests/test_dvfs/src/lib.rs @@ -186,10 +186,10 @@ async fn test_latency_diff() { diff.push((t.elapsed(), (end - start) as i64)); } - let mut result = Vec::new(); + let mut result = Vec::with_capacity(diff.len()); - for i in 0..(N - 1) { - result.push((diff[i].0.as_nanos() as u64, diff[i + 1].1 - diff[i].1)); + for (t, d) in diff.iter() { + result.push((t.as_nanos() as u64, *d)); } let cpu_id = awkernel_lib::cpu::cpu_id(); diff --git a/userland/Cargo.toml b/userland/Cargo.toml index c84e66351..009306018 100644 --- a/userland/Cargo.toml +++ b/userland/Cargo.toml @@ -83,7 +83,7 @@ path = "../applications/tests/test_voluntary_preemption" optional = true [features] -default = [] +default = ["test_dvfs"] perf = ["awkernel_services/perf"] # Evaluation applications