SyncUnsafeCell替換Mutex提高性能

1. 背景

在Rust開發過程中,很多情況下需要在不可變的情況下獲取可變性或者在多線程的情況下可以安全的貢獻可變數據。這種情況下我們一般使用**Mutex來實現通過加鎖來實現。現在我們可以通過使用SyncUnsafeCell來替代Mutex**。

2. SyncUnsafeCell

SyncUnsafeCell 是 Rust 標準庫中的一個類型,用于在多線程環境中安全地共享可變數據。它是 UnsafeCell 的一個包裝,提供了額外的同步機制。

作用

  1. 共享可變數據:在 Rust 中,默認情況下,數據是不可變的,且不能在多個線程之間共享可變數據。SyncUnsafeCell 允許你在多線程環境中共享可變數據。
  2. 內部可變性SyncUnsafeCell 提供了內部可變性,這意味著你可以在不獲取可變引用的情況下修改其內容。這對于需要在多線程環境中共享可變狀態的場景非常有用。
  3. 安全性:雖然名字中包含 “Unsafe”,但 SyncUnsafeCell 在多線程環境中提供了一定程度的安全性。它通過內部的同步機制確保了對 UnsafeCell 的訪問是安全的。

使用場景

  • 多線程共享狀態:當你需要在多個線程之間共享可變狀態時,可以使用 SyncUnsafeCell
  • 性能優化:在某些情況下,使用 SyncUnsafeCell 可以比使用 MutexRwLock 等同步原語更高效,因為它提供了更細粒度的控制。

3. SyncUnsafeCell在Rocketmq-rust中的應用

在**TopicConfigManager** 功能模塊宗,因為很多方法都是使用了不可變引用 &self 那么需要修改**data_version** 就必須使用可變引用。為了解決這個問題就使用**Mutex** 來實現。如下圖:

image-20240630155428782

但是這種情況下每次獲取可以變引用都需要進行加鎖才能獲取。而這里的同步性是可預見的。不存在數據競爭所以使用**SyncUnsafeCell來替換Mutex** 減少加鎖帶來的性能消耗。

image-20240630155933525

這里為什么不使用**UnsafeCell** 因為在rocketmq-rust項目中需要Sync也就是:

image-20240630160220034

4.SyncUnsafeCell和Mutex的bench表現測試

我們使用下面的代碼進行測試(測試代碼參照:https://github.com/mxsm/rocketmq-rust/blob/main/rocketmq-broker/benches/syncunsafecell_mut.rs):

#![feature(sync_unsafe_cell)]
use std::cell::SyncUnsafeCell;
use std::collections::HashSet;use criterion::criterion_group;
use criterion::criterion_main;
use criterion::Criterion;pub struct Test {pub a: SyncUnsafeCell<HashSet<String>>,pub b: parking_lot::Mutex<HashSet<String>>,
}impl Test {pub fn new() -> Self {Test {a: SyncUnsafeCell::new(HashSet::new()),b: parking_lot::Mutex::new(HashSet::new()),}}pub fn insert_1(&self, key: String) {unsafe {let a = &mut *self.a.get();a.insert(key);}}pub fn insert_2(&self, key: String) {let mut b = self.b.lock();b.insert(key);}pub fn get_1(&self, key: &str) -> String {unsafe {let a = &*self.a.get();a.get(key).unwrap().to_string()}}pub fn get_2(&self, key: &str) -> String {let b = self.b.lock();b.get(key).unwrap().as_str().to_string()}
}fn benchmark_insert_1(c: &mut Criterion) {let test = Test::new();c.bench_function("insert_1", |b| {b.iter(|| {test.insert_1("key".to_string());})});
}fn benchmark_insert_2(c: &mut Criterion) {let test = Test::new();c.bench_function("insert_2", |b| {b.iter(|| {test.insert_2("key".to_string());})});
}fn benchmark_get_1(c: &mut Criterion) {let test = Test::new();let key = String::from("test_key");// Insert key for the get benchmarkstest.insert_1(key.clone());c.bench_function("get_1", |b| {b.iter(|| {test.get_1("test_key");})});
}fn benchmark_get_2(c: &mut Criterion) {let test = Test::new();let key = String::from("test_key");// Insert key for the get benchmarkstest.insert_2(key.clone());c.bench_function("get_2", |b| {b.iter(|| {test.get_2("test_key");})});
}criterion_group!(benches,benchmark_insert_1,benchmark_insert_2,benchmark_get_1,benchmark_get_2
);
criterion_main!(benches);

測試命令:

cargo bench

執行結果:

image-20240630160952494

要比較 insert_1insert_2 方法的優劣,我們需要考慮以下幾個方面:

  1. 執行時間:

    • insert_1: 平均執行時間約為 42.637 ns。
    • insert_2: 平均執行時間約為 54.484 ns。

    從執行時間上看,insert_1 明顯比 insert_2 快。

  2. 性能變化:

    • insert_1: 性能提升了約 5.8%。
    • insert_2: 性能下降了約 2.5%。

    insert_1 的性能提升,而 insert_2 的性能下降。

  3. 異常值數量:

    • insert_1: 4個異常值,3個輕微異常,1個嚴重異常。
    • insert_2: 4個異常值,均為輕微異常。

    雖然兩者的異常值數量相同,但 insert_1 的異常值有一個嚴重異常,而 insert_2 的異常值均為輕微異常。

綜合來看,insert_1 方法在執行時間和性能提升方面明顯優于 insert_2,但需要注意的是 insert_1 存在一個嚴重異常的情況。這表明在大多數情況下 insert_1 是更好的選擇,但在某些極端情況下可能會有性能波動。

4.1 get_1 和 get_2 方法的比較

  1. 執行時間:

    • get_1: 平均執行時間約為 49.228 ns。
    • get_2: 平均執行時間約為 50.564 ns。

    從執行時間上看,get_1 稍微比 get_2 快。

  2. 異常值數量:

    • get_1: 7個異常值,1個輕微異常,6個嚴重異常。
    • get_2: 4個異常值,均為輕微異常。

    get_1 存在較多的嚴重異常值,而 get_2 異常值較少且均為輕微異常。

結論
  • 插入操作insert_1 是更好的選擇,因為它的執行時間更短且性能提升顯著。雖然存在一些嚴重異常,但整體表現優于 insert_2
  • 獲取操作get_1 的平均執行時間比 get_2 快,但存在較多嚴重異常。如果系統對性能一致性要求較高,get_2 可能是更好的選擇,因為它的異常值較少且均為輕微異常。

總的來說,如果追求平均性能且可以接受一定程度的性能波動,insert_1get_1 是較好的選擇;如果追求性能的一致性,insert_2get_2 可能更適合。

image-20240630161655253

讓我們分析 insert_1 基準測試的結果:

4.2 圖表分析

左側圖表(密度圖)
  • X軸:代表插入操作的平均時間(以納秒為單位)。
  • Y軸:代表密度(密度單位)。
  • 藍色區域:顯示了每次迭代所花費時間的概率分布,密度圖的高峰顯示了最可能的時間范圍。
  • 藍色垂直線:代表平均時間。對于 insert_1 操作,平均時間大約為 42.637 ns。
右側圖表(總樣本時間)
  • X軸:表示迭代次數(以百萬次為單位)。
  • Y軸:表示總樣本時間(以毫秒為單位)。
  • 藍色點:顯示了總樣本時間的線性回歸,表明每次迭代的時間是相對恒定的。
附加統計數據
  • Slope:斜率,估算的每次迭代所需時間。
    • 下限:42.590 ns
    • 估算值:42.637 ns
    • 上限:42.686 ns
  • R2:決定系數,表示數據擬合度。值越接近1,擬合度越高。
    • R2:0.9954266
  • Mean:平均時間。
    • 42.661 ns
  • Std. Dev.:標準差,表示數據的離散程度。
    • 391.47 ps
  • Median:中位數,表示數據的中間值。
    • 42.591 ns
  • MAD:中位絕對偏差,表示數據的波動性。
    • 219.56 ps
結論
  • 平均時間insert_1 操作的平均時間為 42.637 ns。這表明操作的時間開銷非常小。
  • 一致性:高 R2 值(0.9954266)表明基準測試結果非常一致,操作時間非常穩定。
  • 波動性:標準差(391.47 ps)和中位絕對偏差(219.56 ps)都非常小,表明操作時間波動很小。

總的來說,insert_1 操作性能非常穩定且高效,時間開銷非常小。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/37150.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/37150.shtml
英文地址,請注明出處:http://en.pswp.cn/web/37150.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【計算機網絡——1.1網絡internet】

網絡 就是用通信線路和通信設備把很多個“主機/端設備“相互聯系。然后按照某種溝通方式&#xff0c;專業術語叫“協議”&#xff0c;共享信息。 **&#xff08; 計算機網絡&#xff1a;節點和邊構成的系統 節點&#xff1a; 主機節點&#xff1a;主機/端設備(手機&#x…

K8S之網絡深度剖析(一)(持續更新ing)

K8S之網絡深度剖析 一 、關于K8S的網絡模型 在K8s的世界上,IP是以Pod為單位進行分配的。一個Pod內部的所有容器共享一個網絡堆棧(相當于一個網絡命名空間,它們的IP地址、網絡設備、配置等都是共享的)。按照這個網絡原則抽象出來的為每個Pod都設置一個IP地址的模型也被稱作為I…

SpringBoot(一)創建一個簡單的SpringBoot工程

Spring框架常用注解簡單介紹 SpringMVC常用注解簡單介紹 SpringBoot&#xff08;一&#xff09;創建一個簡單的SpringBoot工程 SpringBoot&#xff08;二&#xff09;SpringBoot多環境配置 SpringBoot&#xff08;三&#xff09;SpringBoot整合MyBatis SpringBoot&#xff08;四…

3.ROS串口實例

#include <iostream> #include <ros/ros.h> #include <serial/serial.h> #include<geometry_msgs/Twist.h> using namespace std;//運行打開速度控制插件&#xff1a; rosrun rqt_robot_steering rqt_robot_steering //若串口訪問權限不夠&#xff1a…

詳解PEFT庫中LoRA源碼

前言 GitHub項目地址Some-Paper-CN。本項目是譯者在學習長時間序列預測、CV、NLP和機器學習過程中精讀的一些論文&#xff0c;并對其進行了中文翻譯。還有部分最佳示例教程。如果有幫助到大家&#xff0c;請幫忙點亮Star&#xff0c;也是對譯者莫大的鼓勵&#xff0c;謝謝啦~本…

讀書筆記-《Spring技術內幕》(三)MVC與Web環境

前面我們學習了 Spring 最核心的 IoC 與 AOP 模塊&#xff08;讀書筆記-《Spring技術內幕》&#xff08;一&#xff09;IoC容器的實現、讀書筆記-《Spring技術內幕》&#xff08;二&#xff09;AOP的實現&#xff09;&#xff0c;接下來繼續學習 MVC&#xff0c;其同樣也是經典…

Spring底層原理之bean的加載方式八 BeanDefinitionRegistryPostProcessor注解

BeanDefinitionRegistryPostProcessor注解 這種方式和第七種比較像 要實現兩個方法 第一個方法是實現工廠 第二個方法叫后處理bean注冊 package com.bigdata1421.bean;import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.…

解決idea中git無法管理項目中所有需要管理的文件

點擊文件->設置 選擇版本控制—>目錄映射 點擊加號 設置整個項目被Git管理

【python入門】自定義函數

文章目錄 定義自定義函數的基本語法參數類型示例代碼函數作用域匿名函數&#xff08;Lambda&#xff09;閉包裝飾器 Python中的自定義函數允許你編寫一段可重用的代碼塊&#xff0c;這段代碼可以帶參數&#xff08;輸入&#xff09;&#xff0c;并可能返回一個值&#xff08;輸…

MySQL高級-事務-并發事務演示及隔離級別

文章目錄 0、四種隔離級別1、創建表 account2、修改當前會話隔離級別為 read uncommitted2.1、會出現臟讀 3、修改當前會話隔離級別為 read committed3.1、可以解決臟讀3.2、會出現不可重復讀 4、修改當前會話隔離級別為 repeatable read&#xff08;默認&#xff09;4.1、解決…

解決docker鏡像pull失敗的有效

機器環境 本實踐將在 Ubuntu 22.04.3LTS 系統上進行測試 docker 版本Docker Engine - Community 24.0.6 &#xff0c;原則上docker版本無影響 本實踐進僅學習研究使用&#xff0c;無作他用途。 背景 曾幾何時&#xff0c;docker鏡像的拉去會失敗&#xff0c;網速會慢&#xff0…

代碼隨想錄算法訓練營第五十三天| 739. 每日溫度、 496.下一個更大元素 I、503.下一個更大元素II

LeetCode 739. 每日溫度 題目鏈接&#xff1a;https://leetcode.cn/problems/daily-temperatures/description/ 文章鏈接&#xff1a;https://programmercarl.com/0739.%E6%AF%8F%E6%97%A5%E6%B8%A9%E5%BA%A6.html 思路 * 單調棧的本質是空間換時間&#xff0c;因為在遍歷的過…

【論文閱讀】transformer及其變體

寫在前面&#xff1a; transformer模型已經是老生常談的一個東西&#xff0c;以transformer為基礎出現了很多變體和文章&#xff0c;Informer、autoformer、itransformer等等都是頂刊頂會。一提到transformer自然就是注意力機制&#xff0c;變體更是數不勝數&#xff0c;一提到…

【目標檢測】DN-DETR

一、引言 論文&#xff1a; DN-DETR: Accelerate DETR Training by Introducing Query DeNoising 作者&#xff1a; IDEA 代碼&#xff1a; DN-DETR 注意&#xff1a; 該算法是在DAB-DETR基礎上的改進&#xff0c;在學習該算法前&#xff0c;建議掌握DETR、DAB-DETR等相關知識…

TCP和UDP的區別以及應用場景

TCP&#xff08;傳輸控制協議&#xff09;和UDP&#xff08;用戶數據報協議&#xff09;是兩種不同的傳輸層協議 區別 TCP是面向連接的&#xff0c;UDP是無連接的&#xff1b; TCP是可靠的&#xff0c;UDP是不可靠的&#xff1b; TCP是面向字節流的&#xff0c;UDP是面向數據…

如何高效配置與使用Pip換源

目錄 1. Pip源的基本概念 1.1 常見的國內鏡像源 2. 臨時換源 2.1 使用命令行參數指定鏡像源 2.2 安裝多個包時指定鏡像源 3. 永久換源 3.1 修改用戶級配置文件 3.1.1 創建和編輯配置文件 3.2 修改全局配置文件 3.2.1 創建和編輯全局配置文件 4. 驗證換源配置 5. 切…

VMamba: Visual State Space Model論文筆記

文章目錄 VMamba: Visual State Space Model摘要引言相關工作Preliminaries方法網絡結構2D-Selective-Scan for Vision Data(SS2D) VMamba: Visual State Space Model 論文地址: https://arxiv.org/abs/2401.10166 代碼地址: https://github.com/MzeroMiko/VMamba 摘要 卷積神…

防火墻共性檢測技術

防火墻共性檢測技術 防火墻共性檢測技術是指防火墻在監控和控制網絡流量時&#xff0c;共同采用的一些檢測和過濾方法。無論是哪種類型的防火墻&#xff0c;這些技術都可以用于識別和阻止惡意流量&#xff0c;確保網絡安全。以下是防火墻共性檢測技術的詳細介紹&#xff0c;包…

axios的基本使用和vue腳手架自帶的跨域問題解決

axios的基本使用和vue腳手架自帶的跨域問題解決 1. axios 1.1 導入axios npm i axios1.2 創建serve1.js serve1.js const express require(express) const app express()app.use((request,response,next)>{console.log(有人請求服務器1了);console.log(請求來自于,re…

go Channel 原理 (一)

Channel 設計原理 不要通過共享內存的方式進行通信&#xff0c;而是應該通過通信的方式共享內存。 在主流編程語言中&#xff0c;多個線程傳遞數據的方式一般都是共享內存。 Go 可以使用共享內存加互斥鎖進行通信&#xff0c;同時也提供了一種不同的并發模型&#xff0c;即通…