【问题标题】:Why is my Rust program that performs array reversal slower than the equivalent Go program? [duplicate]为什么我执行数组反转的 Rust 程序比等效的 Go 程序慢? [复制]
【发布时间】:2018-12-13 21:15:44
【问题描述】:

我正在初始化一个数组,然后多次反转它以查看性能。

我想了解我是否编写了无法比较的代码,还是 Rust 真的太糟糕以至于花费了这么多时间?

下面是 Rust 的构建和计时过程:

rustc main.rs
time ./main

这会一直运行下去。令人惊讶

fn reverse(mylist: &mut Vec<u16>) {
    let length = mylist.len();

    let mid_length = length / 2;

    for number in 0..mid_length {
        let mut a = mylist[number];
        let mut b = mylist[length - number - 1];

        mylist[number] = b;

        mylist[length - number - 1] = a;
    }
}

fn main() {
    let array_size = 100000;

    let iterations = 100000;

    let mut v = vec![0u16; array_size];

    for _ in 0..iterations {
        reverse(&mut v);
    }
}

Go 代码与上面的 Rust 代码完全一样。需要注意的重要一点是,Go 有垃圾收集,而 Rust 没有。令人惊讶的是,Go 在不到 6 秒的时间内完成了这项工作:

go build main.go
time ./main 100000 100000

real    0m5.932s
user    0m5.928s
sys 0m0.004s

package main

import (
    "os"
    "strconv"
)

func reverse(mylist []int) []int {

    length := len(mylist)
    half := int(length / 2)

    for i := 0; i < half; i++ {

        mylist[i], mylist[length-i-1] = mylist[length-i-1], mylist[i]

    }   

    return mylist

}

func main() {

    array_size, _ := strconv.Atoi(os.Args[1])
    iterations, _ := strconv.Atoi(os.Args[2])

    mylist := make([]int, array_size)

    for i := 0; i < iterations; i++ {
        reverse(mylist)

    }

}

【问题讨论】:

  • 为什么要重新发明轮子? Vec::reverse.
  • 我能想到的第一个问题是,据我所知,默认情况下rustc 在调试模式下编译。使用-O 标志重复实验(用于优化)。
  • @ljedrz:基本算法类似(参见doc.rust-lang.org/1.7.0/src/core/slice.rs.html#432),它只是使用unsafe 来避免边界检查。
  • @MatthieuM。无论如何你是对的 - 它运行得非常快,优化了。
  • @ljedrz:有多快?如果优化器足够聪明,这应该是一个空操作。去拿 6 秒的空操作速度太慢了。

标签: go rust


【解决方案1】:

默认情况下,rustc(和cargo)在调试模式下编译。它被认为是最有用的默认值,因为它在开发过程中最常编译。

调试模式,尤其是在您的代码中,包括大量检查:对mylist[..] 的每次访问都受到边界检查等的保护。

相反,出于基准测试的目的,您希望通过优化进行编译。如果直接使用rustc,这就像传递-O 标志一样简单。


正如@ljerdz 所指出的,有一个[T]::reverse 方法,它比您的实现更有效,因为它在内部使用unsafe 来省略边界检查。

但是,在操场上进行快速检查并没有发现您的 reverseVec::reverse 之间的任何奇怪差异;优化器已经足够聪明,可以忽略边界检查。

不幸的是,它没有足够聪明地省略循环,可能是因为它没有意识到 __rust_alloc_zeroed__rust_dealloc 是内存分配/释放例程并且没有可观察到的副作用。

【讨论】:

  • 我测试了@ljerdz 提到的 Vec::reverse,性能提高了 2 倍。但正如我所说,意图是使用基本面测试性能。你的 -O 建议是正确的
  • @Tahseen Vec 不是一块生锈的基础砖。像你一样使用它会有开销。
猜你喜欢
  • 2014-10-05
  • 2014-12-21
  • 1970-01-01
  • 1970-01-01
  • 2011-03-03
  • 2018-05-27
相关资源
最近更新 更多