This question already has an answer here:
I have a Rust program that implements a brute-force parity check for 64-bit unsigned integers:
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));
}
}
When I feed it with input file containing 1000000 unsigned integers:
$ 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
And here comes a surprise - the effectively same program in Go does 4x faster:
$ 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
Here's the code in 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)
}
}
Versions:
$ rustc --version
rustc 1.7.0
$ go version
go version go1.6 darwin/amd64
Sample of input file, first line contains number of input values in the file:
8
7727369244898783789
2444477357490019411
4038350233697550492
8106226119927945594
1538904728446207070
0
1
18446744073709551615
Why do the Rust and Go programs I've written have such a dramatic difference in performance? I expected Rust to be a bit faster than Go in this case. Am I doing something wrong in my Rust code?
</div>
I think you're not compiling with optimisation. try
$ rustc -O parity.rs
Your benchmark doesn't measure the parity check. It measures input plus parity check plus output. For example, in Go, you measure scanner.Scan
and strconv.ParseUint
and fmt.Println
as well as the parity check.
Here's a Go benchmark that just measures 1000000 parity checks.
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
}
Output:
$ go test -bench=.
BenchmarkParity1000000 50 34586769 ns/op
$