【发布时间】:2018-03-30 09:36:30
【问题描述】:
我阅读了Minimal distance in Manhattan metric,并在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 倍。
【问题讨论】:
-
问题是你的实现没有一个是等价的。完全按照 C++ 版本编写 Rust 代码。在应用程序的开头处理 stdout 和 stdin 并锁定它们。直接写入标准输出的缓冲区,而不是使用会导致锁定+格式化开销的宏。
-
尝试使用
env RUSTFLAGS="-C target-cpu=native" cargo build --release构建。 Rust 编译器拒绝使用各种高端 CPU 扩展而不专门启用它们。 -
FWIW,
BufReader不是stdin的理想用法;尝试改用stdin.lock(),它会为您提供BufRead并避免重复锁定。不过,这种差异并没有真正意义,因为这里的 IO 成本并不高。 -
@user1244932:我邀请您阅读 reddit 讨论 here,我个人觉得有许多 cmets 很有趣,而且有点太长,无法简明扼要地总结 :)
标签: rust