猿问

为什么在 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 程序可以用惯用的方式编写,以更快地执行吗?


胡说叔叔
浏览 94回答 2
2回答

慕娘9325324

Rust 程序可以用惯用的方式编写,以更快地执行吗?是的。要创建包含几个元素的向量,请使用vec![]宏:let mut work: Vec<f64> = vec![0.0, 1.0];&nbsp; &nbsp;&nbsp;for _x in 1..100000000 {&nbsp; &nbsp; work.swap(0, 1);&nbsp;}那么这段代码更快吗?是的。查看生成的程序集:example::main:&nbsp; mov eax, 99999999.LBB0_1:&nbsp; add eax, -11&nbsp; jne .LBB0_1&nbsp; ret在我的 PC 上,它的运行速度比您的原始代码快 30 倍。为什么程序集仍然包含这个什么都不做的循环?为什么编译器无法看到两个pushes 与 相同vec![0.0, 1.0]?这两个问题都很好,而且都可能指向 LLVM 或 Rust 编译器中的缺陷。然而,遗憾的是,从您的微基准测试中没有太多有用的信息。基准测试很难,真的很难。有太多的陷阱,连专业人士都会掉进去。就您而言,基准测试在几个方面存在缺陷。首先,您以后永远不会观察向量的内容(它从未被使用过)。这就是为什么一个好的编译器可以删除所有甚至触及向量的代码(就像上面的 Rust 编译器所做的那样)。所以这不好。除此之外,这与任何真正的性能关键代码都不相似。即使稍后会观察到向量,奇数次交换也等于单次交换。所以除非你想看看优化器是否能理解这个交换规则,否则遗憾的是你的基准测试并不是很有用。

一只名叫tom的猫

(不是答案)但为了扩充 Lukas 所写的内容,以下是 Go 1.11 为循环本身生成的内容:&nbsp; &nbsp; xorl&nbsp; &nbsp; CX, CX&nbsp; &nbsp; movsd&nbsp; &nbsp;8(AX), X0&nbsp; &nbsp; movsd&nbsp; &nbsp;(AX), X1&nbsp; &nbsp; movsd&nbsp; &nbsp;X0, (AX)&nbsp; &nbsp; movsd&nbsp; &nbsp;X1, 8(AX)&nbsp; &nbsp; incq&nbsp; &nbsp; CX&nbsp; &nbsp; cmpq&nbsp; &nbsp; CX, $100000000&nbsp; &nbsp; jlt&nbsp; &nbsp; &nbsp;68(由https://godbolt.org提供)在任何一种情况下,请注意,您测量的时间很可能主要由进程的启动和初始化决定,因此您实际上并未测量循环执行的速度。IOW 你的方法是不正确的。
随时随地看视频慕课网APP

相关分类

Go
我要回答