typestar

par_wordcount.rs en Rust

Frecuencias de palabras sobre un corpus, en serie y en paralelo, con los tiempos comparados.

use std::collections::HashMap;
use std::time::Instant;

use rayon::prelude::*;

fn corpus(copies: usize) -> Vec<String> {
    let base = [
        "the quick brown fox jumps over the lazy dog",
        "practice makes the difference between typing and thinking",
        "ownership borrowing lifetimes traits iterators errors",
    ];
    base.iter()
        .cycle()
        .take(base.len() * copies)
        .map(|line| line.to_string())
        .collect()
}

fn serial_counts(lines: &[String]) -> HashMap<&str, u32> {
    let mut counts = HashMap::new();
    for line in lines {
        for word in line.split_whitespace() {
            *counts.entry(word).or_insert(0) += 1;
        }
    }
    counts
}

fn parallel_counts(lines: &[String]) -> HashMap<&str, u32> {
    lines
        .par_iter()
        .fold(HashMap::new, |mut acc: HashMap<&str, u32>, line| {
            for word in line.split_whitespace() {
                *acc.entry(word).or_insert(0) += 1;
            }
            acc
        })
        .reduce(HashMap::new, |mut a, b| {
            for (word, n) in b {
                *a.entry(word).or_insert(0) += n;
            }
            a
        })
}

fn top(counts: &HashMap<&str, u32>, n: usize) -> Vec<(String, u32)> {
    let mut pairs: Vec<(String, u32)> =
        counts.iter().map(|(w, c)| (w.to_string(), *c)).collect();
    pairs.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0)));
    pairs.into_iter().take(n).collect()
}

fn main() {
    let lines = corpus(20_000);
    println!("{} lines, {} threads", lines.len(),
             rayon::current_num_threads());

    let started = Instant::now();
    let one = serial_counts(&lines);
    let serial_time = started.elapsed();

    let started = Instant::now();
    let many = parallel_counts(&lines);
    let parallel_time = started.elapsed();

    assert_eq!(one, many);
    println!("serial   {:?}", serial_time);
    println!("parallel {:?}", parallel_time);
    println!(
        "speedup  {:.2}x",
        serial_time.as_secs_f64() / parallel_time.as_secs_f64()
    );

    for (word, count) in top(&many, 5) {
        println!("{count:>7}  {word}");
    }
}

Cómo funciona

  1. El camino paralelo pliega un mapa por hilo y luego los fusiona.
  2. Ambos caminos producen la misma tabla, y el código lo afirma.
  3. Los tiempos se imprimen lado a lado para que la ganancia se vea.

Palabras clave y builtins usados aquí

El intento, en números

Líneas
77
Caracteres a escribir
1770
Tokens
548
Ritmo de tres estrellas
115 tpm

Al ritmo de tres estrellas de 115 tokens por minuto, este intento toma unos 286 segundos.

Escribe este fragmento

Paso 1 de 1 en Bis; paso 9 de 9 en Paralelismo de datos con rayon.

← Anterior