【问题标题】:rust vs c performance生锈与c性能
【发布时间】:2014-10-09 14:04:57
【问题描述】:

我想了解一些关于 rust 任务的知识,所以我做了一个蒙特卡罗计算 PI。现在我的困惑是为什么单线程 C 版本快 4 倍 比 4 路线程 Rust 版本。很明显我做错了什么,或者我的心理表现模型离题了。

这是 C 版本:

#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

#define PI 3.1415926535897932

double monte_carlo_pi(int nparts)
{
    int i, in=0;
    double x, y;
    srand(getpid());

    for (i=0; i<nparts; i++) {
        x = (double)rand()/(double)RAND_MAX;
        y = (double)rand()/(double)RAND_MAX;

            if (x*x + y*y < 1.0) {
            in++;
        }
    }

    return in/(double)nparts * 4.0;
}

int main(int argc, char **argv)
{
    int nparts;
    double mc_pi;

    nparts = atoi(argv[1]);
    mc_pi = monte_carlo_pi(nparts);
    printf("computed: %f error: %f\n", mc_pi, mc_pi - PI);
}

Rust 版本不是逐行移植:

use std::rand;
use std::rand::distributions::{IndependentSample,Range};

fn monte_carlo_pi(nparts: uint ) -> uint {
    let between = Range::new(0f64,1f64);
    let mut rng = rand::task_rng();
    let mut in_circle = 0u;
    for _ in range(0u, nparts) {
        let a = between.ind_sample(&mut rng);
    let b = between.ind_sample(&mut rng);

    if a*a + b*b <= 1.0 {
        in_circle += 1;
    }
    }
    in_circle
}

fn main() {
    let (tx, rx) = channel();

    let ntasks = 4u;
    let nparts = 100000000u; /* I haven't learned how to parse cmnd line args yet!*/
    for _ in range(0u, ntasks) {
        let child_tx = tx.clone();
        spawn(proc() {
        child_tx.send(monte_carlo_pi(nparts/ntasks));
        });
    }

    let result = rx.recv() + rx.recv() + rx.recv() + rx.recv();

    println!("pi is {}", (result as f64)/(nparts as f64)*4.0);
}

C 版本的构建和计时:

$ clang -O2 mc-pi.c -o mc-pi-c; time ./mc-pi-c 100000000
computed: 3.141700 error: 0.000108
./mc-pi-c 100000000  1.68s user 0.00s system 99% cpu 1.683 total

构建 Rust 版本并计时:

$ rustc -v      
rustc 0.12.0-nightly (740905042 2014-09-29 23:52:21 +0000)
$ rustc --opt-level 2 --debuginfo 0 mc-pi.rs -o mc-pi-rust; time ./mc-pi-rust  
pi is 3.141327
./mc-pi-rust  2.40s user 24.56s system 352% cpu 7.654 tota

【问题讨论】:

  • 不要在打开调试符号的情况下编译。
  • the single-threaded C version is 4 times slower than the 4-way threaded Rust version。您发布的数字似乎与此相反
  • @RobLatham 这里的瓶颈可能是随机数生成器。尝试使用rand::XorShiftRng::new_unseeded() 而不是rand::task_rng() 以获得更快的随机生成器。
  • 您可以创建一个随机播种的XorShiftRng,例如let mut rng: XorShiftRng = rand::random();(速度的提升来自于算法的改变,而不是缺少种子)。
  • 现在更像了。在 4 核系统上,四路线程 rust 比单线程 C 版本快 4.5 倍。如果 Dogbert 想把它写下来,我会接受它,或者我会在几天后自行回答。

标签: c performance rust


【解决方案1】:

正如 Dogbert 所观察到的,瓶颈是随机数生成器。这是一个快速且在每个线程上以不同方式播种的方法

fn monte_carlo_pi(id: u32, nparts: uint ) -> uint {
    ...
    let mut rng: XorShiftRng = SeedableRng::from_seed([id,id,id,id]);
    ...
}

【讨论】:

    【解决方案2】:

    有意义的基准测试是一件棘手的事情,因为你有各种各样的优化选项等等。而且,代码的结构会产生巨大的影响。

    比较 C 和 Rust 有点像比较苹果和橘子。我们通常使用计算密集型算法,例如您在上面描述的算法,但现实世界可能会让您陷入困境。

    话虽如此,总的来说,Rust 可以并且确实接近 C 和 C++ 的性能,并且大多数情况下在并发任务上可以做得更好。

    在此处查看基准:

    https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/rust-clang.html

    我选择了 Rust 与 C Clang 基准比较,因为两者都依赖于底层 LLVM。

    另一方面,与 C gcc 的比较会产生不同的结果:

    你猜怎么着? Rust 仍然领先!

    我恳请您更详细地浏览 Benchmark Game 网站。在某些情况下,C 会在某些情况下胜过 Rust。

    一般来说,当您创建实际解决方案时,您希望针对特定情况进行性能基准测试。 总是这样做,因为你经常会对结果感到惊讶。永远不要假设。

    我认为太多次,基准被用来转发“我的语言比你的语言更好”的 rwars 风格。但作为一个在他漫长的职业生涯中使用过 20 多种计算机语言的人,我总是说这是最适合这项工作的工具。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-25
      • 1970-01-01
      • 2022-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多