rost gegen c leistung

Lesezeit: 4 Minuten

Benutzer-Avatar
Rob Latham

Ich wollte etwas über Rust-Aufgaben lernen, also habe ich eine Monte-Carlo-Berechnung von PI durchgeführt. Jetzt ist mein Rätsel, warum die Single-Threaded-C-Version viermal schneller ist als die 4-Wege-Threaded-Rust-Version. Offensichtlich mache ich etwas falsch, oder mein mentales Leistungsmodell ist weit davon entfernt.

Hier die C-Version:

#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);
}

Die Rust-Version war kein Line-by-Line-Port:

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);
}

Erstellen und timen Sie die C-Version:

$ 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

Erstellen und timen Sie die Rust-Version:

$ 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

  • Kompilieren Sie nicht mit aktivierten Debugging-Symbolen.

    – AndyG

    9. Oktober 2014 um 14:07 Uhr

  • the single-threaded C version is 4 times slower than the 4-way threaded Rust version. Die Zahlen, die Sie gepostet haben, scheinen umgekehrt zu sein

    – Benutzer703016

    9. Oktober 2014 um 14:07 Uhr

  • @RobLatham Der Engpass hier ist wahrscheinlich der Zufallszahlengenerator. Versuchen rand::XorShiftRng::new_unseeded() Anstatt von rand::task_rng() für einen schnelleren Zufallsgenerator.

    – Dogbert

    9. Oktober 2014 um 14:47 Uhr


  • Sie können ein zufälliges Seed erstellen XorShiftRngz.B let mut rng: XorShiftRng = rand::random(); (Die Geschwindigkeitsverbesserung ergibt sich aus der Änderung des Algorithmus, nicht aus dem Mangel an Seeding).

    – huon

    9. Oktober 2014 um 22:01 Uhr

  • jetzt ist es eher so. Auf einem 4-Kern-System ist Vier-Wege-Threaded-Rost 4,5-mal schneller als die Single-Threaded-C-Version. Ich werde Dogberts Antwort akzeptieren, wenn er sie aufschreiben möchte, oder ich werde in ein paar Tagen selbst antworten.

    – Rob Latham

    10. Oktober 2014 um 2:26 Uhr

Der Engpass war, wie Dogbert feststellte, der Zufallszahlengenerator. Hier ist eine, die schnell ist und in jedem Thread anders gesät ist

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

Aussagekräftige Benchmarks sind eine knifflige Sache, da man alle möglichen Optimierungsmöglichkeiten usw. hat. Auch die Struktur des Codes kann einen großen Einfluss haben.

Der Vergleich von C und Rust ist ein bisschen wie der Vergleich von Äpfeln und Birnen. Wir verwenden normalerweise rechenintensive Algorithmen wie den, den Sie oben dargestellt haben, aber die reale Welt kann Ihnen eine Kurve werfen.

Abgesehen davon kann sich Rust im Allgemeinen der Leistung von C und C++ annähern und tut dies auch, und höchstwahrscheinlich kann es bei Nebenläufigkeitsaufgaben im Allgemeinen besser abschneiden.

Sehen Sie sich hier die Benchmarks an:

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

Ich habe mich für den Benchmark-Vergleich zwischen Rust und C Clang entschieden, da beide auf dem zugrunde liegenden LLVM basieren.

Geben Sie hier die Bildbeschreibung ein

Ein Vergleich mit C gcc hingegen liefert andere Ergebnisse:

Geben Sie hier die Bildbeschreibung ein

Und rate was? Rost kommt immer noch voran!

Ich bitte Sie, die Website von Benchmark Game genauer zu erkunden. Es gibt einige Fälle, in denen C Rust in einigen Fällen verdrängt.

Wenn Sie eine reale Lösung erstellen, möchten Sie im Allgemeinen Leistungsbenchmarks für Ihre speziellen Fälle durchführen. Tun Sie dies immer, denn Sie werden oft von den Ergebnissen überrascht sein. Niemals annehmen.

Ich denke, dass zu oft Benchmarks verwendet werden, um den “Meine Sprache ist besser als Ihre Sprache”-Stil von rwars weiterzuleiten. Aber als jemand, der während seiner langen Karriere über 20 Computersprachen verwendet hat, sage ich immer, dass es um das beste Werkzeug für den Job geht.

1367610cookie-checkrost gegen c leistung

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy