[英]Why is this Rust program so slow? Did I miss something?
我讀了曼哈頓的最小距離度量,並重寫了作者在Rust中的“天真”實現。 C ++變體是:
#include <utility>
#include <cstdio>
#include <cstdlib>
std::pair<int, int> pointsA[1000001];
std::pair<int, int> pointsB[1000001];
int main() {
int n, t;
unsigned long long dist;
scanf("%d", &t);
while(t-->0) {
dist = 4000000000LL;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d%d", &pointsA[i].first, &pointsA[i].second);
}
for(int i = 0; i < n; i++) {
scanf("%d%d", &pointsB[i].first, &pointsB[i].second);
}
for(int i = 0; i < n ;i++) {
for(int j = 0; j < n ; j++) {
if(abs(pointsA[i].first - pointsB[j].first) + abs(pointsA[i].second - pointsB[j].second) < dist)
dist = abs(pointsA[i].first - pointsB[j].first) + abs(pointsA[i].second - pointsB[j].second);
}
}
printf("%lld\n", dist);
}
}
Rust變種是:
use std::io;
use std::io::BufReader;
use std::io::BufRead;
fn read_array(stdin: &mut BufReader<io::Stdin>, array_len: usize, points: &mut Vec<(i32, i32)>) {
let mut line = String::new();
for _ in 0..array_len {
line.clear();
stdin.read_line(&mut line).unwrap();
let mut item = line.split_whitespace();
let x = item.next().unwrap().parse().unwrap();
let y = item.next().unwrap().parse().unwrap();
points.push((x, y));
}
}
fn manhattan_dist(a: &(i32, i32), b: &(i32, i32)) -> u32 {
((a.0 - b.0).abs() + (a.1 - b.1).abs()) as u32
}
fn main() {
let mut line = String::new();
let mut stdin = BufReader::new(io::stdin());
stdin.read_line(&mut line).unwrap();
let n_iters = line.trim_right().parse::<usize>().unwrap();
let mut points_a = Vec::with_capacity(10000);
let mut points_b = Vec::with_capacity(10000);
for _ in 0..n_iters {
line.clear();
stdin.read_line(&mut line).unwrap();
let set_len = line.trim_right().parse::<usize>().unwrap();
points_a.clear();
points_b.clear();
read_array(&mut stdin, set_len, &mut points_a);
read_array(&mut stdin, set_len, &mut points_b);
let mut dist = u32::max_value();
for i in points_a.iter() {
for j in points_b.iter() {
dist = std::cmp::min(manhattan_dist(i, j), dist);
}
}
println!("{}", dist);
}
}
然后,我使用Python腳本生成數據:
import random
ITER = 100
N = 10000
MAX_INT = 1000000
print("%d" % ITER)
for _ in range(0, ITER):
print("%d" % N)
for _ in range(0, N):
print(random.randrange(-MAX_INT, MAX_INT + 1), random.randrange(1, MAX_INT + 1))
for _ in range(0, N):
print(random.randrange(-MAX_INT, MAX_INT + 1), random.randrange(-MAX_INT, 0))
並使用g++ -Ofast -march=native
和rustc -C opt-level=3
分別編譯了兩個變體。 時間是:
C ++
real 0m7.789s
user 0m7.760s
sys 0m0.020s
銹
real 0m28.589s
user 0m28.570s
sys 0m0.010s
為什么我的Rust代碼比C ++變體慢四倍? 我正在使用Rust 1.12.0-beta.1。
我添加了時間測量:
let now = SystemTime::now();
line.clear();
stdin.read_line(&mut line).unwrap();
let set_len = line.trim_right().parse::<usize>().unwrap();
points_a.clear();
points_b.clear();
read_array(&mut stdin, set_len, &mut points_a);
read_array(&mut stdin, set_len, &mut points_b);
io_time += now.elapsed().unwrap();
let now = SystemTime::now();
let mut dist = u32::max_value();
for i in points_a.iter() {
for j in points_b.iter() {
dist = std::cmp::min(manhattan_dist(i, j), dist);
}
}
calc_time += now.elapsed().unwrap();
並且writeln!(&mut std::io::stderr(), "io_time: {}, calc_time: {}", io_time.as_secs(), calc_time.as_secs()).unwrap();
打印io_time: 0, calc_time: 27
。
我夜間嘗試每晚rustc 1.13.0-nightly (e9bc1bac8 2016-08-24)
:
$ time ./test_rust < data.txt > test3_res
io_time: 0, calc_time: 19
real 0m19.592s
user 0m19.560s
sys 0m0.020s
$ time ./test1 < data.txt > test1_res
real 0m7.797s
user 0m7.780s
sys 0m0.010s
所以它在我的Core i7上差不多是2.7倍。
區別當然是-march=native
...有點。 Rust通過-C target_cpu=native
這一點,但這並沒有給出相同的速度優勢。 這是因為LLVM不願意在這種情況下進行矢量化,而GCC則不然 。 您可能會注意到,使用Clang (一種也使用LLVM的C ++編譯器)也會產生相對較慢的代碼。
為了鼓勵LLVM進行矢量化,您可以將主循環移動到單獨的函數中。 或者,您可以使用本地塊。 如果你仔細編寫代碼
let dist = {
let mut dist = i32::max_value();
for &(a, b) in &points_a[..n] {
for &(c, d) in &points_b[..n] {
dist = std::cmp::min(((a - c).abs() + (b - d).abs()), dist);
}
}
dist
} as u32;
Rust和C ++之間的區別幾乎可以忽略不計(~4%)。
您在C ++中看到的絕大多數性能都歸功於標志-march=native
。
這個標志不是Rust的--release
的等效標志。 它采用特定於它是在編譯的CPU CPU指令,所以數學特別是將是方式更快。
刪除該標志會使C ++代碼處於19秒。
然后是C ++代碼中存在的不安全現象。 沒有選中任何輸入。 銹病代碼不檢查它,你用.unwrap()
- unwrap
有性能上的成本,有一個說法,那么代碼需要平倉等。
使用if let
而不是raw unwrap
,或者在可能的情況下忽略結果,再次使Rust代碼失效。
銹:22秒
C ++:19秒
3秒來自哪里? 一點點玩耍讓我相信它是println!
與printf
相比,但我沒有C ++代碼的硬編號。 我可以說的是,當我在基准測試之外執行打印時,Rust代碼會下降到13秒。
TLDR:您的編譯器標志不同,您的C ++代碼不安全。
我絕對沒有看到執行時間的任何差異。 在我的機器上
C ++:
real 0m19.672s
user 0m19.636s
sys 0m0.060s
銹:
real 0m19.047s
user 0m19.028s
sys 0m0.040s
我使用rustc -O test.rs -o ./test
編譯Rust代碼,使用g++ -Ofast test.cpp -o test
C ++代碼。
我正在使用Linux Kernel 4.6.3-040603-generic運行Ubuntu 16.04。 我運行它的筆記本電腦有一個Intel(R)Core(TM)i5-6200U CPU和8GB RAM,沒什么特別的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.