mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 17:15:38 +08:00
perf(draft): optimize memory layout by removing double indirection & implemented optimistic locking via Arc::ptr_eq for with_data_modify (#5942)
* perf(draft): optimize memory layout by removing double indirection - Replace `Arc<Box<T>>` with `Arc<T>` to reduce pointer chasing and memory overhead. - Leverage `Arc::from(Box<T>)` in async modify path for efficient ownership transfer. - Fix race conditions in `edit_draft` by ensuring atomicity under write lock. - Performance improved by ~16-24% across all operations (based on Criterion bench). Benchmarks: - latest_arc: 41.1ns (-24.2%) - edit_draft: 92.2ns (-17.6%) - apply: 89.8ns (-17.7%) - async_modify: 66.0ns (-16.6%) * perf(draft): implemented optimistic locking via Arc::ptr_eq for with_data_modify Benchmarks confirm only a negligible ~2% (1.3ns) overhead for async operations, ensuring total data integrity during concurrent updates.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type SharedBox<T> = Arc<Box<T>>;
|
||||
type DraftInner<T> = (SharedBox<T>, Option<SharedBox<T>>);
|
||||
pub type SharedDraft<T> = Arc<T>;
|
||||
type DraftInner<T> = (SharedDraft<T>, Option<SharedDraft<T>>);
|
||||
|
||||
/// Draft 管理:committed 与 optional draft 都以 Arc<Box<T>> 存储,
|
||||
// (committed_snapshot, optional_draft_snapshot)
|
||||
@@ -15,12 +15,12 @@ impl<T: Clone> Draft<T> {
|
||||
#[inline]
|
||||
pub fn new(data: T) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new((Arc::new(Box::new(data)), None))),
|
||||
inner: Arc::new(RwLock::new((Arc::new(data), None))),
|
||||
}
|
||||
}
|
||||
/// 以 Arc<Box<T>> 的形式获取当前“已提交(正式)”数据的快照(零拷贝,仅 clone Arc)
|
||||
#[inline]
|
||||
pub fn data_arc(&self) -> SharedBox<T> {
|
||||
pub fn data_arc(&self) -> SharedDraft<T> {
|
||||
let guard = self.inner.read();
|
||||
Arc::clone(&guard.0)
|
||||
}
|
||||
@@ -28,7 +28,7 @@ impl<T: Clone> Draft<T> {
|
||||
/// 获取当前(草稿若存在则返回草稿,否则返回已提交)的快照
|
||||
/// 这也是零拷贝:只 clone Arc,不 clone T
|
||||
#[inline]
|
||||
pub fn latest_arc(&self) -> SharedBox<T> {
|
||||
pub fn latest_arc(&self) -> SharedDraft<T> {
|
||||
let guard = self.inner.read();
|
||||
guard.1.clone().unwrap_or_else(|| Arc::clone(&guard.0))
|
||||
}
|
||||
@@ -41,21 +41,11 @@ impl<T: Clone> Draft<T> {
|
||||
where
|
||||
F: FnOnce(&mut T) -> R,
|
||||
{
|
||||
// 先获得写锁以创建或取出草稿 Arc 的可变引用位置
|
||||
let mut guard = self.inner.write();
|
||||
let mut draft_arc = if guard.1.is_none() {
|
||||
Arc::clone(&guard.0)
|
||||
} else {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
guard.1.take().unwrap()
|
||||
};
|
||||
drop(guard);
|
||||
// Arc::make_mut: 如果只有一个引用则返回可变引用;否则会克隆底层 Box<T>(要求 T: Clone)
|
||||
let boxed = Arc::make_mut(&mut draft_arc); // &mut Box<T>
|
||||
// 对 Box<T> 解引用得到 &mut T
|
||||
let result = f(&mut **boxed);
|
||||
// 恢复修改后的草稿 Arc
|
||||
self.inner.write().1 = Some(draft_arc);
|
||||
let mut draft_arc = guard.1.take().unwrap_or_else(|| Arc::clone(&guard.0));
|
||||
let data_mut = Arc::make_mut(&mut draft_arc);
|
||||
let result = f(data_mut);
|
||||
guard.1 = Some(draft_arc);
|
||||
result
|
||||
}
|
||||
|
||||
@@ -84,19 +74,19 @@ impl<T: Clone> Draft<T> {
|
||||
F: FnOnce(Box<T>) -> Fut + Send,
|
||||
Fut: std::future::Future<Output = Result<(Box<T>, R), anyhow::Error>> + Send,
|
||||
{
|
||||
// 读取已提交快照(cheap Arc clone, 然后得到 Box<T> 所有权 via clone)
|
||||
// 注意:为了让闭包接收 Box<T> 所有权,我们需要 clone 底层 T(不可避免)
|
||||
let local: Box<T> = {
|
||||
let (local, original_arc) = {
|
||||
let guard = self.inner.read();
|
||||
// 将 Arc<Box<T>> 的 Box<T> clone 出来(会调用 T: Clone)
|
||||
(*guard.0).clone()
|
||||
let arc = Arc::clone(&guard.0);
|
||||
(Box::new((*arc).clone()), arc)
|
||||
};
|
||||
|
||||
let (new_local, res) = f(local).await?;
|
||||
|
||||
// 将新的 Box<T> 放到已提交位置(包进 Arc)
|
||||
self.inner.write().0 = Arc::new(new_local);
|
||||
|
||||
let mut guard = self.inner.write();
|
||||
if !Arc::ptr_eq(&guard.0, &original_arc) {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Optimistic lock failed: Committed data has changed during async operation"
|
||||
));
|
||||
}
|
||||
guard.0 = Arc::from(new_local);
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::{
|
||||
ret_err,
|
||||
utils::{dirs, help},
|
||||
};
|
||||
use clash_verge_draft::SharedBox;
|
||||
use clash_verge_draft::SharedDraft;
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use scopeguard::defer;
|
||||
use smartstring::alias::String;
|
||||
@@ -26,7 +26,7 @@ use std::time::Duration;
|
||||
static CURRENT_SWITCHING_PROFILE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_profiles() -> CmdResult<SharedBox<IProfiles>> {
|
||||
pub async fn get_profiles() -> CmdResult<SharedDraft<IProfiles>> {
|
||||
logging!(debug, Type::Cmd, "获取配置文件列表");
|
||||
let draft = Config::profiles().await;
|
||||
let data = draft.data_arc();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use super::CmdResult;
|
||||
use crate::{cmd::StringifyErr as _, config::IVerge, feat};
|
||||
use clash_verge_draft::SharedBox;
|
||||
use clash_verge_draft::SharedDraft;
|
||||
|
||||
/// 获取Verge配置
|
||||
#[tauri::command]
|
||||
pub async fn get_verge_config() -> CmdResult<SharedBox<IVerge>> {
|
||||
pub async fn get_verge_config() -> CmdResult<SharedDraft<IVerge>> {
|
||||
feat::fetch_verge_config().await.stringify_err()
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ async fn get_config_values() -> ConfigValues {
|
||||
ref verge_http_enabled,
|
||||
ref enable_dns_settings,
|
||||
..
|
||||
} = **verge_arc;
|
||||
} = *verge_arc;
|
||||
|
||||
let (clash_core, enable_tun, enable_builtin, socks_enabled, http_enabled, enable_dns_settings) = (
|
||||
Some(verge_arc.get_valid_clash_core()),
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
module::{auto_backup::AutoBackupManager, lightweight},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use clash_verge_draft::SharedBox;
|
||||
use clash_verge_draft::SharedDraft;
|
||||
use clash_verge_logging::{Type, logging, logging_error};
|
||||
use serde_yaml_ng::Mapping;
|
||||
|
||||
@@ -269,7 +269,7 @@ pub async fn patch_verge(patch: &IVerge, not_save_file: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fetch_verge_config() -> Result<SharedBox<IVerge>> {
|
||||
pub async fn fetch_verge_config() -> Result<SharedDraft<IVerge>> {
|
||||
let draft = Config::verge().await;
|
||||
let data = draft.data_arc();
|
||||
Ok(data)
|
||||
|
||||
Reference in New Issue
Block a user