From 518875acdea72fb4bc8611086a547b67cb77d274 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Fri, 31 Oct 2025 23:31:04 +0800 Subject: [PATCH] refactor: update draft handling and improve benchmark structure --- src-tauri/benches/draft_benchmark.rs | 189 +++++++++++---------------- src-tauri/src/config/config.rs | 4 +- src-tauri/src/config/verge.rs | 13 +- src-tauri/src/core/manager/config.rs | 4 +- src-tauri/src/core/timer.rs | 16 ++- src-tauri/src/feat/profile.rs | 2 +- src-tauri/src/feat/window.rs | 2 +- src-tauri/src/utils/draft.rs | 32 ++--- 8 files changed, 114 insertions(+), 148 deletions(-) diff --git a/src-tauri/benches/draft_benchmark.rs b/src-tauri/benches/draft_benchmark.rs index 694fc82a..3942382f 100644 --- a/src-tauri/benches/draft_benchmark.rs +++ b/src-tauri/benches/draft_benchmark.rs @@ -3,7 +3,6 @@ use std::hint::black_box; use std::process; use tokio::runtime::Runtime; -// 引入业务模型 & Draft 实现 use app_lib::config::IVerge; use app_lib::utils::Draft as DraftNew; @@ -17,108 +16,86 @@ fn make_draft() -> DraftNew> { DraftNew::from(verge) } -/// 基准:只读 data_ref(正式数据) -fn bench_data_ref(c: &mut Criterion) { - c.bench_function("draft_data_ref", |b| { - b.iter(|| { - let draft = make_draft(); - let data = draft.data_ref(); - black_box(data.enable_auto_launch); - }); - }); -} - -/// 基准:可写 data_mut(正式数据) -fn bench_data_mut(c: &mut Criterion) { - c.bench_function("draft_data_mut", |b| { - b.iter(|| { - let draft = make_draft(); - let mut data = draft.data_mut(); - data.enable_tun_mode = Some(true); - black_box(data.enable_tun_mode); - }); - }); -} - -/// 基准:首次创建草稿(会触发 clone) -fn bench_draft_mut_first(c: &mut Criterion) { - c.bench_function("draft_draft_mut_first", |b| { - b.iter(|| { - let draft = make_draft(); - let mut d = draft.draft_mut(); - d.enable_auto_launch = Some(false); - black_box(d.enable_auto_launch); - }); - }); -} - -/// 基准:重复 draft_mut(已存在草稿,不再 clone) -fn bench_draft_mut_existing(c: &mut Criterion) { - c.bench_function("draft_draft_mut_existing", |b| { - b.iter(|| { - let draft = make_draft(); - { - let mut first = draft.draft_mut(); - first.enable_tun_mode = Some(true); - } - let mut second = draft.draft_mut(); - second.enable_tun_mode = Some(false); - black_box(second.enable_tun_mode); - }); - }); -} - -/// 基准:零拷贝读取最新视图(latest_ref) -fn bench_latest_ref(c: &mut Criterion) { - c.bench_function("draft_latest_ref", |b| { - b.iter(|| { - let draft = make_draft(); - let latest = draft.latest_ref(); - black_box(latest.enable_auto_launch); - }); - }); -} - -/// 基准:apply(提交草稿) -fn bench_apply(c: &mut Criterion) { - c.bench_function("draft_apply", |b| { - b.iter(|| { - let draft = make_draft(); - { - let mut d = draft.draft_mut(); - d.enable_auto_launch = Some(false); - } - let _ = draft.apply(); - }); - }); -} - -/// 基准:discard(丢弃草稿) -fn bench_discard(c: &mut Criterion) { - c.bench_function("draft_discard", |b| { - b.iter(|| { - let draft = make_draft(); - { - let mut d = draft.draft_mut(); - d.enable_auto_launch = Some(false); - } - let _ = draft.discard(); - }); - }); -} - -/// 基准:异步 with_data_modify -fn bench_with_data_modify(c: &mut Criterion) { - let rt = Runtime::new().unwrap_or_else(|error| { - eprintln!("draft benchmarks require a Tokio runtime: {error}"); +pub fn bench_draft(c: &mut Criterion) { + let rt = Runtime::new().unwrap_or_else(|e| { + eprintln!("Tokio runtime init failed: {e}"); process::exit(1); }); - c.bench_function("draft_with_data_modify", |b| { + let mut group = c.benchmark_group("draft"); + group.sample_size(100); + group.warm_up_time(std::time::Duration::from_millis(300)); + group.measurement_time(std::time::Duration::from_secs(1)); + + group.bench_function("data_mut", |b| { + b.iter(|| { + let draft = black_box(make_draft()); + let mut data = draft.data_mut(); + data.enable_tun_mode = Some(true); + black_box(&data.enable_tun_mode); + }); + }); + + group.bench_function("draft_mut_first", |b| { + b.iter(|| { + let draft = black_box(make_draft()); + let mut d = draft.draft_mut(); + d.enable_auto_launch = Some(false); + black_box(&d.enable_auto_launch); + }); + }); + + group.bench_function("draft_mut_existing", |b| { + b.iter(|| { + let draft = black_box(make_draft()); + { + let mut first = draft.draft_mut(); + first.enable_tun_mode = Some(true); + black_box(&first.enable_tun_mode); + } + let mut second = draft.draft_mut(); + second.enable_tun_mode = Some(false); + black_box(&second.enable_tun_mode); + }); + }); + + group.bench_function("latest_ref", |b| { + b.iter(|| { + let draft = black_box(make_draft()); + let latest = draft.latest_ref(); + black_box(&latest.enable_auto_launch); + }); + }); + + group.bench_function("apply", |b| { + b.iter(|| { + let draft = black_box(make_draft()); + { + let mut d = draft.draft_mut(); + d.enable_auto_launch = Some(false); + } + draft.apply(); + black_box(&draft); + }); + }); + + group.bench_function("discard", |b| { + b.iter(|| { + let draft = black_box(make_draft()); + { + let mut d = draft.draft_mut(); + d.enable_auto_launch = Some(false); + } + draft.discard(); + black_box(&draft); + }); + }); + + group.bench_function("with_data_modify_async", |b| { b.to_async(&rt).iter(|| async { - let draft = make_draft(); - let _res: Result<(), anyhow::Error> = draft - .with_data_modify(|mut box_data| async move { + let draft = black_box(make_draft()); + let _: Result<(), anyhow::Error> = draft + .with_data_modify::<_, _, _, anyhow::Error>(|mut box_data| async move { box_data.enable_auto_launch = Some(!box_data.enable_auto_launch.unwrap_or(false)); Ok((box_data, ())) @@ -126,17 +103,9 @@ fn bench_with_data_modify(c: &mut Criterion) { .await; }); }); + + group.finish(); } -criterion_group!( - benches, - bench_data_ref, - bench_data_mut, - bench_draft_mut_first, - bench_draft_mut_existing, - bench_latest_ref, - bench_apply, - bench_discard, - bench_with_data_modify -); +criterion_group!(benches, bench_draft); criterion_main!(benches); diff --git a/src-tauri/src/config/config.rs b/src-tauri/src/config/config.rs index 4318a3e2..246a4c6b 100644 --- a/src-tauri/src/config/config.rs +++ b/src-tauri/src/config/config.rs @@ -168,11 +168,11 @@ impl Config { pub async fn generate() -> Result<()> { let (config, exists_keys, logs) = enhance::enhance().await; - *Config::runtime().await.draft_mut() = Box::new(IRuntime { + **Config::runtime().await.draft_mut() = IRuntime { config: Some(config), exists_keys, chain_logs: logs, - }); + }; Ok(()) } diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index bfa45816..25edd04b 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -1,3 +1,4 @@ +use crate::config::Config; use crate::{ config::{DEFAULT_PAC, deserialize_encrypted, serialize_encrypted}, logging, @@ -304,19 +305,17 @@ impl IVerge { /// 配置修正后重新加载配置 async fn reload_config_after_fix(updated_config: IVerge) -> Result<()> { - use crate::config::Config; - - let config_draft = Config::verge().await; - *config_draft.draft_mut() = Box::new(updated_config.clone()); - config_draft.apply(); - logging!( info, Type::Config, "内存配置已强制更新,新的clash_core: {:?}", - updated_config.clash_core + &updated_config.clash_core ); + let config_draft = Config::verge().await; + **config_draft.draft_mut() = updated_config; + config_draft.apply(); + Ok(()) } diff --git a/src-tauri/src/core/manager/config.rs b/src-tauri/src/core/manager/config.rs index 263ddb4b..70b4bc6f 100644 --- a/src-tauri/src/core/manager/config.rs +++ b/src-tauri/src/core/manager/config.rs @@ -19,11 +19,11 @@ impl CoreManager { let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG); let clash_config = Config::clash().await.latest_ref().0.clone(); - *Config::runtime().await.draft_mut() = Box::new(IRuntime { + **Config::runtime().await.draft_mut() = IRuntime { config: Some(clash_config.clone()), exists_keys: vec![], chain_logs: Default::default(), - }); + }; help::save_yaml(&runtime_path, &clash_config, Some("# Clash Verge Runtime")).await?; handle::Handle::notice_message(error_key, error_msg); diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index 2ceca275..fa232933 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -419,13 +419,15 @@ impl Timer { }; // Get the profile updated timestamp - now safe to await - let config_profiles = Config::profiles().await; - let profiles = config_profiles.data_ref().clone(); - let items = match profiles.get_items() { - Some(i) => i, - None => { - logging!(warn, Type::Timer, "获取配置列表失败"); - return None; + let items = { + let profiles = Config::profiles().await; + let profiles_guard = profiles.latest_ref(); + match profiles_guard.get_items() { + Some(i) => i.clone(), + None => { + logging!(warn, Type::Timer, "获取配置列表失败"); + return None; + } } }; diff --git a/src-tauri/src/feat/profile.rs b/src-tauri/src/feat/profile.rs index 948ba3af..ffba85d0 100644 --- a/src-tauri/src/feat/profile.rs +++ b/src-tauri/src/feat/profile.rs @@ -102,7 +102,7 @@ async fn perform_profile_update( let profile_name = item.name.clone().unwrap_or_else(|| uid.clone()); handle::Handle::notice_message("update_with_clash_proxy", profile_name); - let is_current = Some(uid.clone()) == profiles.data_ref().get_current(); + let is_current = Some(uid.clone()) == profiles.latest_ref().get_current(); log::info!(target: "app", "[订阅更新] 是否为当前使用的订阅: {is_current}"); Ok(is_current) } diff --git a/src-tauri/src/feat/window.rs b/src-tauri/src/feat/window.rs index 57cfc810..246179e5 100644 --- a/src-tauri/src/feat/window.rs +++ b/src-tauri/src/feat/window.rs @@ -47,7 +47,7 @@ pub async fn clean_async() -> bool { let tun_task = async { let tun_enabled = Config::verge() .await - .data_ref() + .latest_ref() .enable_tun_mode .unwrap_or(false); diff --git a/src-tauri/src/utils/draft.rs b/src-tauri/src/utils/draft.rs index 044f6f1f..76465db9 100644 --- a/src-tauri/src/utils/draft.rs +++ b/src-tauri/src/utils/draft.rs @@ -24,7 +24,6 @@ impl From for Draft { /// /// # Methods /// - `data_mut`: Returns a mutable reference to the committed data. -/// - `data_ref`: Returns an immutable reference to the committed data. /// - `draft_mut`: Creates or retrieves a mutable reference to the draft data, cloning the committed data if no draft exists. /// - `latest_ref`: Returns an immutable reference to the draft data if it exists, otherwise to the committed data. /// - `apply`: Commits the draft data, replacing the committed data and returning the old committed value if a draft existed. @@ -35,11 +34,6 @@ impl Draft> { RwLockWriteGuard::map(self.inner.write(), |inner| &mut inner.0) } - /// 返回正式数据的只读视图(不包含草稿) - pub fn data_ref(&self) -> MappedRwLockReadGuard<'_, Box> { - RwLockReadGuard::map(self.inner.read(), |inner| &inner.0) - } - /// 创建或获取草稿并返回可写引用 pub fn draft_mut(&self) -> MappedRwLockWriteGuard<'_, Box> { let guard = self.inner.upgradable_read(); @@ -69,17 +63,21 @@ impl Draft> { } /// 提交草稿,返回旧正式数据 - pub fn apply(&self) -> Option> { - let mut inner = self.inner.write(); - inner - .1 - .take() - .map(|draft| std::mem::replace(&mut inner.0, draft)) + pub fn apply(&self) { + let guard = self.inner.upgradable_read(); + if guard.1.is_none() { + return; + } + + let mut guard = RwLockUpgradableReadGuard::upgrade(guard); + if let Some(draft) = guard.1.take() { + guard.0 = draft; + } } /// 丢弃草稿,返回被丢弃的草稿 - pub fn discard(&self) -> Option> { - self.inner.write().1.take() + pub fn discard(&self) { + self.inner.write().1.take(); } /// 异步修改正式数据,闭包直接获得 Box 所有权 @@ -152,8 +150,7 @@ fn test_draft_box() { } // 5. 提交草稿 - assert!(draft.apply().is_some()); // 第一次提交应有返回 - assert!(draft.apply().is_none()); // 第二次提交返回 None + draft.apply(); // 正式数据已更新 { @@ -170,8 +167,7 @@ fn test_draft_box() { assert_eq!(draft.draft_mut().enable_auto_launch, Some(true)); // 7. 丢弃草稿 - assert!(draft.discard().is_some()); // 第一次丢弃返回 Some - assert!(draft.discard().is_none()); // 再次丢弃返回 None + draft.discard(); // 8. 草稿已被丢弃,新的 draft_mut() 会重新 clone assert_eq!(draft.draft_mut().enable_auto_launch, Some(false));