Rust以其獨(dú)特的安全性、速度和并發(fā)性組合而迅速流行。但是與其它任何語(yǔ)言一樣,要充分利用Rust需要的不僅僅是理解它的語(yǔ)法和習(xí)慣用法——還需要深入了解如何有效地利用和優(yōu)化它的編譯器。
為了說(shuō)明這一點(diǎn),我們?cè)O(shè)計(jì)了一個(gè)實(shí)際用例——一個(gè)Actix Web應(yīng)用程序中的矩陣乘法任務(wù)。這種cpu密集型操作為分析各種編譯器優(yōu)化提供了一個(gè)完美的場(chǎng)景。
隨著實(shí)驗(yàn)的深入,我們將調(diào)整Cargo.toml文件的設(shè)置。利用特定的構(gòu)建標(biāo)志,甚至交換內(nèi)存分配器。通過(guò)測(cè)量每次更改對(duì)性能的影響,我們將對(duì)Rust的編譯器優(yōu)化有一個(gè)全面的了解。
實(shí)際用例
我們使用Actix Web開(kāi)發(fā)了一個(gè)緊湊的應(yīng)用程序,具有唯一的路由/matrix-multiplication。這個(gè)接口接收一個(gè)JSON數(shù)據(jù),帶有一個(gè)屬性:n。
在接收到請(qǐng)求后,應(yīng)用程序立即開(kāi)始行動(dòng),動(dòng)態(tài)地生成兩個(gè)大小為n x n的矩陣,在矩陣中隨機(jī)填充一些數(shù)據(jù)。然后將這些矩陣相乘在一起,將計(jì)算的結(jié)果返回給用戶。
新建一個(gè)Rust項(xiàng)目:
cargonewcompiler-optimizations然后在Cargo.toml文件中寫入如下內(nèi)容:
[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)化設(shè)置
1,Cargo.toml配置文件配置了-[profile.release]部分,用于調(diào)整優(yōu)化性能。我們使用了以下優(yōu)化設(shè)置:
lto = true:用于啟用鏈路時(shí)間優(yōu)化;
codegen-units = 1:即在整個(gè)crate中使用最高級(jí)別優(yōu)化;
panic = "abort":發(fā)生panic時(shí)調(diào)用abort而不是unwind;
strip = true:通過(guò)移除debug符號(hào)來(lái)減小二進(jìn)制大小。
2,構(gòu)建標(biāo)識(shí)——通過(guò)設(shè)置RUSTFLAGS= " -c target-cpu=native ",我們可以確保編譯器根據(jù)機(jī)器的特定架構(gòu)來(lái)優(yōu)化構(gòu)建。
3,備用內(nèi)存分配器——我們還嘗試了mimalloc內(nèi)存分配器,對(duì)于某些工作負(fù)載,它可以提供比默認(rèn)分配器更好的性能特征。
測(cè)試
為了對(duì)Actix Web API進(jìn)行負(fù)載測(cè)試,我們將使用一個(gè)功能強(qiáng)大但輕量級(jí)的工具——Drill。
為了模擬高負(fù)載,我們的測(cè)試參數(shù)將包括兩個(gè)場(chǎng)景中的500個(gè)并發(fā)請(qǐng)求——一個(gè)有10,000次迭代,另一個(gè)有20,000次迭代。這實(shí)際上分別達(dá)到了50,000和100,000個(gè)請(qǐng)求。
測(cè)試將在各種配置下進(jìn)行,以獲得全面的性能視圖,如下所列:
1,cargo run :構(gòu)建一個(gè)沒(méi)有任何優(yōu)化的開(kāi)發(fā)版本(標(biāo)記為“D”)。
2,cargo run --release:構(gòu)建一個(gè)沒(méi)有任何優(yōu)化的發(fā)布版本(標(biāo)記為“R”)。
3,RUSTFLAGS="-C target-cpu=native" cargo run --release:根據(jù)機(jī)器的特定架構(gòu)來(lái)優(yōu)化構(gòu)建一個(gè)發(fā)布版本,(標(biāo)記為“ROpt”)。
4,與上一個(gè)命令一樣,但是在代碼中采用了MimAlloc的內(nèi)存分配器(表示為'ROptMimAlloc')。
結(jié)果
|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請(qǐng)求測(cè)試開(kāi)始,未優(yōu)化的開(kāi)發(fā)構(gòu)建每秒能夠處理大約701.45個(gè)請(qǐng)求,但是當(dāng)代碼在發(fā)布模式下編譯時(shí),每秒的請(qǐng)求飆升到1849.95個(gè)。這展示了Rust編譯器在從開(kāi)發(fā)模式切換到發(fā)布模式時(shí)所產(chǎn)生的顯著差異。
使用針對(duì)本機(jī)CPU架構(gòu)的構(gòu)建標(biāo)志添加優(yōu)化,進(jìn)一步提高了性能,達(dá)到每秒1937.80個(gè)請(qǐng)求。
當(dāng)我們加入mimalloc(備用內(nèi)存分配器)時(shí),每秒請(qǐng)求數(shù)略微下降到1873.65。這表明,雖然mimalloc可以提高內(nèi)存使用效率,但它不一定能在每個(gè)場(chǎng)景中都能提高請(qǐng)求處理速度。
轉(zhuǎn)到100k個(gè)請(qǐng)求測(cè)試,有趣的是,未優(yōu)化版本和優(yōu)化版本之間的性能差異不那么明顯。未優(yōu)化的版本實(shí)現(xiàn)了每秒1918.27個(gè)請(qǐng)求,而優(yōu)化的版本(帶和不帶mimalloc)分別達(dá)到了每秒1934.59和1955.07個(gè)請(qǐng)求。
這表明,當(dāng)處理大量請(qǐng)求時(shí),我們優(yōu)化的影響變得不那么明顯。盡管如此,即使在更重的負(fù)載下,構(gòu)建優(yōu)化仍然能提供最佳性能。
原作者:劉清
-
分配器
+關(guān)注
關(guān)注
0文章
193瀏覽量
25747 -
JSON
+關(guān)注
關(guān)注
0文章
117瀏覽量
6963 -
rust語(yǔ)言
+關(guān)注
關(guān)注
0文章
57瀏覽量
3009
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論