自定义程序
1 subscriber
73 photos
15 videos
5 files
252 links
自定义程序丰富生活
Download Telegram
Forwarded from Rust 视界
「SO问答」对超过240个元素的数组进行循环时,为什么会有很大的性能影响?

#stackoverflow

问题:

> 下面代码当 CAPACITY >= 240 的时候,与 CAPACITY >= 239 相比,性能慢了80倍。Rust编译器专门为240以内的长度做了优化? 使用 rustc -C opt-level=3 进行编译。

rust
use std::time::Instant;

const CAPACITY: usize = 240;
const IN_LOOPS: usize = 500000;

fn main() {
let mut arr = [0; CAPACITY];
for i in 0..CAPACITY {
arr[i] = i;
}
let mut sum = 0;
let now = Instant::now();
for _ in 0..IN_LOOPS {
let mut s = 0;
for i in 0..arr.len() {
s += arr[i];
}
sum += s;
}
println!("sum:{} time:{:?}", sum, now.elapsed());
}


解答:

> 总结:低于240,LLVM完全展开内部循环,可以优化掉重复循环,增加性能。

分析:

> 这是一个神奇的阈值,超过该阈值LLVM将停止执行某些优化。阈值是 8字节* 240 = 1920字节 (数组是usizes数组,因此长度乘以8字节,假设 x86-64 CPU)。在该问题中的基准测试中,是仅针对长度239执行的一个特定优化,所以导致了巨大的性能差异。

比如这段代码:

rust
pub fn foo() -> usize {
let arr = [0; 240];
let mut s = 0;
for i in 0..arr.len() {
s += arr[i];
}
s
}


你在 [godbolt](https://rust.godbolt.org/z/VKL9MS) 编辑器中查看生成的汇编代码,比较240和239,会发现有很大区别。比如当239的时候生成:

rust
movdqa xmm1, xmmword ptr [rsp + 32]
movdqa xmm0, xmmword ptr [rsp + 48]
paddq xmm1, xmmword ptr [rsp]
paddq xmm0, xmmword ptr [rsp + 16]
paddq xmm1, xmmword ptr [rsp + 64]
; more stuff omitted here ...
paddq xmm0, xmmword ptr [rsp + 1840]
paddq xmm1, xmmword ptr [rsp + 1856]
paddq xmm0, xmmword ptr [rsp + 1872]
paddq xmm0, xmm1
pshufd xmm1, xmm0, 78
paddq xmm1, xmm0


就是所谓的循环展开: LLVM将循环体粘贴一段时间,以避免执行那些“循环管理指令”,即循环变量的增量,检查循环是否结束和跳转。(可以自行对比一下240的输出)。但是,即便循环不展开,也不会造成80倍的性能差异。所以,实际上那个性能测试代码嵌套循环导致的(LLVM生成的代码基本上首先只执行内部循环(计算总和),然后通过多次累加总和来模拟外部循环!)。最好要使用Rust的惯用法: `arr.iter().sum()`,这样就不会产生80倍的性能差异了。

Read More: [https://stackoverflow.com/questions/57458460/why-is-there-a-large-performance-impact-when-looping-over-an-array-over-240-elem](https://stackoverflow.com/questions/57458460/why-is-there-a-large-performance-impact-when-looping-over-an-array-over-240-elem)