0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

最大化Rust性能:编译器优化的比较分析

jf_wN0SrCdH 来源:coding到灯火阑珊 2023-05-29 15:31 次阅读

Rust以其独特的安全性、速度和并发性组合而迅速流行。但是与其它任何语言一样,要充分利用Rust需要的不仅仅是理解它的语法和习惯用法——还需要深入了解如何有效地利用和优化它的编译器。

为了说明这一点,我们设计了一个实际用例——一个Actix Web应用程序中的矩阵乘法任务。这种cpu密集型操作为分析各种编译器优化提供了一个完美的场景。

随着实验的深入,我们将调整Cargo.toml文件的设置。利用特定的构建标志,甚至交换内存分配器。通过测量每次更改对性能的影响,我们将对Rust的编译器优化有一个全面的了解。

实际用例

我们使用Actix Web开发了一个紧凑的应用程序,具有唯一的路由/matrix-multiplication。这个接口接收一个JSON数据,带有一个属性:n。

在接收到请求后,应用程序立即开始行动,动态地生成两个大小为n x n的矩阵,在矩阵中随机填充一些数据。然后将这些矩阵相乘在一起,将计算的结果返回给用户。

新建一个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(())
}

优化设置

1,Cargo.toml配置文件配置了-[profile.release]部分,用于调整优化性能。我们使用了以下优化设置:

lto = true:用于启用链路时间优化;

codegen-units = 1:即在整个crate中使用最高级别优化;

panic = "abort":发生panic时调用abort而不是unwind;

strip = true:通过移除debug符号来减小二进制大小。

2,构建标识——通过设置RUSTFLAGS= " -c target-cpu=native ",我们可以确保编译器根据机器的特定架构来优化构建。

3,备用内存分配器——我们还尝试了mimalloc内存分配器,对于某些工作负载,它可以提供比默认分配器更好的性能特征。

测试

为了对Actix Web API进行负载测试,我们将使用一个功能强大但轻量级的工具——Drill。

为了模拟高负载,我们的测试参数将包括两个场景中的500个并发请求——一个有10,000次迭代,另一个有20,000次迭代。这实际上分别达到了50,000和100,000个请求。

测试将在各种配置下进行,以获得全面的性能视图,如下所列:

1,cargo run :构建一个没有任何优化的开发版本(标记为“D”)。

2,cargo run --release:构建一个没有任何优化的发布版本(标记为“R”)。

3,RUSTFLAGS="-C target-cpu=native" cargo run --release:根据机器的特定架构来优化构建一个发布版本,(标记为“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|

94020f4c-fdee-11ed-90ce-dac502259ad0.png

941072c6-fdee-11ed-90ce-dac502259ad0.png

从50k请求测试开始,未优化的开发构建每秒能够处理大约701.45个请求,但是当代码在发布模式下编译时,每秒的请求飙升到1849.95个。这展示了Rust编译器在从开发模式切换到发布模式时所产生的显著差异。

使用针对本机CPU架构的构建标志添加优化,进一步提高了性能,达到每秒1937.80个请求。

当我们加入mimalloc(备用内存分配器)时,每秒请求数略微下降到1873.65。这表明,虽然mimalloc可以提高内存使用效率,但它不一定能在每个场景中都能提高请求处理速度。

转到100k个请求测试,有趣的是,未优化版本和优化版本之间的性能差异不那么明显。未优化的版本实现了每秒1918.27个请求,而优化的版本(带和不带mimalloc)分别达到了每秒1934.59和1955.07个请求。

这表明,当处理大量请求时,我们优化的影响变得不那么明显。尽管如此,即使在更重的负载下,构建优化仍然能提供最佳性能。





原作者:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 分配器
    +关注

    关注

    0

    文章

    193

    浏览量

    25684
  • JSON
    +关注

    关注

    0

    文章

    116

    浏览量

    6927
  • rust语言
    +关注

    关注

    0

    文章

    57

    浏览量

    2997
收藏 人收藏

    评论

    相关推荐

    使用Rust优化Python性能

    在数据分析领域Python无疑是最流行的编程语言,但是Python有一个硬伤就是作为一个编译语言在性能上有些微的欠缺。而同样最流行的语言Rust则在
    的头像 发表于 11-01 15:59 811次阅读
    使用<b class='flag-5'>Rust</b><b class='flag-5'>优化</b>Python<b class='flag-5'>性能</b>

    性能最大化Δ-Σ 转换

    时钟和PGA 的调整,相同数据速率在性能方面会 有所不同。在优化数据转换结果时,对于这些方方面面做到完全了解并非易事。另外一些问题还包 括输入阻抗、滤波响应、抗混叠,以及长期漂移。性能
    发表于 10-21 11:24

    [转]LabVIEW实现窗口最大化和最小化

    分享VI程序 代码名称:LabVIEW实现窗口最大化和最小化 适用平台:LabVIEW8.2.x LabVIEW8.5代码作者:LaRisa_S 版权所有:LaRisa_S 原创/转载:转载代码
    发表于 03-08 14:56

    如何使应用程序开机运行最大化

    我现在生成一个应用程序并开机启动了,但是如何啊能让他启动是最大化显示呢现在一开机启动不是最大化啊。。。。。。很苦恼啊,希望大家指点
    发表于 05-11 20:57

    如何实现能源效率最大化

    如何设计智能燃气表和水表实现能源效率最大化
    发表于 05-13 07:18

    怎样去实现OTDR/iOLM长期性能最大化

    怎样去实现OTDR/iOLM长期性能最大化
    发表于 05-24 07:15

    SIMD计算机的优化编译器设计

    利用处理的相关资源,提高编译器优化性能和增强代码可适应性是SIMD处理优化编译的关键。该文基
    发表于 04-03 08:47 30次下载

    最大化自动化测试系统的精度

    最大化自动化测试系统的精度 引言 在设计自动化测试系统时,精度的最大化通常是关键的考虑因素。确定如何最大化精度总是很困难
    发表于 06-13 15:02 704次阅读
    <b class='flag-5'>最大化</b>自动化测试系统的精度

    编译器_keil的优化选项问题

    keil编译器优化选项针对ARM,对STM32编译的一些优化的问题
    发表于 02-25 14:18 3次下载

    编译器优化对函数的影响

    编译器如gcc,可以指定不同的优化参数,在某些条件下,有些函数可能会被优化掉。
    的头像 发表于 06-22 14:58 2780次阅读
    <b class='flag-5'>编译器</b><b class='flag-5'>优化</b>对函数的影响

    基于互信息最大化的Raptor码优化设计方法

    基于互信息最大化的Raptor码优化设计方法
    发表于 07-02 11:47 8次下载

    最大化Rust性能编译器优化比较分析

    Rust以其独特的安全性、速度和并发性组合而迅速流行。但是与其它任何语言一样,要充分利用Rust需要的不仅仅是理解它的语法和习惯用法——还需要深入了解如何有效地利用和优化它的编译器
    的头像 发表于 05-29 16:17 1894次阅读
    <b class='flag-5'>最大化</b><b class='flag-5'>Rust</b><b class='flag-5'>性能</b>:<b class='flag-5'>编译器</b><b class='flag-5'>优化</b>的<b class='flag-5'>比较</b><b class='flag-5'>分析</b>

    编译器优化选项

    一个程序首先要保证正确性,在保证正确性的基础上,性能也是一个重要的考量。要编写高性能的程序,第一,必须选择合适的算法和数据结构;第二,应该编写编译器能够有效优化以转换成高效可执行代码的
    的头像 发表于 11-24 15:37 828次阅读
    <b class='flag-5'>编译器</b>的<b class='flag-5'>优化</b>选项

    TVM编译器的整体架构和基本方法

    。但是这其中也去思考了一下基于FPGA加速编译器架构。在FPGA深度学习加速中,编译器除了需要自动化生成指令外,还要优化指令的结构,来
    的头像 发表于 11-30 09:36 2167次阅读
    TVM<b class='flag-5'>编译器</b>的整体架构和基本方法

    Keil编译器优化方法

    我们都知道,代码是可以通过编译器优化的,有的时候,为了提高运行速度或者减少代码尺寸,会开启优化选项。
    的头像 发表于 10-23 16:35 172次阅读
    Keil<b class='flag-5'>编译器</b><b class='flag-5'>优化</b>方法