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

最大化Rust性能:編譯器優(yōu)化的比較分析
評論