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

时间:2018-10-22 16:30:33

标签: performance go rust

我有两个(等效于?)程序,一个在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程序,以便更快地执行吗?

2 个答案:

答案 0 :(得分:9)

  

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

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

let mut work: Vec<f64> = vec![0.0, 1.0];    

for _x in 1..100000000 {
    work.swap(0, 1); 
}

那么这段代码更快吗?是。看看what assembly is generated

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

在我的PC上,它的运行速度比原始代码快30倍。

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

但是,可悲的是,没有太多有用的信息可从您的微型基准测试中获得。基准测试很难,就像真的很难。有很多陷阱,甚至专业人士也喜欢。在您的情况下,基准在多种方面存在缺陷。首先,您以后再也不会观察到向量的内容(永远不会使用)。这就是为什么优秀的编译器可以删除甚至触及矢量的所有代码(如Rust编译器所做的那样)。所以那不好。

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

答案 1 :(得分:2)

(不是一个答案),但是为了增加卢卡斯的所作所为, 这是Go 1.11 generates的内容 循环本身:

    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提供)

在任何一种情况下,请注意,很可能测量的时间主要由进程的启动和初始化决定,因此您实际上没有测量循环执行的速度。哎呀,你的方法不正确。