mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 08:45:41 +08:00
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.
This commit is contained in:
@@ -22,7 +22,7 @@ pub async fn get_profiles() -> CmdResult<IProfiles> {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
match profiles_result {
|
match profiles_result {
|
||||||
Ok(Ok(profiles)) => Ok(profiles),
|
Ok(Ok(profiles)) => Ok(*profiles),
|
||||||
Ok(Err(join_err)) => {
|
Ok(Err(join_err)) => {
|
||||||
logging!(error, Type::Cmd, true, "获取配置列表任务失败: {}", join_err);
|
logging!(error, Type::Cmd, true, "获取配置列表任务失败: {}", join_err);
|
||||||
Ok(IProfiles {
|
Ok(IProfiles {
|
||||||
@@ -41,7 +41,7 @@ pub async fn get_profiles() -> CmdResult<IProfiles> {
|
|||||||
match tokio::task::spawn_blocking(move || Config::profiles().latest().clone()).await {
|
match tokio::task::spawn_blocking(move || Config::profiles().latest().clone()).await {
|
||||||
Ok(profiles) => {
|
Ok(profiles) => {
|
||||||
logging!(info, Type::Cmd, true, "使用latest()成功获取配置");
|
logging!(info, Type::Cmd, true, "使用latest()成功获取配置");
|
||||||
Ok(profiles)
|
Ok(*profiles)
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
logging!(error, Type::Cmd, true, "fallback获取配置也失败,返回空配置");
|
logging!(error, Type::Cmd, true, "fallback获取配置也失败,返回空配置");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{config::*, feat, wrap_err};
|
|||||||
pub fn get_verge_config() -> CmdResult<IVergeResponse> {
|
pub fn get_verge_config() -> CmdResult<IVergeResponse> {
|
||||||
let verge = Config::verge();
|
let verge = Config::verge();
|
||||||
let verge_data = verge.data().clone();
|
let verge_data = verge.data().clone();
|
||||||
Ok(IVergeResponse::from(verge_data))
|
Ok(IVergeResponse::from(*verge_data))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 修改Verge配置
|
/// 修改Verge配置
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ pub const RUNTIME_CONFIG: &str = "clash-verge.yaml";
|
|||||||
pub const CHECK_CONFIG: &str = "clash-verge-check.yaml";
|
pub const CHECK_CONFIG: &str = "clash-verge-check.yaml";
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
clash_config: Draft<IClashTemp>,
|
clash_config: Draft<Box<IClashTemp>>,
|
||||||
verge_config: Draft<IVerge>,
|
verge_config: Draft<Box<IVerge>>,
|
||||||
profiles_config: Draft<IProfiles>,
|
profiles_config: Draft<Box<IProfiles>>,
|
||||||
runtime_config: Draft<IRuntime>,
|
runtime_config: Draft<Box<IRuntime>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -26,26 +26,26 @@ impl Config {
|
|||||||
static CONFIG: OnceCell<Config> = OnceCell::new();
|
static CONFIG: OnceCell<Config> = OnceCell::new();
|
||||||
|
|
||||||
CONFIG.get_or_init(|| Config {
|
CONFIG.get_or_init(|| Config {
|
||||||
clash_config: Draft::from(IClashTemp::new()),
|
clash_config: Draft::from(Box::new(IClashTemp::new())),
|
||||||
verge_config: Draft::from(IVerge::new()),
|
verge_config: Draft::from(Box::new(IVerge::new())),
|
||||||
profiles_config: Draft::from(IProfiles::new()),
|
profiles_config: Draft::from(Box::new(IProfiles::new())),
|
||||||
runtime_config: Draft::from(IRuntime::new()),
|
runtime_config: Draft::from(Box::new(IRuntime::new())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clash() -> Draft<IClashTemp> {
|
pub fn clash() -> Draft<Box<IClashTemp>> {
|
||||||
Self::global().clash_config.clone()
|
Self::global().clash_config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verge() -> Draft<IVerge> {
|
pub fn verge() -> Draft<Box<IVerge>> {
|
||||||
Self::global().verge_config.clone()
|
Self::global().verge_config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn profiles() -> Draft<IProfiles> {
|
pub fn profiles() -> Draft<Box<IProfiles>> {
|
||||||
Self::global().profiles_config.clone()
|
Self::global().profiles_config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runtime() -> Draft<IRuntime> {
|
pub fn runtime() -> Draft<Box<IRuntime>> {
|
||||||
Self::global().runtime_config.clone()
|
Self::global().runtime_config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,11 +149,11 @@ impl Config {
|
|||||||
pub async fn generate() -> Result<()> {
|
pub async fn generate() -> Result<()> {
|
||||||
let (config, exists_keys, logs) = enhance::enhance().await;
|
let (config, exists_keys, logs) = enhance::enhance().await;
|
||||||
|
|
||||||
*Config::runtime().draft() = IRuntime {
|
*Config::runtime().draft() = Box::new(IRuntime {
|
||||||
config: Some(config),
|
config: Some(config),
|
||||||
exists_keys,
|
exists_keys,
|
||||||
chain_logs: logs,
|
chain_logs: logs,
|
||||||
};
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -164,3 +164,42 @@ pub enum ConfigType {
|
|||||||
Run,
|
Run,
|
||||||
Check,
|
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<T> is always pointer-sized (usually 8 bytes on 64-bit)
|
||||||
|
// assert_eq!(box_prfitem_size, mem::size_of::<Box<PrfItem>>());
|
||||||
|
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::<Draft<IRuntime>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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::<Draft<Box<IRuntime>>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,13 +9,21 @@ pub struct Draft<T: Clone + ToOwned> {
|
|||||||
|
|
||||||
macro_rules! draft_define {
|
macro_rules! draft_define {
|
||||||
($id: ident) => {
|
($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<Box<$id>> {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn data(&self) -> MappedMutexGuard<$id> {
|
pub fn data(&self) -> MappedMutexGuard<Box<$id>> {
|
||||||
MutexGuard::map(self.inner.lock(), |guard| &mut guard.0)
|
MutexGuard::map(self.inner.lock(), |guard| &mut guard.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn latest(&self) -> MappedMutexGuard<$id> {
|
pub fn latest(&self) -> MappedMutexGuard<Box<$id>> {
|
||||||
MutexGuard::map(self.inner.lock(), |inner| {
|
MutexGuard::map(self.inner.lock(), |inner| {
|
||||||
if inner.1.is_none() {
|
if inner.1.is_none() {
|
||||||
&mut inner.0
|
&mut inner.0
|
||||||
@@ -25,7 +33,7 @@ macro_rules! draft_define {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draft(&self) -> MappedMutexGuard<$id> {
|
pub fn draft(&self) -> MappedMutexGuard<Box<$id>> {
|
||||||
MutexGuard::map(self.inner.lock(), |inner| {
|
MutexGuard::map(self.inner.lock(), |inner| {
|
||||||
if inner.1.is_none() {
|
if inner.1.is_none() {
|
||||||
inner.1 = Some(inner.0.clone());
|
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<Box<$id>> {
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
|
|
||||||
match inner.1.take() {
|
match inner.1.take() {
|
||||||
@@ -48,14 +56,14 @@ macro_rules! draft_define {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn discard(&self) -> Option<$id> {
|
pub fn discard(&self) -> Option<Box<$id>> {
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
inner.1.take()
|
inner.1.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<$id> for Draft<$id> {
|
impl From<Box<$id>> for Draft<Box<$id>> {
|
||||||
fn from(data: $id) -> Self {
|
fn from(data: Box<$id>) -> Self {
|
||||||
Draft {
|
Draft {
|
||||||
inner: Arc::new(Mutex::new((data, None))),
|
inner: Arc::new(Mutex::new((data, None))),
|
||||||
}
|
}
|
||||||
@@ -71,12 +79,12 @@ draft_define!(IRuntime);
|
|||||||
draft_define!(IVerge);
|
draft_define!(IVerge);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_draft() {
|
fn test_draft_box() {
|
||||||
let verge = IVerge {
|
let verge = Box::new(IVerge {
|
||||||
enable_auto_launch: Some(true),
|
enable_auto_launch: Some(true),
|
||||||
enable_tun_mode: Some(false),
|
enable_tun_mode: Some(false),
|
||||||
..IVerge::default()
|
..IVerge::default()
|
||||||
};
|
});
|
||||||
|
|
||||||
let draft = Draft::from(verge);
|
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_auto_launch, Some(true));
|
||||||
assert_eq!(draft.draft().enable_tun_mode, Some(false));
|
assert_eq!(draft.draft().enable_tun_mode, Some(false));
|
||||||
|
|
||||||
let mut d = draft.draft();
|
{
|
||||||
d.enable_auto_launch = Some(false);
|
let mut d = draft.draft();
|
||||||
d.enable_tun_mode = Some(true);
|
d.enable_auto_launch = Some(false);
|
||||||
drop(d);
|
d.enable_tun_mode = Some(true);
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(draft.data().enable_auto_launch, Some(true));
|
assert_eq!(draft.data().enable_auto_launch, Some(true));
|
||||||
assert_eq!(draft.data().enable_tun_mode, Some(false));
|
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_auto_launch, Some(false));
|
||||||
assert_eq!(draft.draft().enable_tun_mode, Some(true));
|
assert_eq!(draft.draft().enable_tun_mode, Some(true));
|
||||||
|
|
||||||
let mut d = draft.draft();
|
{
|
||||||
d.enable_auto_launch = Some(true);
|
let mut d = draft.draft();
|
||||||
drop(d);
|
d.enable_auto_launch = Some(true);
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(draft.data().enable_auto_launch, Some(false));
|
assert_eq!(draft.data().enable_auto_launch, Some(false));
|
||||||
|
|
||||||
assert_eq!(draft.draft().enable_auto_launch, Some(true));
|
assert_eq!(draft.draft().enable_auto_launch, Some(true));
|
||||||
|
|
||||||
assert!(draft.discard().is_some());
|
assert!(draft.discard().is_some());
|
||||||
|
|
||||||
assert_eq!(draft.data().enable_auto_launch, Some(false));
|
assert_eq!(draft.data().enable_auto_launch, Some(false));
|
||||||
|
|
||||||
assert!(draft.discard().is_none());
|
assert!(draft.discard().is_none());
|
||||||
|
|
||||||
assert_eq!(draft.draft().enable_auto_launch, Some(false));
|
assert_eq!(draft.draft().enable_auto_launch, Some(false));
|
||||||
|
|||||||
@@ -140,11 +140,11 @@ impl CoreManager {
|
|||||||
/// 使用默认配置
|
/// 使用默认配置
|
||||||
pub async fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> {
|
pub async fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> {
|
||||||
let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG);
|
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()),
|
config: Some(Config::clash().latest().0.clone()),
|
||||||
exists_keys: vec![],
|
exists_keys: vec![],
|
||||||
chain_logs: Default::default(),
|
chain_logs: Default::default(),
|
||||||
};
|
});
|
||||||
help::save_yaml(
|
help::save_yaml(
|
||||||
&runtime_path,
|
&runtime_path,
|
||||||
&Config::clash().latest().0,
|
&Config::clash().latest().0,
|
||||||
|
|||||||
@@ -104,10 +104,10 @@ fn test_script() {
|
|||||||
let (config, results) = use_script(script.into(), config, "".to_string()).unwrap();
|
let (config, results) = use_script(script.into(), config, "".to_string()).unwrap();
|
||||||
|
|
||||||
let _ = serde_yaml::to_string(&config).unwrap();
|
let _ = serde_yaml::to_string(&config).unwrap();
|
||||||
let origin_size = std::mem::size_of_val(&config);
|
let yaml_config_size = std::mem::size_of_val(&config);
|
||||||
dbg!(origin_size);
|
dbg!(yaml_config_size);
|
||||||
let box_size = std::mem::size_of_val(&Box::new(config));
|
let box_yaml_config_size = std::mem::size_of_val(&Box::new(config));
|
||||||
dbg!(box_size);
|
dbg!(box_yaml_config_size);
|
||||||
dbg!(results);
|
dbg!(results);
|
||||||
assert!(origin_size > box_size);
|
assert!(box_yaml_config_size < yaml_config_size);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user