From 689042df606f57c72dd3cc47a94c4dfb3e278ea5 Mon Sep 17 00:00:00 2001 From: Tunglies Date: Fri, 6 Jun 2025 14:49:23 +0800 Subject: [PATCH] refactor: use Box to store large config objects and add memory usage tests - Refactored config-related structs to use Box for storing large objects (e.g., IRuntime, IProfiles, PrfItem) to reduce stack memory usage and improve performance. - Updated related methods and assignments to handle Boxed types correctly. - Added and improved unit tests to compare memory usage between Boxed and non-Boxed config objects, demonstrating the memory efficiency of Box. - Test output now shows the size difference between stack-allocated and heap-allocated (Box) config objects. --- src-tauri/src/cmd/profile.rs | 4 +- src-tauri/src/cmd/verge.rs | 2 +- src-tauri/src/config/config.rs | 67 ++++++++++++++++++++++++++------- src-tauri/src/config/draft.rs | 48 +++++++++++++---------- src-tauri/src/core/core.rs | 4 +- src-tauri/src/enhance/script.rs | 10 ++--- 6 files changed, 91 insertions(+), 44 deletions(-) diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index f98e17a6..45956376 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -22,7 +22,7 @@ pub async fn get_profiles() -> CmdResult { .await; match profiles_result { - Ok(Ok(profiles)) => Ok(profiles), + Ok(Ok(profiles)) => Ok(*profiles), Ok(Err(join_err)) => { logging!(error, Type::Cmd, true, "获取配置列表任务失败: {}", join_err); Ok(IProfiles { @@ -41,7 +41,7 @@ pub async fn get_profiles() -> CmdResult { match tokio::task::spawn_blocking(move || Config::profiles().latest().clone()).await { Ok(profiles) => { logging!(info, Type::Cmd, true, "使用latest()成功获取配置"); - Ok(profiles) + Ok(*profiles) } Err(_) => { logging!(error, Type::Cmd, true, "fallback获取配置也失败,返回空配置"); diff --git a/src-tauri/src/cmd/verge.rs b/src-tauri/src/cmd/verge.rs index 7e8bc7a8..9d7c4fd2 100644 --- a/src-tauri/src/cmd/verge.rs +++ b/src-tauri/src/cmd/verge.rs @@ -6,7 +6,7 @@ use crate::{config::*, feat, wrap_err}; pub fn get_verge_config() -> CmdResult { let verge = Config::verge(); let verge_data = verge.data().clone(); - Ok(IVergeResponse::from(verge_data)) + Ok(IVergeResponse::from(*verge_data)) } /// 修改Verge配置 diff --git a/src-tauri/src/config/config.rs b/src-tauri/src/config/config.rs index 7cefb5d1..c0429b12 100644 --- a/src-tauri/src/config/config.rs +++ b/src-tauri/src/config/config.rs @@ -15,10 +15,10 @@ pub const RUNTIME_CONFIG: &str = "clash-verge.yaml"; pub const CHECK_CONFIG: &str = "clash-verge-check.yaml"; pub struct Config { - clash_config: Draft, - verge_config: Draft, - profiles_config: Draft, - runtime_config: Draft, + clash_config: Draft>, + verge_config: Draft>, + profiles_config: Draft>, + runtime_config: Draft>, } impl Config { @@ -26,26 +26,26 @@ impl Config { static CONFIG: OnceCell = OnceCell::new(); CONFIG.get_or_init(|| Config { - clash_config: Draft::from(IClashTemp::new()), - verge_config: Draft::from(IVerge::new()), - profiles_config: Draft::from(IProfiles::new()), - runtime_config: Draft::from(IRuntime::new()), + clash_config: Draft::from(Box::new(IClashTemp::new())), + verge_config: Draft::from(Box::new(IVerge::new())), + profiles_config: Draft::from(Box::new(IProfiles::new())), + runtime_config: Draft::from(Box::new(IRuntime::new())), }) } - pub fn clash() -> Draft { + pub fn clash() -> Draft> { Self::global().clash_config.clone() } - pub fn verge() -> Draft { + pub fn verge() -> Draft> { Self::global().verge_config.clone() } - pub fn profiles() -> Draft { + pub fn profiles() -> Draft> { Self::global().profiles_config.clone() } - pub fn runtime() -> Draft { + pub fn runtime() -> Draft> { Self::global().runtime_config.clone() } @@ -149,11 +149,11 @@ impl Config { pub async fn generate() -> Result<()> { let (config, exists_keys, logs) = enhance::enhance().await; - *Config::runtime().draft() = IRuntime { + *Config::runtime().draft() = Box::new(IRuntime { config: Some(config), exists_keys, chain_logs: logs, - }; + }); Ok(()) } @@ -164,3 +164,42 @@ pub enum ConfigType { Run, Check, } +#[cfg(test)] +mod tests { + use super::*; + use std::mem; + + #[test] + fn test_prfitem_from_merge_size() { + let merge_item = PrfItem::from_merge(Some("Merge".to_string())).unwrap(); + dbg!(&merge_item); + let prfitem_size = mem::size_of_val(&merge_item); + dbg!(prfitem_size); + // Boxed version + let boxed_merge_item = Box::new(merge_item); + let box_prfitem_size = mem::size_of_val(&boxed_merge_item); + dbg!(box_prfitem_size); + // The size of Box is always pointer-sized (usually 8 bytes on 64-bit) + // assert_eq!(box_prfitem_size, mem::size_of::>()); + assert!(box_prfitem_size < prfitem_size); + } + + #[test] + fn test_draft_size_non_boxed() { + let draft = Draft::from(IRuntime::new()); + let iruntime_size = std::mem::size_of_val(&draft); + dbg!(iruntime_size); + assert_eq!(iruntime_size, std::mem::size_of::>()); + } + + #[test] + fn test_draft_size_boxed() { + let draft = Draft::from(Box::new(IRuntime::new())); + let box_iruntime_size = std::mem::size_of_val(&draft); + dbg!(box_iruntime_size); + assert_eq!( + box_iruntime_size, + std::mem::size_of::>>() + ); + } +} diff --git a/src-tauri/src/config/draft.rs b/src-tauri/src/config/draft.rs index 5876f1bb..97ef7412 100644 --- a/src-tauri/src/config/draft.rs +++ b/src-tauri/src/config/draft.rs @@ -9,13 +9,21 @@ pub struct Draft { macro_rules! draft_define { ($id: ident) => { - impl Draft<$id> { + impl From<$id> for Draft<$id> { + fn from(data: $id) -> Self { + Draft { + inner: Arc::new(Mutex::new((data, None))), + } + } + } + + impl Draft> { #[allow(unused)] - pub fn data(&self) -> MappedMutexGuard<$id> { + pub fn data(&self) -> MappedMutexGuard> { MutexGuard::map(self.inner.lock(), |guard| &mut guard.0) } - pub fn latest(&self) -> MappedMutexGuard<$id> { + pub fn latest(&self) -> MappedMutexGuard> { MutexGuard::map(self.inner.lock(), |inner| { if inner.1.is_none() { &mut inner.0 @@ -25,7 +33,7 @@ macro_rules! draft_define { }) } - pub fn draft(&self) -> MappedMutexGuard<$id> { + pub fn draft(&self) -> MappedMutexGuard> { MutexGuard::map(self.inner.lock(), |inner| { if inner.1.is_none() { inner.1 = Some(inner.0.clone()); @@ -35,7 +43,7 @@ macro_rules! draft_define { }) } - pub fn apply(&self) -> Option<$id> { + pub fn apply(&self) -> Option> { let mut inner = self.inner.lock(); match inner.1.take() { @@ -48,14 +56,14 @@ macro_rules! draft_define { } } - pub fn discard(&self) -> Option<$id> { + pub fn discard(&self) -> Option> { let mut inner = self.inner.lock(); inner.1.take() } } - impl From<$id> for Draft<$id> { - fn from(data: $id) -> Self { + impl From> for Draft> { + fn from(data: Box<$id>) -> Self { Draft { inner: Arc::new(Mutex::new((data, None))), } @@ -71,12 +79,12 @@ draft_define!(IRuntime); draft_define!(IVerge); #[test] -fn test_draft() { - let verge = IVerge { +fn test_draft_box() { + let verge = Box::new(IVerge { enable_auto_launch: Some(true), enable_tun_mode: Some(false), ..IVerge::default() - }; + }); let draft = Draft::from(verge); @@ -86,10 +94,11 @@ fn test_draft() { assert_eq!(draft.draft().enable_auto_launch, Some(true)); assert_eq!(draft.draft().enable_tun_mode, Some(false)); - let mut d = draft.draft(); - d.enable_auto_launch = Some(false); - d.enable_tun_mode = Some(true); - drop(d); + { + let mut d = draft.draft(); + d.enable_auto_launch = Some(false); + d.enable_tun_mode = Some(true); + } assert_eq!(draft.data().enable_auto_launch, Some(true)); assert_eq!(draft.data().enable_tun_mode, Some(false)); @@ -109,18 +118,17 @@ fn test_draft() { assert_eq!(draft.draft().enable_auto_launch, Some(false)); assert_eq!(draft.draft().enable_tun_mode, Some(true)); - let mut d = draft.draft(); - d.enable_auto_launch = Some(true); - drop(d); + { + let mut d = draft.draft(); + d.enable_auto_launch = Some(true); + } assert_eq!(draft.data().enable_auto_launch, Some(false)); - assert_eq!(draft.draft().enable_auto_launch, Some(true)); assert!(draft.discard().is_some()); assert_eq!(draft.data().enable_auto_launch, Some(false)); - assert!(draft.discard().is_none()); assert_eq!(draft.draft().enable_auto_launch, Some(false)); diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs index 7734fbc9..50255600 100644 --- a/src-tauri/src/core/core.rs +++ b/src-tauri/src/core/core.rs @@ -140,11 +140,11 @@ impl CoreManager { /// 使用默认配置 pub async fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> { let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG); - *Config::runtime().draft() = IRuntime { + *Config::runtime().draft() = Box::new(IRuntime { config: Some(Config::clash().latest().0.clone()), exists_keys: vec![], chain_logs: Default::default(), - }; + }); help::save_yaml( &runtime_path, &Config::clash().latest().0, diff --git a/src-tauri/src/enhance/script.rs b/src-tauri/src/enhance/script.rs index 1f837098..98919794 100644 --- a/src-tauri/src/enhance/script.rs +++ b/src-tauri/src/enhance/script.rs @@ -104,10 +104,10 @@ fn test_script() { let (config, results) = use_script(script.into(), config, "".to_string()).unwrap(); let _ = serde_yaml::to_string(&config).unwrap(); - let origin_size = std::mem::size_of_val(&config); - dbg!(origin_size); - let box_size = std::mem::size_of_val(&Box::new(config)); - dbg!(box_size); + let yaml_config_size = std::mem::size_of_val(&config); + dbg!(yaml_config_size); + let box_yaml_config_size = std::mem::size_of_val(&Box::new(config)); + dbg!(box_yaml_config_size); dbg!(results); - assert!(origin_size > box_size); + assert!(box_yaml_config_size < yaml_config_size); }