为什么我的Rust程序比执行相同的按位和I / O操作的Go程序慢4倍?

时间:2016-03-20 23:14:23

标签: go rust

我有一个Rust程序,它对64位无符号整数实现强制奇偶校验:

use std::io;
use std::io::BufRead;

fn parity(mut num: u64) -> u8 {
    let mut result: u8 = 0;
    while num > 0 {
        result = result ^ (num & 1) as u8;
        num = num >> 1;
    }
    result
}

fn main() {
    let stdin = io::stdin();
    let mut num: u64;
    let mut it = stdin.lock().lines();
    // skip 1st line with number of test cases
    it.next();
    for line in it {
        num = line.unwrap().parse().unwrap();
        println!("{}", parity(num));
    }
}

当我用包含1000000个无符号整数的输入文件提供它时:

$ rustc parity.rs
$ time cat input.txt | ./parity &> /dev/null
cat input.txt  0.00s user 0.02s system 0% cpu 4.178 total
./parity &> /dev/null  3.87s user 0.32s system 99% cpu 4.195 total

这是一个惊喜 - Go中有效的相同程序速度提高了4倍:

$ go build parity.go
$ time cat input.txt | ./parity &> /dev/null
cat input.txt  0.00s user 0.03s system 3% cpu 0.952 total
./parity &> /dev/null  0.63s user 0.32s system 99% cpu 0.955 total

这是Go中的代码:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

func parity(line string) uint64 {
    var parity uint64
    u, err := strconv.ParseUint(line, 10, 64)
    if err != nil {
        panic(err)
    }
    for u > 0 {
        parity ^= u & 1
        u >>= 1
    }
    return parity
}

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    // skip line with number of cases
    if !scanner.Scan() {
        // panic if there's no number of test cases
        panic("missing number of test cases")
    }
    for scanner.Scan() {
        fmt.Println(parity(scanner.Text()))
    }
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading standard input:", err)
    }
}

版本:

$ rustc --version
rustc 1.7.0
$ go version
go version go1.6 darwin/amd64

输入文件样本,第一行包含文件中的输入值数:

8
7727369244898783789
2444477357490019411
4038350233697550492
8106226119927945594
1538904728446207070
0
1
18446744073709551615

为什么我写的Rust and Go程序在性能上有如此显着的差异?在这种情况下,我预计Rust会比Go快一点。我在Rust代码中做错了吗?

2 个答案:

答案 0 :(得分:4)

我认为您没有使用优化进行编译。 试试

$ rustc -O parity.rs

答案 1 :(得分:0)

您的基准测试不会测量奇偶校验。它测量输入加奇偶校验加输出。例如,在Go中,您可以衡量scanner.Scanstrconv.ParseUint以及fmt.Println以及奇偶校验。

这是一个只测量1000000奇偶校验的Go基准。

parity_test.go

package parity

import (
    "math/rand"
    "runtime"
    "testing"
)

func parity(n uint64) uint64 {
    var parity uint64
    for n > 0 {
        parity ^= n & 1
        n >>= 1
    }
    return parity
}

func init() { runtime.GOMAXPROCS(1) }

// Benchmark 1000000 parity checks.
func BenchmarkParity1000000(b *testing.B) {
    n := make([]uint64, 1000000)
    for i := range n {
        r := uint64(rand.Uint32())
        n[i] = (r << 32) | r
    }
    p := parity(42)
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        for _, n := range n {
            p = parity(n)
        }
    }
    b.StopTimer()
    _ = p
}

输出:

$ go test -bench=.
BenchmarkParity1000000        50      34586769 ns/op
$