寫個小程式,用暴力的方法不斷地找出 10000000 之後的質數,本來想說 Rust 和 Go 幾乎相同的寫法應該會差不多的,不過實際執行時 Go 卻明顯快很多,實在搞不懂怎麼回事,特來請教,謝謝。
// Rust
fn main() {
println!("Hello, Rust");
let mut i: u64 = 10000000;
let mut c: u64 = 0;
loop {
c = 0;
for j in 2..i {
if i % j == 0 {
c += 1;
}
}
if c == 0 {
println!(
"i = {}, c = {}\t>>=======> {} is a prime. By Rust!",
i, c, i
);
} else {
println!("i = {}, c = {}", i, c);
}
i += 1;
}
}
// Go
package main
import "fmt"
func main() {
fmt.Printf("Hello, Go")
var i uint64 = 10000000
var c uint64 = 0
for {
c = 0
for j := uint64(2); j < i; j++ {
if i%j == 0 {
c = c + 1
}
}
if c == 0 {
fmt.Printf("i = %d, c = %d\t>>=======> %d is a prime. By Go!\n", i, c, i)
} else {
fmt.Printf("i = %d, c = %d\n", i, c)
}
i = i + 1
}
}
$ rustc -vV
rustc 1.38.0
binary: rustc
commit-hash: unknown
commit-date: unknown
host: x86_64-unknown-linux-gnu
release: 1.38.0
LLVM version: 7.0
因為你這樣是無窮迴圈,如果要計時來比較速度的話沒辦法比,所以我這邊先改了一下你的程式,讓它們跑到同一個數字就停下來:
Rust:
fn main() {
println!("Hello, Rust");
for i in 10000000..10000500 {
let mut c: u64 = 0;
for j in 2..i {
if i % j == 0 {
c += 1;
}
}
if c == 0 {
println!(
"i = {}, c = {}\t>>=======> {} is a prime. By Rust!",
i, c, i
);
} else {
println!("i = {}, c = {}", i, c);
}
}
}
Rust 用 rustc -O -o prime-rs prime.rs
編譯
Go:
package main
import "fmt"
func main() {
fmt.Printf("Hello, Go")
var c uint64
for i := uint64(10000000); i < 10000500; i++ {
for j := uint64(2); j < i; j++ {
if i%j == 0 {
c = c + 1
}
}
if c == 0 {
fmt.Printf("i = %d, c = %d\t>>=======> %d is a prime. By Go!\n", i, c, i)
} else {
fmt.Printf("i = %d, c = %d\n", i, c)
}
i = i + 1
}
}
Go 則是用 go build -o prime-go prime.go
編譯,預設 Go 似乎就會最佳化了,所以這邊就沒另外加什麼參數
https://stackoverflow.com/questions/45003259/passing-an-optimization-flag-to-a-go-compiler
然後都用 hyperfine 測速度得到以下結果:
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
./prime-rs |
13.185 ± 0.026 | 13.151 | 13.245 | 1.0 |
./prime-go |
20.795 ± 0.094 | 20.701 | 21.018 | 1.0 |
其實這樣光這樣就可以看到 Rust 已經比較快跑完了,但如果把 Rust 的程式再修改一下,中間的 for j in 2..i
這段改成以下這樣,用 Iterator
做的話:
let c = (2..i).filter(|j| i % j == 0).count();
再跑一次結果:
Command | Mean [s] | Min [s] | Max [s] | Relative |
---|---|---|---|---|
./prime-rs |
11.558 ± 0.077 | 11.488 | 11.713 | 1.0 |
所以並沒有 Rust 跑比較慢這回事,有可能是你沒有開最佳化之類的原因導致的
另外補充一下我測試的環境
$ rustc -vV
rustc 1.40.0-nightly (e413dc36a 2019-10-14)
binary: rustc
commit-hash: e413dc36a83a5aad3ab6270373000693a917e92b
commit-date: 2019-10-14
host: x86_64-unknown-linux-gnu
release: 1.40.0-nightly
LLVM version: 9.0
$ cat /proc/cpuinfo
...
model name : Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
...
效能分析
Go 的簡便無所不在,連讓程式最佳化的工具也不例外。從效能分析(profiling)來看,Go 內建追蹤 CPU 和記憶體用量的機制,且能與 pprof 工具整合。可以很容易檢查 Go 程式並取得有用的資料來最佳化。
我還沒發現任何 Rust 的分析工具可以和 Go 的工具一樣整合 pprof。當然,有一個函式庫可以生成類似 pprof 的追蹤資料,但我無法簡易上手,安裝上也有點詭異(需要 gperftools 以顯示在系統上)。這篇舊文有相關資訊和工具可供參考。
獲勝者:就我目前所知,Go 在這方面大勝。
跑程式的快慢與許多環境因素有關
硬體、作業系統、程式寫法、題目類型...
根據 Russ Cox 的說法
只有 fannkuch-redux, fasta, k-nucleotide, mandlebrot, nbody, reverse-complement and spectral-norm 這幾類的題目
比較可以拿來公平地比較 Go 與其他語言