Rust以其獨特的安全性、速度和并發(fā)性組合而迅速流行。但是與其它任何語言一樣,要充分利用Rust需要的不僅僅是理解它的語法和習慣用法——還需要深入了解如何有效地利用和優(yōu)化它的編譯器。
為了說明這一點,我們設計了一個實際用例——一個Actix Web應用程序中的矩陣乘法任務。這種cpu密集型操作為分析各種編譯器優(yōu)化提供了一個完美的場景。
隨著實驗的深入,我們將調整Cargo.toml文件的設置。利用特定的構建標志,甚至交換內存分配器。通過測量每次更改對性能的影響,我們將對Rust的編譯器優(yōu)化有一個全面的了解。
實際用例
我們使用Actix Web開發(fā)了一個緊湊的應用程序,具有唯一的路由/matrix-multiplication。這個接口接收一個JSON數(shù)據(jù),帶有一個屬性:n。
在接收到請求后,應用程序立即開始行動,動態(tài)地生成兩個大小為n x n的矩陣,在矩陣中隨機填充一些數(shù)據(jù)。然后將這些矩陣相乘在一起,將計算的結果返回給用戶。
新建一個Rust項目:
cargonewcompiler-optimizations然后在Cargo.toml文件中寫入如下內容:
[dependencies] anyhow="1.0.71" actix-web="4.3.1" dotenv="0.15.0" serde={version="1.0",features=["derive"]} serde_json="1.0.96" log="0.4.17" env_logger="0.10.0" serde_derive="1.0.163" rand="0.8.5" mimalloc={version="0.1.37",default-features=false} [profile.release] lto=true codegen-units=1 panic="abort" strip=true在src/main.rs中寫入如下代碼:
usestd::env; userand::Rng; useactix_web::{App,get,post,HttpResponse,HttpServer,middleware,web}; useanyhow::Result; useserde::{Deserialize,Serialize}; #[global_allocator] staticGLOBAL:mimalloc::MiMalloc=mimalloc::MiMalloc; #[derive(Debug,Clone,Serialize,Deserialize)] structMessage{ pubmessage:String, } #[derive(Debug,Clone,Serialize,Deserialize)] structMatrixSize{ pubn:usize, } #[derive(Debug,Clone,Serialize,Deserialize)] structMatrixResult{ pubmatrix:Vec>, } #[get("/healthz")] asyncfnhealth()->HttpResponse{ HttpResponse::Ok().json(Message{ message:"healthy".to_string(), }) } asyncfnnot_found()->HttpResponse{ HttpResponse::NotFound().json(Message{ message:"notfound".to_string(), }) } #[post("/matrix-multiplication")] asyncfnmatrix_multiplication(size:web::Json )->HttpResponse{ letn=size.n; letmatrix_a=generate_random_matrix(n); letmatrix_b=generate_random_matrix(n); letresult=multiply_matrices(&matrix_a,&matrix_b); HttpResponse::Ok().json(MatrixResult{matrix:result}) } fngenerate_random_matrix(n:usize)->Vec >{ letmutrng=rand::thread_rng(); (0..n).map(|_|(0..n).map(|_|rng.gen_range(0..nasi32)).collect()).collect() } fnmultiply_matrices(matrix_a:&Vec >,matrix_b:&Vec >)->Vec >{ leta_rows=matrix_a.len(); leta_cols=matrix_a[0].len(); letb_cols=matrix_b[0].len(); letmutresult=vec![vec![0;b_cols];a_rows]; foriin0..a_rows{ forjin0..b_cols{ forkin0..a_cols{ result[i][j]+=matrix_a[i][k]*matrix_b[k][j]; } } } result } #[actix_web::main] asyncfnmain()->Result<()>{ env_logger::new().default_filter_or("info")); letport=env::var("PORT").unwrap_or_else(|_|"8080".to_string()); HttpServer::new(move||{ App::new() .wrap(middleware::default()) .service(health) .service(matrix_multiplication) .default_service(web::route().to(not_found)) }) .bind(format!("0.0.0.0:{}",port))? .run() .await.expect("failedtorunserver"); Ok(()) }
優(yōu)化設置
1,Cargo.toml配置文件配置了-[profile.release]部分,用于調整優(yōu)化性能。我們使用了以下優(yōu)化設置:
lto = true:用于啟用鏈路時間優(yōu)化;
codegen-units = 1:即在整個crate中使用最高級別優(yōu)化;
panic = "abort":發(fā)生panic時調用abort而不是unwind;
strip = true:通過移除debug符號來減小二進制大小。
2,構建標識——通過設置RUSTFLAGS= " -c target-cpu=native ",我們可以確保編譯器根據(jù)機器的特定架構來優(yōu)化構建。
3,備用內存分配器——我們還嘗試了mimalloc內存分配器,對于某些工作負載,它可以提供比默認分配器更好的性能特征。
測試
為了對Actix Web API進行負載測試,我們將使用一個功能強大但輕量級的工具——Drill。
為了模擬高負載,我們的測試參數(shù)將包括兩個場景中的500個并發(fā)請求——一個有10,000次迭代,另一個有20,000次迭代。這實際上分別達到了50,000和100,000個請求。
測試將在各種配置下進行,以獲得全面的性能視圖,如下所列:
1,cargo run :構建一個沒有任何優(yōu)化的開發(fā)版本(標記為“D”)。
2,cargo run --release:構建一個沒有任何優(yōu)化的發(fā)布版本(標記為“R”)。
3,RUSTFLAGS="-C target-cpu=native" cargo run --release:根據(jù)機器的特定架構來優(yōu)化構建一個發(fā)布版本,(標記為“ROpt”)。
4,與上一個命令一樣,但是在代碼中采用了MimAlloc的內存分配器(表示為'ROptMimAlloc')。
結果
|BuildType|TotalTime(s)|Requestspersecond| |---|---|---| |DevBuildUnoptimized50k|71.3|701.45| |ReleaseBuildUnoptimized50k|27.0|1849.95| |ReleaseBuildOptimized(flags)50k|25.8|1937.80| |ReleaseBuildOptimized(flags+mimalloc)50k|26.7|1873.65| |ReleaseBuildUnoptimized100k|52.1|1918.27| |ReleaseBuildOptimized(flags)100k|51.7|1934.59| |ReleaseBuildOptimized(flags+mimalloc)100k|51.1|1955.07|
從50k請求測試開始,未優(yōu)化的開發(fā)構建每秒能夠處理大約701.45個請求,但是當代碼在發(fā)布模式下編譯時,每秒的請求飆升到1849.95個。這展示了Rust編譯器在從開發(fā)模式切換到發(fā)布模式時所產生的顯著差異。
使用針對本機CPU架構的構建標志添加優(yōu)化,進一步提高了性能,達到每秒1937.80個請求。
當我們加入mimalloc(備用內存分配器)時,每秒請求數(shù)略微下降到1873.65。這表明,雖然mimalloc可以提高內存使用效率,但它不一定能在每個場景中都能提高請求處理速度。
轉到100k個請求測試,有趣的是,未優(yōu)化版本和優(yōu)化版本之間的性能差異不那么明顯。未優(yōu)化的版本實現(xiàn)了每秒1918.27個請求,而優(yōu)化的版本(帶和不帶mimalloc)分別達到了每秒1934.59和1955.07個請求。
這表明,當處理大量請求時,我們優(yōu)化的影響變得不那么明顯。盡管如此,即使在更重的負載下,構建優(yōu)化仍然能提供最佳性能。
審核編輯:湯梓紅
-
接口
+關注
關注
33文章
8575瀏覽量
151014 -
應用程序
+關注
關注
37文章
3265瀏覽量
57677 -
編譯器
+關注
關注
1文章
1623瀏覽量
49107 -
Rust
+關注
關注
1文章
228瀏覽量
6599
原文標題:最大化Rust性能:編譯器優(yōu)化的比較分析
文章出處:【微信號:Rust語言中文社區(qū),微信公眾號:Rust語言中文社區(qū)】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論