首页 > 其他分享 >【Rust自学】13.10. 性能对比:循环 vs. 迭代器

【Rust自学】13.10. 性能对比:循环 vs. 迭代器

时间:2025-01-23 16:28:19浏览次数:3  
标签:开销 13.10 迭代 buffer 代码 编译器 vs Rust

13.10.0. 写在正文之前

Rust语言在设计过程中收到了很多语言的启发,而函数式编程对Rust产生了非常显著的影响。函数式编程通常包括通过将函数作为值传递给参数、从其他函数返回它们、将它们分配给变量以供以后执行等等。

在本章中,我们会讨论 Rust 的一些特性,这些特性与许多语言中通常称为函数式的特性相似:

  • 闭包
  • 迭代器
  • 使用闭包和迭代器改进I/O项目
  • 闭包和迭代器的性能(本文)

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
请添加图片描述

13.10.1. 一个测试

将阿瑟·柯南道尔爵士所著的《夏洛克·福尔摩斯历险记》的全部内容加载到String中并在内容中查找单词the来运行基准测试。以下是使用for循环的search版本和使用迭代器的版本的基准测试结果:

test bench_search_for  ... bench:  19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench:  19,234,900 ns/iter (+/- 657,200)

迭代器版本稍微快一些!

我们不会在这里解释基准测试代码,因为重点不是证明这两个版本是等效的,而是为了大致了解这两个实现在性能方面的比较。

迭代器是Rust中的一种高层次的抽象,它在编译后生成的代码和我们手写的底层代码是几乎一样的产物。这套东西叫做零开销抽象(Zero-Cost Abstraction)

13.10.2. 零开销抽象(Zero-Cost Abstraction)

零开销抽象确保程序员使用抽象时不会引入运行时额外的开销。

Rust的迭代器能实现零开销抽象是因为:

1. 泛型与单态化

Rust的迭代器大量使用泛型(generics)来定义操作,比如Iterator trait。编译器在编译期间会对每个具体的类型实例化泛型代码,生成专门针对这些类型的高效机器代码,这个过程叫单态化(monomorphization)

  • 静态分发:编译器根据具体的类型生成直接调用的代码,不需要在运行时查找函数地址(如动态分发中的虚表)。

  • 优化机会:由于类型在编译期是已知的,编译器可以对代码进行深入优化,比如消除函数调用的开销和内联。

2. 内联与LLVM优化

Rust 使用 LLVM 作为其后端编译器。编译器可以通过以下方式优化迭代器链:

  • 函数内联:Rust 编译器会内联迭代器中的操作(如map、filter等),将这些方法展开为一段紧凑的代码,而不会有函数调用开销。

  • 循环展开与合并:多个迭代器方法的调用(如map().filter().collect())在编译时会被合并为单个循环。

  • 冗余消除:例如,对于一些多余的中间变量或操作,编译器会直接去掉。

结果是,迭代器链的最终执行代码效率几乎与手写的循环相当。

3. 惰性求值

Rust的迭代器是惰性求值的。这意味着:

  • 在调用终结方法(如collect()for_each())之前,迭代器不会执行任何实际操作。

  • 每个中间操作(如mapfilter)仅生成一个新迭代器,并不会立即应用操作。

这种惰性设计允许编译器在最终使用迭代器时直接生成针对具体场景优化的代码,而不会引入不必要的中间数据结构或计算。

4. 无运行时开销

Rust的设计原则之一是避免运行时成本。迭代器的实现避免了动态分配和运行时多态:

  • Rust迭代器是基于静态类型的,通常无需堆分配(除非显式使用Boxdyn Iterator)。

  • Iterator trait使用静态分发,避免了动态分发(即使需要动态分发,也需显式声明为dyn Iterator)。

5. 没有额外的抽象成本

Rust迭代器通过直接对底层数据结构的操作提供功能,而不会引入额外的抽象层。比如:

  • 调用.iter()生成的迭代器直接操作底层切片或集合,开销极低。

  • 中间迭代器(如Map、Filter)在编译时会被优化成一段紧凑的指令,而不会引入多余的封装。

13.10.3. 一个例子:音频解码程序

以下代码取自音频解码器。这 解码算法使用线性预测数学运算 根据先前样本的线性函数估计未来值。这 代码使用迭代器链对范围内的三个变量进行一些数学运算: 数据的buffer切片、12 个coefficients的数组以及qlp_shift中数据移位的量。我们在这个例子中声明了变量,但没有给它们任何值;尽管这段代码在其上下文之外没有太多意义,但它仍然是 Rust 如何将高级思想转化为低级代码的简洁、真实的示例。

let buffer: &mut [i32];
let coefficients: [i64; 12];
let qlp_shift: i16;

for i in 12..buffer.len() {
    let prediction = coefficients.iter()
                                 .zip(&buffer[i - 12..i])
                                 .map(|(&c, &s)| c * s as i64)
                                 .sum::<i64>() >> qlp_shift;
    let delta = buffer[i];
    buffer[i] = prediction as i32 + delta;
}

为了计算prediction值,此代码迭代了coefficients中的12个值中的每一个,并使用zip方法将系数值与buffer中的前12个值配对。然后,对于每一对,我们将这些值相乘,对所有结果求和,并将总和qlp_shift位中的位向右移动。

所有系数都存储在寄存器中,这意味着访问这些值非常快。运行时对数组访问没有边界检查。 Rust 能够应用的所有这些优化使生成的代码极其高效。现在您知道了这一点,您可以毫无恐惧地使用迭代器和闭包!它们使代码看起来更高级别,但不会因此而造成运行时性能损失。

标签:开销,13.10,迭代,buffer,代码,编译器,vs,Rust
From: https://blog.csdn.net/weixin_71793197/article/details/145251590

相关文章

  • Rust能否通过一个对象实例的引用clone这个对象?
    eeeeeeeeeeeeeeeeeeeeeeeeeee在Rust中,通过对象实例的引用克隆对象是完全可以的,但前提是对象的类型必须实现了Clonetrait。以下是具体示例:#[derive(Clone,Debug)]structMyStruct{value:i32,}fnmain(){letobj=MyStruct{value:42};letobj_re......
  • 折腾笔记[11]-使用rust进行直接法视觉里程计估计
    摘要使用rust实现了一个完整的直接法视觉里程计系统,能够通过比较两幅图像中的像素强度来估计相机的运动。它通过单层和多层的优化策略,结合图像金字塔和并行计算,提高了位姿估计的精度和效率。最终,代码输出了优化后的相机位姿变换矩阵,并可视化了投影点的位置。Thisisacomplete......
  • 2025年Rust权威指南、Rust程序设计语言中文版PDF下载
    2025年《Rust权威指南》、《Rust程序设计语言》中文版PDF下载本书英文名为《TheRustProgrammingLanguage》,中文版名为《Rust权威指南》,社区翻译版名为《Rust程序设计语言》。本书由Rust核心开发团队编写而成,由浅入深地探讨了Rust语言的方方面面。从学习函数、选择数据结......
  • Rust代码中有一个Vec集合对象,此集合中存放了一些结构体实例,这个Vec集合对象的生命周期
    eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee当你需要长生命周期的Vec并想在多个地方引用它内部的结构体实例时,有几种方法可以实现这种设计。以下是不同场景的解决方案:方法1:使用索引引用Vec元素如果Vec本身的生命周期长,而你只是需要访问其元素,可以通过索引......
  • 在Rust中大范围使用引用会导致生命周期泛滥, 但是使用结构体clone也会导致内存的频繁分
    eeeeeeeeeeeeeeeeeeeeee在Rust中,引用和克隆各有优缺点,你的选择需要基于实际应用场景的需求进行权衡。以下是一些分析和建议:使用引用的优缺点优点:性能更优:避免了内存的分配和拷贝操作。更适合读密集场景:如果数据只需要被读取而不需要修改,引用非常高效。显式的生命周期管......
  • 企业级调度器LVS
    1.集群和分布式系统性能扩展方式:ScaleUP:垂直扩展,向上扩展,增强,性能更强的计算机运行同样的服务ScaleOut:水平扩展,向外扩展,增加设备,并行地运行多个服务调度分配问题,Cluster垂直扩展不再提及:随着计算机性能的增长,其价格会成倍增长单台计算机的性能是有上限的,不可能无限制......
  • 【渗透测试】漏洞扫描AWVS安装使用教程,三分钟手把手教会,非常简单
    一、AWS简介AcunetixWebVulnerabilityScanner(简称AWVS)是一个自动化的Web漏洞扫描工具,它可以扫描任何通过Web浏览器访问和遵循HITP/HTTPS规则的Web站点。AWVS原理是基于漏洞匹配方法,通过网络爬虫测试你的网站安全,检测流行安全AWVS可以检测什么漏洞,它有什么优势?AWVS......
  • Android图形层垂直同步虚拟VSYNC机制
    简介某次调图形性能的时候(启动后台录屏,下(或)称case)发现AndroidSurfaceFlingerVsync机制并没有以前想的这么简单粗糙,特别是这次调图形性能发现一些跟Vsync有关联,因此做个总结详解。跟不上旋律节奏的VSYNC一份追踪报告,发现Vsync信号非常不规律,于是从这里入手分析、总结Vsync。......
  • VSCode使用之go语言配置
    时间:2025/1/22扩展:go目的:支持go语言,方便安装其他必备插件安装该扩展包后可以执行该扩展包提供的命令Go:Install/UpdateTools来进一步扩展go工具执行命令的窗口可以通过Ctrol+Shift+P调出点击后会出现很多选项,可以根据自己需要勾选然后点击确定,等待下载安装,一般情况下......
  • 折腾笔记[10]-使用rust进行ORB角点检测
    摘要打包ORB算法到bye_orb_rs库,使用rust进行ORB角点检测.PackagetheORBalgorithmintothebye_orb_rslibrary,anduseRustforORBcornerdetection.关键词rust;ORB;FAST;slam;关键信息项目地址:[https://github.com/ByeIO/slambook2.rs][package]name="exp65-......