为什么在 Go 中交换 []float64 的元素比在 Rust 中交换 Vec 的元素更快<f64>?



我有两个(等效的?(程序,一个在Go中,另一个在Rust中。平均执行时间为:

  • 去 ~169ms
  • 生锈 ~201ms

package main
import (
"fmt"
"time"
)
func main() {
work := []float64{0.00, 1.00}
start := time.Now()
for i := 0; i < 100000000; i++ {
work[0], work[1] = work[1], work[0]
}
elapsed := time.Since(start)
fmt.Println("Execution time: ", elapsed)
}

我用--release编译

use std::time::Instant;
fn main() {
let mut work: Vec<f64> = Vec::new();
work.push(0.00);
work.push(1.00);
let now = Instant::now();
for _x in 1..100000000 {
work.swap(0, 1); 
}
let elapsed = now.elapsed();
println!("Execution time: {:?}", elapsed);
}

在这种情况下,Rust 的性能是否不如 Go? Rust 程序是否可以以惯用的方式编写,以更快地执行?

Rust 程序可以用惯用的方式编写,以便更快地执行吗?

是的。要创建包含几个元素的矢量,请使用vec![]宏:

let mut work: Vec<f64> = vec![0.0, 1.0];    
for _x in 1..100000000 {
work.swap(0, 1); 
}

那么这段代码更快吗?是的。看看生成了什么程序集:

example::main:
mov eax, 99999999
.LBB0_1:
add eax, -11
jne .LBB0_1
ret

在我的电脑上,它的运行速度比原始代码快 30 倍左右。

为什么程序集仍然包含这个不执行任何操作的循环?为什么编译器无法看到两个pushvec![0.0, 1.0]相同?这两个非常好的问题都可能指向 LLVM 或 Rust 编译器中的缺陷。

然而,可悲的是,从您的微观基准中获得的有用信息并不多。基准测试很难,就像真的很难一样。即使是专业人士也会陷入如此多的陷阱。在您的情况下,基准在几个方面存在缺陷。首先,您以后永远不会观察向量的内容(它永远不会被使用(。这就是为什么一个好的编译器可以删除所有甚至接触向量的代码(就像上面的 Rust 编译器所做的那样(。所以这不好。

除此之外,这不像任何真正的性能关键代码。即使稍后会观察到向量,交换奇数次也等于一次交换。因此,除非您想查看优化器是否可以理解此交换规则,否则可悲的是,您的基准测试并没有真正有用。

(不是答案(,但为了补充卢卡斯所写的内容, 这是 Go 1.11 生成的内容 对于循环本身:

xorl    CX, CX
movsd   8(AX), X0
movsd   (AX), X1
movsd   X0, (AX)
movsd   X1, 8(AX)
incq    CX
cmpq    CX, $100000000
jlt     68

(图片由 https://godbolt.org 提供(

无论哪种情况,请注意,您测量的时间很可能是由进程的启动和初始化决定的,因此您实际上并没有测量循环的执行速度。IOW你的方法不正确。

相关内容

  • 没有找到相关文章

最新更新